perf: text and avatar
This commit is contained in:
@@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const myModel = await Model.findOne({ userId });
|
||||
if (!myModel) {
|
||||
const { _id } = await Model.create({
|
||||
name: 'AI助手1',
|
||||
name: '应用1',
|
||||
userId,
|
||||
status: ModelStatusEnum.running
|
||||
});
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Collection } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import type { ShareModelItem } from '@/types/model';
|
||||
|
||||
/* 获取模型列表 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
// 凭证校验
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
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 || '/icon/logo.png',
|
||||
name: item.modelId?.name || '',
|
||||
userId: item.modelId?.userId || '',
|
||||
share: item.modelId?.share || {},
|
||||
isCollection: true
|
||||
}))
|
||||
.filter((item) => item.share.isShare)
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
data: []
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Collection, Model } from '@/service/mongo';
|
||||
import type { PagingData } from '@/types';
|
||||
import type { ShareModelItem } from '@/types/model';
|
||||
import { parseCookie } from '@/service/utils/auth';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
/* 获取模型列表 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
@@ -15,6 +17,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
let userId = '';
|
||||
|
||||
try {
|
||||
userId = await parseCookie(req.headers.cookie);
|
||||
} catch (error) {
|
||||
error;
|
||||
}
|
||||
|
||||
const regex = new RegExp(searchText, 'i');
|
||||
|
||||
const where = {
|
||||
@@ -23,15 +33,58 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
{ $or: [{ name: { $regex: regex } }, { 'share.intro': { $regex: regex } }] }
|
||||
]
|
||||
};
|
||||
const pipeline = [
|
||||
{
|
||||
$match: where
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: 'collections',
|
||||
let: { modelId: '$_id' },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$and: [
|
||||
{ $eq: ['$modelId', '$$modelId'] },
|
||||
{
|
||||
$eq: ['$userId', userId ? new Types.ObjectId(userId) : new Types.ObjectId()]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
as: 'collections'
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 1,
|
||||
avatar: { $ifNull: ['$avatar', '/icon/logo.png'] },
|
||||
name: 1,
|
||||
userId: 1,
|
||||
share: 1,
|
||||
isCollection: {
|
||||
$cond: { if: { $gt: [{ $size: '$collections' }, 0] }, then: true, else: false }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: { 'share.collection': -1 }
|
||||
},
|
||||
{
|
||||
$skip: (pageNum - 1) * pageSize
|
||||
},
|
||||
{
|
||||
$limit: 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),
|
||||
// @ts-ignore
|
||||
Model.aggregate(pipeline),
|
||||
Model.countDocuments(where)
|
||||
]);
|
||||
|
||||
@@ -39,14 +92,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
data: {
|
||||
pageNum,
|
||||
pageSize,
|
||||
data: models.map((item) => ({
|
||||
_id: item._id,
|
||||
avatar: item.avatar || '/icon/logo.png',
|
||||
name: item.name,
|
||||
userId: item.userId,
|
||||
share: item.share,
|
||||
isCollection: false
|
||||
})),
|
||||
data: models,
|
||||
total
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Card, Box, Image, Flex } from '@chakra-ui/react';
|
||||
import { Card, Box, Flex } from '@chakra-ui/react';
|
||||
import { useMarkdown } from '@/hooks/useMarkdown';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import { LOGO_ICON } from '@/constants/chat';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const Empty = ({
|
||||
showChatProblem,
|
||||
@@ -31,13 +32,7 @@ const Empty = ({
|
||||
{name && (
|
||||
<Card p={4} mb={10}>
|
||||
<Flex mb={2} alignItems={'center'} justifyContent={'center'}>
|
||||
<Image
|
||||
src={avatar || LOGO_ICON}
|
||||
w={'32px'}
|
||||
maxH={'40px'}
|
||||
objectFit={'contain'}
|
||||
alt={''}
|
||||
/>
|
||||
<Avatar src={avatar} w={'32px'} h={'32px'} />
|
||||
<Box ml={3} fontSize={'3xl'} fontWeight={'bold'}>
|
||||
{name}
|
||||
</Box>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Box, Flex, Image } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { ModelListItemType } from '@/types/model';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const ModelList = ({ models, modelId }: { models: ModelListItemType[]; modelId: string }) => {
|
||||
const router = useRouter();
|
||||
@@ -32,19 +33,13 @@ const ModelList = ({ models, modelId }: { models: ModelListItemType[]; modelId:
|
||||
router.replace(`/chat?modelId=${item._id}`);
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={item.avatar || '/icon/logo.png'}
|
||||
alt=""
|
||||
w={'34px'}
|
||||
maxH={'50px'}
|
||||
objectFit={'contain'}
|
||||
/>
|
||||
<Avatar src={item.avatar} w={'34px'} h={'34px'} />
|
||||
<Box flex={'1 0 0'} w={0} ml={3}>
|
||||
<Box className="textEllipsis" color={'myGray.1000'}>
|
||||
{item.name}
|
||||
</Box>
|
||||
<Box className="textEllipsis" color={'myGray.400'} fontSize={'sm'}>
|
||||
{item.systemPrompt || '这个AI助手没有设置提示词~'}
|
||||
{item.systemPrompt || '这个 应用 没有设置提示词~'}
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
@@ -7,8 +7,7 @@ import {
|
||||
Divider,
|
||||
useDisclosure,
|
||||
useColorMode,
|
||||
useColorModeValue,
|
||||
Image
|
||||
useColorModeValue
|
||||
} from '@chakra-ui/react';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
@@ -17,6 +16,7 @@ import MyIcon from '@/components/Icon';
|
||||
import WxConcat from '@/components/WxConcat';
|
||||
import { delChatHistoryById } from '@/api/chat';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const PhoneSliderBar = ({
|
||||
chatId,
|
||||
@@ -74,7 +74,7 @@ const PhoneSliderBar = ({
|
||||
color={'white'}
|
||||
>
|
||||
<Flex alignItems={'center'} justifyContent={'space-between'} px={3}>
|
||||
<Box flex={'0 0 50px'}>AI助手</Box>
|
||||
<Box flex={'0 0 50px'}>AI应用</Box>
|
||||
{/* 新对话 */}
|
||||
<Button
|
||||
w={'50%'}
|
||||
@@ -115,7 +115,7 @@ const PhoneSliderBar = ({
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<Image src={item.avatar || '/icon/logo.png'} mr={2} alt={''} w={'16px'} h={'16px'} />
|
||||
<Avatar src={item.avatar} mr={2} w={'18px'} h={'18px'} />
|
||||
<Box className={'textEllipsis'} flex={'1 0 0'} w={0}>
|
||||
{item.name}
|
||||
</Box>
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
Image,
|
||||
Button,
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
@@ -51,6 +50,7 @@ import { useUserStore } from '@/store/user';
|
||||
import Loading from '@/components/Loading';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import SideBar from '@/components/SideBar';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import Empty from './components/Empty';
|
||||
|
||||
const PhoneSliderBar = dynamic(() => import('./components/PhoneSliderBar'), {
|
||||
@@ -595,7 +595,7 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
borderBottom={theme.borders.base}
|
||||
onClick={() => router.push(`/model?modelId=${chatData.modelId}`)}
|
||||
>
|
||||
AI助手详情
|
||||
应用详情
|
||||
</MenuItem>
|
||||
)}
|
||||
{hasVoiceApi && (
|
||||
@@ -654,9 +654,9 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
>
|
||||
{!isPc && (
|
||||
<MyIcon
|
||||
name={'tabbarMore'}
|
||||
w={'14px'}
|
||||
h={'14px'}
|
||||
name={'menu'}
|
||||
w={'20px'}
|
||||
h={'20px'}
|
||||
color={useColorModeValue('blackAlpha.700', 'white')}
|
||||
onClick={onOpenSlider}
|
||||
/>
|
||||
@@ -734,19 +734,15 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
ml: ['6px', 2]
|
||||
})}
|
||||
>
|
||||
<Tooltip label={item.obj === 'AI' ? 'AI助手详情' : ''}>
|
||||
<Image
|
||||
className="avatar"
|
||||
<Tooltip label={item.obj === 'AI' ? '应用详情' : ''}>
|
||||
<Avatar
|
||||
src={
|
||||
item.obj === 'Human'
|
||||
? userInfo?.avatar || '/icon/human.png'
|
||||
: chatData.model.avatar || LOGO_ICON
|
||||
}
|
||||
alt="avatar"
|
||||
w={['20px', '34px']}
|
||||
h={['20px', '34px']}
|
||||
borderRadius={'50%'}
|
||||
objectFit={'contain'}
|
||||
/>
|
||||
</Tooltip>
|
||||
</MenuButton>
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
Image,
|
||||
Button,
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
@@ -48,6 +47,7 @@ import { useUserStore } from '@/store/user';
|
||||
import Loading from '@/components/Loading';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import SideBar from '@/components/SideBar';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import Empty from './components/Empty';
|
||||
|
||||
const ShareHistory = dynamic(() => import('./components/ShareHistory'), {
|
||||
@@ -564,9 +564,9 @@ const Chat = ({ shareId, historyId }: { shareId: string; historyId: string }) =>
|
||||
>
|
||||
{!isPc && (
|
||||
<MyIcon
|
||||
name={'tabbarMore'}
|
||||
w={'14px'}
|
||||
h={'14px'}
|
||||
name={'menu'}
|
||||
w={'20px'}
|
||||
h={'20px'}
|
||||
color={useColorModeValue('blackAlpha.700', 'white')}
|
||||
onClick={onOpenSlider}
|
||||
/>
|
||||
@@ -626,19 +626,15 @@ const Chat = ({ shareId, historyId }: { shareId: string; historyId: string }) =>
|
||||
ml: ['6px', 2]
|
||||
})}
|
||||
>
|
||||
<Tooltip label={item.obj === 'AI' ? 'AI助手详情' : ''}>
|
||||
<Image
|
||||
className="avatar"
|
||||
<Tooltip label={item.obj === 'AI' ? '应用详情' : ''}>
|
||||
<Avatar
|
||||
src={
|
||||
item.obj === 'Human'
|
||||
? userInfo?.avatar || '/icon/human.png'
|
||||
: shareChatData.model.avatar || LOGO_ICON
|
||||
}
|
||||
alt="avatar"
|
||||
w={['20px', '34px']}
|
||||
h={['20px', '34px']}
|
||||
borderRadius={'50%'}
|
||||
objectFit={'contain'}
|
||||
/>
|
||||
</Tooltip>
|
||||
</MenuButton>
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Flex,
|
||||
Button,
|
||||
Tooltip,
|
||||
Image,
|
||||
FormControl,
|
||||
Input,
|
||||
Tag,
|
||||
@@ -18,13 +17,13 @@ import { useForm } from 'react-hook-form';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { delKbById, putKbById } from '@/api/plugins/kb';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { KbItemType } from '@/types/plugin';
|
||||
import { useSelectFile } from '@/hooks/useSelectFile';
|
||||
import { useConfirm } from '@/hooks/useConfirm';
|
||||
import { compressImg } from '@/utils/file';
|
||||
import DataCard from './DataCard';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const Detail = ({ kbId }: { kbId: string }) => {
|
||||
const { toast } = useToast();
|
||||
@@ -58,7 +57,7 @@ const Detail = ({ kbId }: { kbId: string }) => {
|
||||
},
|
||||
onError(err: any) {
|
||||
toast({
|
||||
title: getErrText(err, '获取AI助手异常'),
|
||||
title: getErrText(err, '获取知识库异常'),
|
||||
status: 'error'
|
||||
});
|
||||
loadKbList(true);
|
||||
@@ -95,6 +94,7 @@ const Detail = ({ kbId }: { kbId: string }) => {
|
||||
id: kbId,
|
||||
...data
|
||||
});
|
||||
await getKbDetail(kbId, true);
|
||||
toast({
|
||||
title: '更新成功',
|
||||
status: 'success'
|
||||
@@ -108,7 +108,7 @@ const Detail = ({ kbId }: { kbId: string }) => {
|
||||
}
|
||||
setBtnLoading(false);
|
||||
},
|
||||
[kbId, loadKbList, toast]
|
||||
[getKbDetail, kbId, loadKbList, toast]
|
||||
);
|
||||
const saveSubmitError = useCallback(() => {
|
||||
// deep search message
|
||||
@@ -138,7 +138,7 @@ const Detail = ({ kbId }: { kbId: string }) => {
|
||||
maxH: 100
|
||||
});
|
||||
setValue('avatar', base64);
|
||||
loadKbList(true);
|
||||
setRefresh((state) => !state);
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: typeof err === 'string' ? err : '头像选择异常',
|
||||
@@ -146,7 +146,7 @@ const Detail = ({ kbId }: { kbId: string }) => {
|
||||
});
|
||||
}
|
||||
},
|
||||
[loadKbList, setValue, toast]
|
||||
[setRefresh, setValue, toast]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -180,12 +180,10 @@ const Detail = ({ kbId }: { kbId: string }) => {
|
||||
<Box flex={'0 0 60px'} w={0}>
|
||||
头像
|
||||
</Box>
|
||||
<Image
|
||||
src={getValues('avatar') || '/icon/logo.png'}
|
||||
alt={'avatar'}
|
||||
<Avatar
|
||||
src={getValues('avatar')}
|
||||
w={['28px', '36px']}
|
||||
h={['28px', '36px']}
|
||||
objectFit={'cover'}
|
||||
cursor={'pointer'}
|
||||
title={'点击切换头像'}
|
||||
onClick={onOpenSelectFile}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useState, useMemo } from 'react';
|
||||
import { Box, Flex, useTheme, Input, IconButton, Tooltip, Image, Tag } from '@chakra-ui/react';
|
||||
import { Box, Flex, useTheme, Input, IconButton, Tooltip, Tag } from '@chakra-ui/react';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import { useRouter } from 'next/router';
|
||||
import { postCreateKb } from '@/api/plugins/kb';
|
||||
@@ -8,6 +8,7 @@ import { useToast } from '@/hooks/useToast';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const KbList = ({ kbId }: { kbId: string }) => {
|
||||
const theme = useTheme();
|
||||
@@ -111,13 +112,7 @@ const KbList = ({ kbId }: { kbId: string }) => {
|
||||
router.push(`/kb?kbId=${item._id}`);
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={item.avatar || '/icon/logo.png'}
|
||||
alt=""
|
||||
w={'34px'}
|
||||
maxH={'50px'}
|
||||
objectFit={'contain'}
|
||||
/>
|
||||
<Avatar src={item.avatar} w={'34px'} h={'34px'} />
|
||||
<Box flex={'1 0 0'} w={0} ml={3}>
|
||||
<Box className="textEllipsis" color={'myGray.1000'}>
|
||||
{item.name}
|
||||
|
||||
@@ -65,7 +65,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
});
|
||||
// aut register a model
|
||||
postCreateModel({
|
||||
name: 'AI助手1'
|
||||
name: '应用1'
|
||||
});
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { Box, Flex, useTheme, Input, IconButton, Tooltip, Image } from '@chakra-ui/react';
|
||||
import { Box, Flex, useTheme, Input, IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import { useRouter } from 'next/router';
|
||||
import MyIcon from '@/components/Icon';
|
||||
@@ -8,6 +8,7 @@ import { useLoading } from '@/hooks/useLoading';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const ModelList = ({ modelId }: { modelId: string }) => {
|
||||
const theme = useTheme();
|
||||
@@ -23,7 +24,7 @@ const ModelList = ({ modelId }: { modelId: string }) => {
|
||||
const onclickCreateModel = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const id = await postCreateModel({ name: `AI助手${myModels.length + 1}` });
|
||||
const id = await postCreateModel({ name: `AI应用${myModels.length + 1}` });
|
||||
toast({
|
||||
title: '创建成功',
|
||||
status: 'success'
|
||||
@@ -94,7 +95,7 @@ const ModelList = ({ modelId }: { modelId: string }) => {
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Tooltip label={'新建一个AI助手'}>
|
||||
<Tooltip label={'新建一个AI应用'}>
|
||||
<IconButton
|
||||
h={'32px'}
|
||||
icon={<AddIcon />}
|
||||
@@ -134,19 +135,13 @@ const ModelList = ({ modelId }: { modelId: string }) => {
|
||||
router.push(`/model?modelId=${item._id}`);
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={item.avatar || '/icon/logo.png'}
|
||||
alt=""
|
||||
w={'34px'}
|
||||
maxH={'50px'}
|
||||
objectFit={'contain'}
|
||||
/>
|
||||
<Avatar src={item.avatar} w={'34px'} h={'34px'} />
|
||||
<Box flex={'1 0 0'} w={0} ml={3}>
|
||||
<Box className="textEllipsis" color={'myGray.1000'}>
|
||||
{item.name}
|
||||
</Box>
|
||||
<Box className="textEllipsis" color={'myGray.400'} fontSize={'sm'}>
|
||||
{item.systemPrompt || '这个AI助手没有设置提示词~'}
|
||||
{item.systemPrompt || '这个 应用 没有设置提示词~'}
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
Button,
|
||||
Select,
|
||||
Switch,
|
||||
Image,
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
@@ -51,6 +50,7 @@ import { formatTimeToChatTime, useCopyData, getErrText } from '@/utils/tools';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const ModelEditForm = ({
|
||||
formHooks,
|
||||
@@ -71,7 +71,7 @@ const ModelEditForm = ({
|
||||
const { loadKbList } = useUserStore();
|
||||
|
||||
const { openConfirm, ConfirmChild } = useConfirm({
|
||||
content: '确认删除该AI助手?'
|
||||
content: '确认删除该应用?'
|
||||
});
|
||||
const { copyData } = useCopyData();
|
||||
const { register, setValue, getValues } = formHooks;
|
||||
@@ -189,13 +189,7 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
|
||||
onClick={() => router.push(`/kb?kbId=${item._id}`)}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
<Image
|
||||
src={item.avatar}
|
||||
fallbackSrc="/icon/logo.png"
|
||||
w={'20px'}
|
||||
h={'20px'}
|
||||
alt=""
|
||||
></Image>
|
||||
<Avatar src={item.avatar} w={'20px'} h={'20px'}></Avatar>
|
||||
<Box ml={3} fontWeight={'bold'}>
|
||||
{item.name}
|
||||
</Box>
|
||||
@@ -222,12 +216,10 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
|
||||
<Box flex={'0 0 80px'} w={0}>
|
||||
头像
|
||||
</Box>
|
||||
<Image
|
||||
src={getValues('avatar') || '/icon/logo.png'}
|
||||
alt={'avatar'}
|
||||
<Avatar
|
||||
src={getValues('avatar')}
|
||||
w={['28px', '36px']}
|
||||
h={['28px', '36px']}
|
||||
objectFit={'cover'}
|
||||
cursor={isOwner ? 'pointer' : 'default'}
|
||||
title={'点击切换头像'}
|
||||
onClick={() => isOwner && onOpenSelectFile()}
|
||||
@@ -283,14 +275,14 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
|
||||
</Flex>
|
||||
{isOwner && (
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<Box flex={'0 0 100px'}>删除AI助手</Box>
|
||||
<Box flex={'0 0 100px'}>删除应用</Box>
|
||||
<Button
|
||||
colorScheme={'gray'}
|
||||
variant={'outline'}
|
||||
size={'sm'}
|
||||
onClick={openConfirm(handleDelModel)}
|
||||
>
|
||||
删除AI助手
|
||||
删除
|
||||
</Button>
|
||||
</Flex>
|
||||
)}
|
||||
@@ -621,13 +613,7 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
|
||||
}}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
<Image
|
||||
src={item.avatar}
|
||||
fallbackSrc="/icon/logo.png"
|
||||
w={'20px'}
|
||||
h={'20px'}
|
||||
alt=""
|
||||
></Image>
|
||||
<Avatar src={item.avatar} w={'20px'} h={'20px'} />
|
||||
<Box ml={3} fontWeight={'bold'}>
|
||||
{item.name}
|
||||
</Box>
|
||||
|
||||
@@ -29,7 +29,7 @@ const ModelDetail = ({ modelId, isPc }: { modelId: string; isPc: boolean }) => {
|
||||
},
|
||||
onError(err: any) {
|
||||
toast({
|
||||
title: err?.message || '获取AI助手异常',
|
||||
title: err?.message || '获取应用异常',
|
||||
status: 'error'
|
||||
});
|
||||
setLastModelId('');
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Box, Flex, Image, Button } from '@chakra-ui/react';
|
||||
import { Box, Flex, Button, Tooltip } 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';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const ShareModelList = ({
|
||||
models = [],
|
||||
@@ -27,27 +28,29 @@ const ShareModelList = ({
|
||||
borderRadius={'md'}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
<Image
|
||||
<Avatar
|
||||
src={model.avatar}
|
||||
alt={'avatar'}
|
||||
w={['28px', '36px']}
|
||||
h={['28px', '36px']}
|
||||
objectFit={'cover'}
|
||||
borderRadius={'50%'}
|
||||
/>
|
||||
<Box fontWeight={'bold'} fontSize={'lg'} ml={5}>
|
||||
{model.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box
|
||||
flex={1}
|
||||
className={styles.intro}
|
||||
my={4}
|
||||
fontSize={'sm'}
|
||||
wordBreak={'break-all'}
|
||||
color={'blackAlpha.600'}
|
||||
>
|
||||
{model.share.intro || '这个AI助手还没有介绍~'}
|
||||
</Box>
|
||||
<Tooltip label={model.share.intro}>
|
||||
<Box
|
||||
className={styles.intro}
|
||||
flex={1}
|
||||
my={4}
|
||||
fontSize={'sm'}
|
||||
wordBreak={'break-all'}
|
||||
color={'blackAlpha.600'}
|
||||
>
|
||||
{model.share.intro || '这个 应用 还没有介绍~'}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
|
||||
<Flex justifyContent={'space-between'}>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
@@ -66,21 +69,11 @@ const ShareModelList = ({
|
||||
<Button
|
||||
size={'sm'}
|
||||
variant={'outline'}
|
||||
w={['60px', '80px']}
|
||||
w={['60px', '70px']}
|
||||
onClick={() => router.push(`/chat?modelId=${model._id}`)}
|
||||
>
|
||||
体验
|
||||
</Button>
|
||||
{model.share.isShareDetail && (
|
||||
<Button
|
||||
ml={4}
|
||||
size={'sm'}
|
||||
w={['60px', '80px']}
|
||||
onClick={() => router.push(`/model?modelId=${model._id}`)}
|
||||
>
|
||||
详情
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { useState, useRef, useCallback, useMemo } from 'react';
|
||||
import React, { useState, useRef, useCallback } from 'react';
|
||||
import { Box, Flex, Card, Grid, Input } from '@chakra-ui/react';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { getShareModelList, triggerModelCollection, getCollectionModels } from '@/api/model';
|
||||
import { getShareModelList, triggerModelCollection } from '@/api/model';
|
||||
import { usePagination } from '@/hooks/usePagination';
|
||||
import type { ShareModelItem } from '@/types/model';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import ShareModelList from './components/list';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
const modelList = () => {
|
||||
const { Loading } = useLoading();
|
||||
@@ -15,7 +14,13 @@ const modelList = () => {
|
||||
const { refreshModel } = useUserStore();
|
||||
|
||||
/* 加载模型 */
|
||||
const { data, isLoading, Pagination, getData, pageNum } = usePagination<ShareModelItem>({
|
||||
const {
|
||||
data: models,
|
||||
isLoading,
|
||||
Pagination,
|
||||
getData,
|
||||
pageNum
|
||||
} = usePagination<ShareModelItem>({
|
||||
api: getShareModelList,
|
||||
pageSize: 24,
|
||||
params: {
|
||||
@@ -23,65 +28,32 @@ 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();
|
||||
refreshModel.removeModelDetail(modelId);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
[getData, pageNum, refetchCollection, refreshModel]
|
||||
[getData, pageNum, refreshModel]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box py={[5, 10]} px={'5vw'}>
|
||||
<Card px={6} py={3}>
|
||||
<Flex alignItems={'center'} justifyContent={'space-between'}>
|
||||
<Box fontWeight={'bold'} fontSize={'xl'}>
|
||||
我收藏的AI助手
|
||||
</Box>
|
||||
</Flex>
|
||||
{collectionModels.length == 0 && (
|
||||
<Box textAlign={'center'} pt={3}>
|
||||
还没有收藏AI助手~
|
||||
</Box>
|
||||
)}
|
||||
<Grid templateColumns={['1fr', '1fr 1fr', '1fr 1fr 1fr']} gridGap={4} mt={4}>
|
||||
<ShareModelList models={collectionModels} onclickCollection={onclickCollection} />
|
||||
</Grid>
|
||||
</Card>
|
||||
|
||||
<Card mt={5} px={6} py={3}>
|
||||
<Box display={['block', 'flex']} alignItems={'center'} justifyContent={'space-between'}>
|
||||
<Box fontWeight={'bold'} flex={1} fontSize={'xl'}>
|
||||
AI助手市场
|
||||
<Box as={'span'} fontWeight={'normal'} fontSize={'md'}>
|
||||
(Beta)
|
||||
</Box>
|
||||
应用市场
|
||||
</Box>
|
||||
<Box mt={[2, 0]} textAlign={'right'}>
|
||||
<Input
|
||||
maxW={'240px'}
|
||||
w={['200px', '250px']}
|
||||
size={'sm'}
|
||||
value={searchText}
|
||||
placeholder="搜索AI助手,回车确认"
|
||||
placeholder="搜索应用,回车确认"
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
onBlur={() => {
|
||||
if (searchText === lastSearch.current) return;
|
||||
@@ -98,7 +70,17 @@ const modelList = () => {
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Grid templateColumns={['1fr', '1fr 1fr', '1fr 1fr 1fr']} gridGap={4} mt={4}>
|
||||
<Grid
|
||||
templateColumns={[
|
||||
'repeat(1,1fr)',
|
||||
'repeat(2,1fr)',
|
||||
'repeat(3,1fr)',
|
||||
'repeat(4,1fr)',
|
||||
'repeat(5,1fr)'
|
||||
]}
|
||||
gridGap={4}
|
||||
mt={4}
|
||||
>
|
||||
<ShareModelList models={models} onclickCollection={onclickCollection} />
|
||||
</Grid>
|
||||
<Flex mt={4} justifyContent={'flex-end'}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Card, Box, Flex, Button, Input, Image } from '@chakra-ui/react';
|
||||
import { Card, Box, Flex, Button, Input } from '@chakra-ui/react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { UserUpdateParams } from '@/types/user';
|
||||
import { putUserInfo } from '@/api/user';
|
||||
@@ -14,6 +14,7 @@ import dynamic from 'next/dynamic';
|
||||
import { useSelectFile } from '@/hooks/useSelectFile';
|
||||
import { compressImg } from '@/utils/file';
|
||||
import Loading from '@/components/Loading';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const PayRecordTable = dynamic(() => import('./components/PayRecordTable'), {
|
||||
loading: () => <Loading fixed={false} />,
|
||||
@@ -106,12 +107,10 @@ const NumberSetting = () => {
|
||||
</Flex>
|
||||
<Flex mt={6} alignItems={'center'}>
|
||||
<Box flex={'0 0 50px'}>头像:</Box>
|
||||
<Image
|
||||
<Avatar
|
||||
src={userInfo?.avatar}
|
||||
alt={'avatar'}
|
||||
w={['28px', '36px']}
|
||||
maxH={'40px'}
|
||||
objectFit={'contain'}
|
||||
h={['28px', '36px']}
|
||||
cursor={'pointer'}
|
||||
title={'点击切换头像'}
|
||||
onClick={onOpenSelectFile}
|
||||
|
||||
@@ -11,8 +11,8 @@ const list = [
|
||||
link: '/kb'
|
||||
},
|
||||
{
|
||||
icon: 'shareMarket',
|
||||
label: 'AI助手市场',
|
||||
icon: 'appStore',
|
||||
label: 'AI应用市场',
|
||||
link: '/model/share'
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user