mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 21:13:50 +00:00
feat: collection model
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { GET, POST, DELETE, PUT } from './request';
|
||||
import type { ModelSchema, ModelDataSchema } from '@/types/mongoSchema';
|
||||
import { ModelUpdateParams } from '@/types/model';
|
||||
import { PagingData, RequestPaging } from '../types/index';
|
||||
import { ModelUpdateParams, ShareModelItem } from '@/types/model';
|
||||
import { RequestPaging } from '../types/index';
|
||||
import { Obj2Query } from '@/utils/tools';
|
||||
|
||||
/**
|
||||
@@ -99,4 +99,13 @@ export const delOneModelData = (dataId: string) =>
|
||||
* 获取共享市场模型
|
||||
*/
|
||||
export const getShareModelList = (data: { searchText?: string } & RequestPaging) =>
|
||||
POST<number>(`/model/share/getModels`, data);
|
||||
POST(`/model/share/getModels`, data);
|
||||
/**
|
||||
* 获取收藏的模型
|
||||
*/
|
||||
export const getCollectionModels = () => GET<ShareModelItem[]>(`/model/share/getCollection`);
|
||||
/**
|
||||
* 收藏/取消收藏模型
|
||||
*/
|
||||
export const triggerModelCollection = (modelId: string) =>
|
||||
POST<number>(`/model/share/collection?modelId=${modelId}`);
|
||||
|
@@ -1 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1682602070818" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2479" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M509.606998 143.114488c9.082866 0 17.327644 4.840238 20.996197 12.331863l97.262184 197.441814c5.613858 11.403724 16.663518 19.358907 29.438473 21.216207l223.738737 32.552393c8.420787 1.215688 15.604396 6.851035 18.23327 14.254655 2.520403 7.18361 0.595564 15.062044-5.084808 20.586874L730.253304 601.611947c-8.949836 8.751315-12.994965 21.171182-10.916631 33.370015l38.011732 222.060515c1.325182 7.737218-2.165316 15.426341-8.905834 19.978007-4.088108 2.741437-8.861832 4.155646-13.812587 4.155646-4.022617 0-7.999185-0.972141-11.425214-2.740414L528.149307 775.671215c-5.768377-3.006474-12.155854-4.552689-18.542308-4.552689-6.364965 0-12.727882 1.547239-18.518772 4.552689L296.254819 878.348736c-3.559059 1.855254-7.602142 2.828418-11.668761 2.828418-4.861728 0-9.723455-1.459235-13.546527-4.022617-6.961552-4.684696-10.475586-12.419867-9.127891-20.155039l38.011732-222.016513c2.078335-12.198833-1.988284-24.619724-10.939143-33.370015L125.02397 441.443038c-5.635347-5.492084-7.55814-13.348006-5.061272-20.453844 2.63092-7.481392 9.812483-13.116739 18.298761-14.332427l223.674269-32.552393c12.839423-1.857301 23.867594-9.813506 29.481452-21.216207l97.194646-197.396789C492.325403 147.965983 500.590648 143.114488 509.606998 143.114488M509.606998 104.904235c-24.043602 0-45.922912 13.226233-56.177464 33.95637L356.189863 336.302419l-223.674269 32.54216c-22.983457 3.304256-42.100864 18.718317-49.481971 39.659255-7.381108 21.048385-1.812275 44.23241 14.431687 60.033281l163.916257 160.125931-38.011732 222.016513c-3.868097 22.408359 6.03239 44.819788 25.458835 57.94676 10.69662 7.116071 23.204491 10.784624 35.757388 10.784624 10.298554 0 20.663622-2.475378 30.055526-7.337105l194.987926-102.7205L704.662463 912.072815c9.369392 4.861728 19.712971 7.337105 29.990035 7.337105 12.57541 0 25.082258-3.668553 35.778878-10.784624 19.426445-13.126972 29.305443-35.538401 25.460882-57.94676l-38.012755-222.016513 163.937746-160.125931c16.22145-15.812127 21.810748-38.984896 14.408151-60.033281-7.402597-20.940938-26.51898-36.353976-49.503461-39.659255L663.04767 336.302419l-97.240695-197.441814C555.619962 118.131491 533.695626 104.904235 509.606998 104.904235L509.606998 104.904235z" fill="#5D5D5D" p-id="2480"></path></svg>
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1682602070818" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2479" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M509.606998 143.114488c9.082866 0 17.327644 4.840238 20.996197 12.331863l97.262184 197.441814c5.613858 11.403724 16.663518 19.358907 29.438473 21.216207l223.738737 32.552393c8.420787 1.215688 15.604396 6.851035 18.23327 14.254655 2.520403 7.18361 0.595564 15.062044-5.084808 20.586874L730.253304 601.611947c-8.949836 8.751315-12.994965 21.171182-10.916631 33.370015l38.011732 222.060515c1.325182 7.737218-2.165316 15.426341-8.905834 19.978007-4.088108 2.741437-8.861832 4.155646-13.812587 4.155646-4.022617 0-7.999185-0.972141-11.425214-2.740414L528.149307 775.671215c-5.768377-3.006474-12.155854-4.552689-18.542308-4.552689-6.364965 0-12.727882 1.547239-18.518772 4.552689L296.254819 878.348736c-3.559059 1.855254-7.602142 2.828418-11.668761 2.828418-4.861728 0-9.723455-1.459235-13.546527-4.022617-6.961552-4.684696-10.475586-12.419867-9.127891-20.155039l38.011732-222.016513c2.078335-12.198833-1.988284-24.619724-10.939143-33.370015L125.02397 441.443038c-5.635347-5.492084-7.55814-13.348006-5.061272-20.453844 2.63092-7.481392 9.812483-13.116739 18.298761-14.332427l223.674269-32.552393c12.839423-1.857301 23.867594-9.813506 29.481452-21.216207l97.194646-197.396789C492.325403 147.965983 500.590648 143.114488 509.606998 143.114488M509.606998 104.904235c-24.043602 0-45.922912 13.226233-56.177464 33.95637L356.189863 336.302419l-223.674269 32.54216c-22.983457 3.304256-42.100864 18.718317-49.481971 39.659255-7.381108 21.048385-1.812275 44.23241 14.431687 60.033281l163.916257 160.125931-38.011732 222.016513c-3.868097 22.408359 6.03239 44.819788 25.458835 57.94676 10.69662 7.116071 23.204491 10.784624 35.757388 10.784624 10.298554 0 20.663622-2.475378 30.055526-7.337105l194.987926-102.7205L704.662463 912.072815c9.369392 4.861728 19.712971 7.337105 29.990035 7.337105 12.57541 0 25.082258-3.668553 35.778878-10.784624 19.426445-13.126972 29.305443-35.538401 25.460882-57.94676l-38.012755-222.016513 163.937746-160.125931c16.22145-15.812127 21.810748-38.984896 14.408151-60.033281-7.402597-20.940938-26.51898-36.353976-49.503461-39.659255L663.04767 336.302419l-97.240695-197.441814C555.619962 118.131491 533.695626 104.904235 509.606998 104.904235L509.606998 104.904235z" p-id="2480"></path></svg>
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -1 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1682602068431" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2339" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M335.008 916.629333c-35.914667 22.314667-82.88 10.773333-104.693333-25.557333a77.333333 77.333333 0 0 1-8.96-57.429333l46.485333-198.24a13.141333 13.141333 0 0 0-4.021333-12.864l-152.16-132.586667c-31.605333-27.52-35.253333-75.648-8.234667-107.733333a75.68 75.68 0 0 1 51.733333-26.752L354.848 339.2c4.352-0.362667 8.245333-3.232 10.026667-7.594667l76.938666-188.170666c16.032-39.2 60.618667-57.92 99.52-41.461334a76.309333 76.309333 0 0 1 40.832 41.461334l76.938667 188.16c1.781333 4.373333 5.674667 7.253333 10.026667 7.605333l199.712 16.277333c41.877333 3.413333 72.885333 40.458667 69.568 82.517334a76.938667 76.938667 0 0 1-26.08 51.978666l-152.16 132.586667c-3.541333 3.082667-5.141333 8.074667-4.021334 12.853333l46.485334 198.24c9.621333 41.013333-15.36 82.336-56.138667 92.224a75.285333 75.285333 0 0 1-57.525333-9.237333l-170.976-106.24a11.296 11.296 0 0 0-12.010667 0l-170.986667 106.24z" fill="#000000" p-id="2340"></path></svg>
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1682602068431" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2339" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M335.008 916.629333c-35.914667 22.314667-82.88 10.773333-104.693333-25.557333a77.333333 77.333333 0 0 1-8.96-57.429333l46.485333-198.24a13.141333 13.141333 0 0 0-4.021333-12.864l-152.16-132.586667c-31.605333-27.52-35.253333-75.648-8.234667-107.733333a75.68 75.68 0 0 1 51.733333-26.752L354.848 339.2c4.352-0.362667 8.245333-3.232 10.026667-7.594667l76.938666-188.170666c16.032-39.2 60.618667-57.92 99.52-41.461334a76.309333 76.309333 0 0 1 40.832 41.461334l76.938667 188.16c1.781333 4.373333 5.674667 7.253333 10.026667 7.605333l199.712 16.277333c41.877333 3.413333 72.885333 40.458667 69.568 82.517334a76.938667 76.938667 0 0 1-26.08 51.978666l-152.16 132.586667c-3.541333 3.082667-5.141333 8.074667-4.021334 12.853333l46.485334 198.24c9.621333 41.013333-15.36 82.336-56.138667 92.224a75.285333 75.285333 0 0 1-57.525333-9.237333l-170.976-106.24a11.296 11.296 0 0 0-12.010667 0l-170.986667 106.24z" p-id="2340"></path></svg>
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
44
src/pages/api/model/share/collection.ts
Normal file
44
src/pages/api/model/share/collection.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Collection, Model } from '@/service/mongo';
|
||||
import { authToken } from '@/service/utils/tools';
|
||||
|
||||
/* 模型收藏切换 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { modelId } = req.query as { modelId: string };
|
||||
|
||||
if (!modelId) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
// 凭证校验
|
||||
const userId = await authToken(req.headers.authorization);
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
const collectionRecord = await Collection.findOne({
|
||||
userId,
|
||||
modelId
|
||||
});
|
||||
|
||||
if (collectionRecord) {
|
||||
await Collection.findByIdAndRemove(collectionRecord._id);
|
||||
} else {
|
||||
await Collection.create({
|
||||
userId,
|
||||
modelId
|
||||
});
|
||||
}
|
||||
|
||||
await Model.findByIdAndUpdate(modelId, {
|
||||
'share.collection': await Collection.countDocuments({ modelId })
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
38
src/pages/api/model/share/getCollection.ts
Normal file
38
src/pages/api/model/share/getCollection.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Collection } from '@/service/mongo';
|
||||
import { authToken } from '@/service/utils/tools';
|
||||
import type { ShareModelItem } from '@/types/model';
|
||||
|
||||
/* 获取模型列表 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
// 凭证校验
|
||||
const userId = await authToken(req.headers.authorization);
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
// get my collections
|
||||
const collections = await Collection.find({
|
||||
userId
|
||||
}).populate('modelId', '_id avatar name userId share');
|
||||
|
||||
jsonRes<ShareModelItem[]>(res, {
|
||||
data: collections
|
||||
.map((item: any) => ({
|
||||
_id: item.modelId?._id,
|
||||
avatar: item.modelId?.avatar || '',
|
||||
name: item.modelId?.name || '',
|
||||
userId: item.modelId?.userId || '',
|
||||
share: item.modelId?.share || {},
|
||||
isCollection: true
|
||||
}))
|
||||
.filter((item) => item.share.isShare)
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,8 +1,7 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { connectToDatabase, Collection, Model } from '@/service/mongo';
|
||||
import { authToken } from '@/service/utils/tools';
|
||||
import { Model } from '@/service/models/model';
|
||||
import type { PagingData } from '@/types';
|
||||
import type { ShareModelItem } from '@/types/model';
|
||||
|
||||
@@ -30,19 +29,29 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
};
|
||||
|
||||
// 根据分享的模型
|
||||
const models = await Model.find(where, '_id avatar name userId share')
|
||||
.sort({
|
||||
'share.collection': -1
|
||||
})
|
||||
.limit(pageSize)
|
||||
.skip((pageNum - 1) * pageSize);
|
||||
const [models, total] = await Promise.all([
|
||||
Model.find(where, '_id avatar name userId share')
|
||||
.sort({
|
||||
'share.collection': -1
|
||||
})
|
||||
.limit(pageSize)
|
||||
.skip((pageNum - 1) * pageSize),
|
||||
Model.countDocuments(where)
|
||||
]);
|
||||
|
||||
jsonRes<PagingData<ShareModelItem>>(res, {
|
||||
data: {
|
||||
pageNum,
|
||||
pageSize,
|
||||
data: models,
|
||||
total: await Model.countDocuments(where)
|
||||
data: models.map((item) => ({
|
||||
_id: item._id,
|
||||
avatar: item.avatar,
|
||||
name: item.name,
|
||||
userId: item.userId,
|
||||
share: item.share,
|
||||
isCollection: false
|
||||
})),
|
||||
total
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import React, { useRef, useEffect, useMemo } from 'react';
|
||||
import { AddIcon, ChatIcon, DeleteIcon, MoonIcon, SunIcon } from '@chakra-ui/icons';
|
||||
import {
|
||||
Box,
|
||||
@@ -22,6 +22,7 @@ import { getToken } from '@/utils/user';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import WxConcat from '@/components/WxConcat';
|
||||
import { getChatHistory, delChatHistoryById } from '@/api/chat';
|
||||
import { getCollectionModels } from '@/api/model';
|
||||
import { modelList } from '@/constants/model';
|
||||
|
||||
const SlideBar = ({
|
||||
@@ -45,10 +46,30 @@ const SlideBar = ({
|
||||
cacheTime: 5 * 60 * 1000
|
||||
});
|
||||
|
||||
const { data: collectionModels = [] } = useQuery([getCollectionModels], getCollectionModels);
|
||||
|
||||
const models = useMemo(() => {
|
||||
const myModelList = myModels.map((item) => ({
|
||||
id: item._id,
|
||||
name: item.name,
|
||||
icon: modelList.find((model) => model.model === item?.service?.modelName)?.icon || 'model'
|
||||
}));
|
||||
const collectionList = collectionModels
|
||||
.map((item) => ({
|
||||
id: item._id,
|
||||
name: item.name,
|
||||
icon: 'collectionSolid' as any
|
||||
}))
|
||||
.filter((model) => !myModelList.find((item) => item.id === model.id));
|
||||
|
||||
return myModelList.concat(collectionList);
|
||||
}, [collectionModels, myModels]);
|
||||
|
||||
const { data: chatHistory = [], mutate: loadChatHistory } = useMutation({
|
||||
mutationFn: getChatHistory
|
||||
});
|
||||
|
||||
// update history
|
||||
useEffect(() => {
|
||||
if (chatId && preChatId.current === '') {
|
||||
loadChatHistory();
|
||||
@@ -56,8 +77,11 @@ const SlideBar = ({
|
||||
preChatId.current = chatId;
|
||||
}, [chatId, loadChatHistory]);
|
||||
|
||||
// init history
|
||||
useEffect(() => {
|
||||
loadChatHistory();
|
||||
setTimeout(() => {
|
||||
loadChatHistory();
|
||||
}, 1000);
|
||||
}, [loadChatHistory]);
|
||||
|
||||
const RenderHistory = () => (
|
||||
@@ -165,9 +189,9 @@ const SlideBar = ({
|
||||
{isSuccess && (
|
||||
<>
|
||||
<Box>
|
||||
{myModels.map((item) => (
|
||||
{models.map((item) => (
|
||||
<Flex
|
||||
key={item._id}
|
||||
key={item.id}
|
||||
alignItems={'center'}
|
||||
p={3}
|
||||
borderRadius={'md'}
|
||||
@@ -178,28 +202,19 @@ const SlideBar = ({
|
||||
}}
|
||||
fontSize={'xs'}
|
||||
border={'1px solid transparent'}
|
||||
{...(item._id === modelId
|
||||
{...(item.id === modelId
|
||||
? {
|
||||
borderColor: 'rgba(255,255,255,0.5)',
|
||||
backgroundColor: 'rgba(255,255,255,0.1)'
|
||||
}
|
||||
: {})}
|
||||
onClick={async () => {
|
||||
if (item._id === modelId) return;
|
||||
resetChat(item._id);
|
||||
if (item.id === modelId) return;
|
||||
resetChat(item.id);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<MyIcon
|
||||
name={
|
||||
modelList.find((model) => model.model === item.service.modelName)?.icon ||
|
||||
'model'
|
||||
}
|
||||
mr={2}
|
||||
color={'white'}
|
||||
w={'16px'}
|
||||
h={'16px'}
|
||||
/>
|
||||
<MyIcon name={item.icon} mr={2} color={'white'} w={'16px'} h={'16px'} />
|
||||
<Box className={'textEllipsis'} flex={'1 0 0'} w={0}>
|
||||
{item.name}
|
||||
</Box>
|
||||
|
@@ -467,7 +467,7 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
borderBottom={'1px solid rgba(0,0,0,0.1)'}
|
||||
>
|
||||
<Flex maxW={'750px'} m={'auto'} alignItems={'flex-start'}>
|
||||
<Menu>
|
||||
<Menu autoSelect={false}>
|
||||
<MenuButton as={Box} mr={media(4, 1)} cursor={'pointer'}>
|
||||
<Image
|
||||
src={item.obj === 'Human' ? '/icon/human.png' : chatData.avatar}
|
||||
|
@@ -80,9 +80,9 @@ const ModelEditForm = ({
|
||||
<Image
|
||||
src={getValues('avatar')}
|
||||
alt={'avatar'}
|
||||
w={'36px'}
|
||||
h={'36px'}
|
||||
objectFit={'contain'}
|
||||
w={['28px', '36px']}
|
||||
h={['28px', '36px']}
|
||||
objectFit={'cover'}
|
||||
cursor={'pointer'}
|
||||
title={'点击切换头像'}
|
||||
onClick={onOpenSelectFile}
|
||||
|
@@ -1,35 +1,56 @@
|
||||
import React, { Dispatch, useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { Card, Box, Flex, Image, Button } from '@chakra-ui/react';
|
||||
import type { ShareModelItem } from '@/types/model';
|
||||
import { useRouter } from 'next/router';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import styles from '../index.module.scss';
|
||||
|
||||
const ShareModelList = ({ models }: { models: ShareModelItem[] }) => {
|
||||
const ShareModelList = ({
|
||||
models = [],
|
||||
onclickCollection
|
||||
}: {
|
||||
models: ShareModelItem[];
|
||||
onclickCollection: (modelId: string) => void;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
{models.map((model) => (
|
||||
<Card key={model._id} p={4}>
|
||||
<Box
|
||||
key={model._id}
|
||||
p={4}
|
||||
border={'1px solid'}
|
||||
borderColor={'gray.200'}
|
||||
borderRadius={'md'}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
<Image
|
||||
src={model.avatar}
|
||||
alt={'avatar'}
|
||||
width={'36px'}
|
||||
height={'36px'}
|
||||
objectFit={'contain'}
|
||||
w={['28px', '36px']}
|
||||
h={['28px', '36px']}
|
||||
objectFit={'cover'}
|
||||
/>
|
||||
<Box fontWeight={'bold'} fontSize={'lg'} ml={5}>
|
||||
{model.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box className={styles.intro} my={4} fontSize={'sm'}>
|
||||
<Box className={styles.intro} my={4} fontSize={'sm'} color={'blackAlpha.600'}>
|
||||
{model.share.intro || '这个模型没有介绍~'}
|
||||
</Box>
|
||||
<Flex justifyContent={'space-between'}>
|
||||
<Flex alignItems={'center'} cursor={'pointer'}>
|
||||
<MyIcon mr={1} name={'collectionLight'} w={'16px'} />
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
color={model.isCollection ? 'blue.600' : 'alphaBlack.700'}
|
||||
onClick={() => onclickCollection(model._id)}
|
||||
>
|
||||
<MyIcon
|
||||
mr={1}
|
||||
name={model.isCollection ? 'collectionSolid' : 'collectionLight'}
|
||||
w={'16px'}
|
||||
/>
|
||||
{model.share.collection}
|
||||
</Flex>
|
||||
<Box>
|
||||
@@ -53,7 +74,7 @@ const ShareModelList = ({ models }: { models: ShareModelItem[] }) => {
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Card>
|
||||
</Box>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@@ -1,24 +1,20 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import React, { useState, useRef, useCallback, useMemo } from 'react';
|
||||
import { Box, Flex, Card, Grid, Input } from '@chakra-ui/react';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { getShareModelList } from '@/api/model';
|
||||
import { getShareModelList, triggerModelCollection, getCollectionModels } from '@/api/model';
|
||||
import { usePagination } from '@/hooks/usePagination';
|
||||
import type { ShareModelItem } from '@/types/model';
|
||||
|
||||
import ShareModelList from './components/list';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
const modelList = () => {
|
||||
const { Loading, setIsLoading } = useLoading();
|
||||
const { Loading } = useLoading();
|
||||
const lastSearch = useRef('');
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
/* 加载模型 */
|
||||
const {
|
||||
data: models,
|
||||
isLoading,
|
||||
Pagination,
|
||||
getData
|
||||
} = usePagination<ShareModelItem>({
|
||||
const { data, isLoading, Pagination, getData, pageNum } = usePagination<ShareModelItem>({
|
||||
api: getShareModelList,
|
||||
pageSize: 20,
|
||||
params: {
|
||||
@@ -26,47 +22,90 @@ const modelList = () => {
|
||||
}
|
||||
});
|
||||
|
||||
const { data: collectionModels = [], refetch: refetchCollection } = useQuery(
|
||||
[getCollectionModels],
|
||||
getCollectionModels
|
||||
);
|
||||
|
||||
const models = useMemo(() => {
|
||||
if (!collectionModels) return [];
|
||||
return data.map((model) => ({
|
||||
...model,
|
||||
isCollection: !!collectionModels.find((item) => item._id === model._id)
|
||||
}));
|
||||
}, [collectionModels, data]);
|
||||
|
||||
const onclickCollection = useCallback(
|
||||
async (modelId: string) => {
|
||||
try {
|
||||
await triggerModelCollection(modelId);
|
||||
getData(pageNum);
|
||||
refetchCollection();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
[getData, pageNum, refetchCollection]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box position={'relative'}>
|
||||
{/* 头部 */}
|
||||
<>
|
||||
<Card px={6} py={3}>
|
||||
<Flex alignItems={'center'} justifyContent={'space-between'}>
|
||||
<Box fontWeight={'bold'} fontSize={'xl'}>
|
||||
模型共享市场
|
||||
我收藏的模型
|
||||
</Box>
|
||||
<Box flex={1}>(Beta)</Box>
|
||||
<Input
|
||||
maxW={'240px'}
|
||||
size={'sm'}
|
||||
value={searchText}
|
||||
placeholder="搜索模型,回车确认"
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
onBlur={() => {
|
||||
if (searchText === lastSearch.current) return;
|
||||
getData(1);
|
||||
lastSearch.current = searchText;
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (searchText === lastSearch.current) return;
|
||||
if (e.key === 'Enter') {
|
||||
getData(1);
|
||||
lastSearch.current = searchText;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
{collectionModels.length == 0 && (
|
||||
<Box textAlign={'center'} pt={3}>
|
||||
还没有收藏模型~
|
||||
</Box>
|
||||
)}
|
||||
<Grid templateColumns={['1fr', '1fr 1fr', '1fr 1fr 1fr']} gridGap={4} mt={4}>
|
||||
<ShareModelList models={collectionModels} onclickCollection={onclickCollection} />
|
||||
</Grid>
|
||||
</Card>
|
||||
|
||||
<Grid templateColumns={['1fr', '1fr 1fr']} gridGap={4} mt={4}>
|
||||
<ShareModelList models={models} />
|
||||
</Grid>
|
||||
|
||||
<Box mt={4}>
|
||||
<Pagination />
|
||||
</Box>
|
||||
<Card mt={5} px={6} py={3}>
|
||||
<Box display={['block', 'flex']} alignItems={'center'} justifyContent={'space-between'}>
|
||||
<Box fontWeight={'bold'} flex={1} fontSize={'xl'}>
|
||||
模型共享市场{' '}
|
||||
<Box as={'span'} fontWeight={'normal'} fontSize={'md'}>
|
||||
(Beta)
|
||||
</Box>
|
||||
</Box>
|
||||
<Box mt={[2, 0]} textAlign={'right'}>
|
||||
<Input
|
||||
maxW={'240px'}
|
||||
size={'sm'}
|
||||
value={searchText}
|
||||
placeholder="搜索模型,回车确认"
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
onBlur={() => {
|
||||
if (searchText === lastSearch.current) return;
|
||||
getData(1);
|
||||
lastSearch.current = searchText;
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (searchText === lastSearch.current) return;
|
||||
if (e.key === 'Enter') {
|
||||
getData(1);
|
||||
lastSearch.current = searchText;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Grid templateColumns={['1fr', '1fr 1fr', '1fr 1fr 1fr']} gridGap={4} mt={4}>
|
||||
<ShareModelList models={models} onclickCollection={onclickCollection} />
|
||||
</Grid>
|
||||
<Box mt={4}>
|
||||
<Pagination />
|
||||
</Box>
|
||||
</Card>
|
||||
|
||||
<Loading loading={isLoading} />
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
18
src/service/models/collection.ts
Normal file
18
src/service/models/collection.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Schema, model, models, Model as MongoModel } from 'mongoose';
|
||||
import { CollectionSchema as CollectionType } from '@/types/mongoSchema';
|
||||
|
||||
const CollectionSchema = new Schema({
|
||||
userId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'user',
|
||||
required: true
|
||||
},
|
||||
modelId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'model',
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
export const Collection: MongoModel<CollectionType> =
|
||||
models['collection'] || model('collection', CollectionSchema);
|
@@ -50,3 +50,4 @@ export * from './models/pay';
|
||||
export * from './models/splitData';
|
||||
export * from './models/openapi';
|
||||
export * from './models/promotionRecord';
|
||||
export * from './models/collection';
|
||||
|
1
src/types/model.d.ts
vendored
1
src/types/model.d.ts
vendored
@@ -26,4 +26,5 @@ export interface ShareModelItem {
|
||||
name: string;
|
||||
userId: string;
|
||||
share: ModelSchema['share'];
|
||||
isCollection: boolean;
|
||||
}
|
||||
|
5
src/types/mongoSchema.d.ts
vendored
5
src/types/mongoSchema.d.ts
vendored
@@ -64,6 +64,11 @@ export interface ModelPopulate extends ModelSchema {
|
||||
userId: UserModelSchema;
|
||||
}
|
||||
|
||||
export interface CollectionSchema {
|
||||
modelId: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export type ModelDataType = 0 | 1;
|
||||
export interface ModelDataSchema {
|
||||
_id: string;
|
||||
|
Reference in New Issue
Block a user