perf: text and avatar

This commit is contained in:
archer
2023-05-22 16:47:41 +08:00
parent 1c8db69a5a
commit ee2c259c3d
33 changed files with 231 additions and 277 deletions

View File

@@ -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
});

View File

@@ -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: []
});
}
}

View File

@@ -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
}
});

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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}

View File

@@ -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}

View File

@@ -65,7 +65,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
});
// aut register a model
postCreateModel({
name: 'AI助手1'
name: '应用1'
});
} catch (error: any) {
toast({

View File

@@ -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>

View File

@@ -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>

View File

@@ -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('');

View File

@@ -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>

View File

@@ -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'}>

View File

@@ -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}

View File

@@ -11,8 +11,8 @@ const list = [
link: '/kb'
},
{
icon: 'shareMarket',
label: 'AI助手市场',
icon: 'appStore',
label: 'AI应用市场',
link: '/model/share'
},
{