mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-21 11:43:56 +00:00
perf: text and avatar
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fastgpt",
|
||||
"version": "0.1.0",
|
||||
"version": "3.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -83,5 +83,8 @@
|
||||
},
|
||||
"lint-staged": {
|
||||
"./src/**/*.{ts,tsx,scss}": "npm run format"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { GET, POST, DELETE, PUT } from './request';
|
||||
import type { ModelSchema } from '@/types/mongoSchema';
|
||||
import type { ModelUpdateParams, ShareModelItem } from '@/types/model';
|
||||
import type { ModelUpdateParams } from '@/types/model';
|
||||
import { RequestPaging } from '../types/index';
|
||||
import type { ModelListResponse } from './response/model';
|
||||
|
||||
@@ -36,10 +36,7 @@ export const putModelById = (id: string, data: ModelUpdateParams) =>
|
||||
*/
|
||||
export const getShareModelList = (data: { searchText?: string } & RequestPaging) =>
|
||||
POST(`/model/share/getModels`, data);
|
||||
/**
|
||||
* 获取我收藏的模型
|
||||
*/
|
||||
export const getCollectionModels = () => GET<ShareModelItem[]>(`/model/share/getCollection`);
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏模型
|
||||
*/
|
||||
|
@@ -54,13 +54,13 @@ function responseError(err: any) {
|
||||
if (typeof err === 'string') {
|
||||
return Promise.reject({ message: err });
|
||||
}
|
||||
if (err.response) {
|
||||
// 有报错响应
|
||||
const res = err.response;
|
||||
if (res.data.code in TOKEN_ERROR_CODE) {
|
||||
clearCookie();
|
||||
return Promise.reject({ message: 'token过期,重新登录' });
|
||||
}
|
||||
// 有报错响应
|
||||
if (err?.code in TOKEN_ERROR_CODE) {
|
||||
clearCookie();
|
||||
window.location.replace(
|
||||
`/login?lastRoute=${encodeURIComponent(location.pathname + location.search)}`
|
||||
);
|
||||
return Promise.reject({ message: 'token过期,重新登录' });
|
||||
}
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
19
src/components/Avatar/index.tsx
Normal file
19
src/components/Avatar/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { Image } from '@chakra-ui/react';
|
||||
import type { ImageProps } from '@chakra-ui/react';
|
||||
|
||||
const Avatar = ({ w = '30px', ...props }: ImageProps) => {
|
||||
return (
|
||||
<Image
|
||||
fallbackSrc="/icon/logo.png"
|
||||
borderRadius={'50%'}
|
||||
objectFit={'contain'}
|
||||
alt=""
|
||||
w={w}
|
||||
h={w}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Avatar;
|
1
src/components/Icon/icons/appStore.svg
Normal file
1
src/components/Icon/icons/appStore.svg
Normal file
@@ -0,0 +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="1684739031957" class="icon" viewBox="0 0 1026 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4988" xmlns:xlink="http://www.w3.org/1999/xlink" width="64.125" height="64"><path d="M371.732817 94.172314q25.773475 0 44.112294 17.843175t18.338819 43.616651l0 247.821878q0 25.773475-18.338819 44.112294t-44.112294 18.338819l-247.821878 0q-25.773475 0-43.616651-18.338819t-17.843175-44.112294l0-247.821878q0-25.773475 17.843175-43.616651t43.616651-17.843175l247.821878 0zM371.732817 589.81607q25.773475 0 44.112294 17.843175t18.338819 43.616651l0 248.813166q0 25.773475-18.338819 43.616651t-44.112294 17.843175l-247.821878 0q-25.773475 0-43.616651-17.843175t-17.843175-43.616651l0-248.813166q0-25.773475 17.843175-43.616651t43.616651-17.843175l247.821878 0zM868.367861 589.81607q25.773475 0 43.616651 17.843175t17.843175 43.616651l0 248.813166q0 25.773475-17.843175 43.616651t-43.616651 17.843175l-247.821878 0q-25.773475 0-44.112294-17.843175t-18.338819-43.616651l0-248.813166q0-25.773475 18.338819-43.616651t44.112294-17.843175l247.821878 0zM1006.156825 203.21394q19.82575 19.82575 19.82575 46.590513t-19.82575 45.599226l-184.379477 184.379477q-19.82575 19.82575-46.094869 19.82575t-46.094869-19.82575l-184.379477-184.379477q-18.834463-18.834463-18.834463-45.599226t18.834463-46.590513l184.379477-184.379477q19.82575-18.834463 46.094869-18.834463t46.094869 18.834463z" p-id="4989"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
1
src/components/Icon/icons/menu.svg
Normal file
1
src/components/Icon/icons/menu.svg
Normal file
@@ -0,0 +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="1684745011703" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1481" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M110.025 252.249c0 13.741 5.456 27.374 14.899 37.112s22.663 15.364 35.987 15.364 26.544-5.626 35.987-15.364c9.441-9.738 14.899-23.371 14.899-37.112s-5.456-27.375-14.899-37.111c-9.442-9.738-22.663-15.364-35.987-15.364s-26.544 5.626-35.987 15.364c-9.338 9.736-14.899 23.37-14.899 37.111m0 0zM103.625 512.575c0 13.741 5.455 27.482 14.899 37.22 9.442 9.738 22.663 15.364 36.091 15.364 13.324 0 26.649-5.626 36.091-15.364s14.899-23.371 14.899-37.22c0-13.741-5.455-27.482-14.899-37.22-9.442-9.738-22.662-15.364-36.091-15.364-13.324 0-26.649 5.626-36.091 15.364-9.444 9.737-14.899 23.37-14.899 37.22m0 0zM103.625 774.089c0 13.741 5.455 27.482 14.899 37.22 9.442 9.738 22.663 15.364 36.091 15.364 13.324 0 26.649-5.626 36.091-15.364s14.899-23.37 14.899-37.22c0-13.741-5.455-27.482-14.899-37.22-9.442-9.737-22.662-15.364-36.091-15.364-13.324 0-26.649 5.627-36.091 15.364-9.444 9.847-14.899 23.479-14.899 37.22m0 0zM919.041 249.869c0 27.699-19.935 50.095-44.59 50.095H345.88c-24.655 0-44.59-22.397-44.59-50.095 0-27.699 19.935-50.095 44.59-50.095h528.571c24.656-0.001 44.59 22.396 44.59 50.095m0 0zM919.041 510.195c0 27.59-19.935 50.095-44.59 50.095H345.88c-24.655 0-44.59-22.398-44.59-50.096 0-27.699 19.935-50.096 44.59-50.096h528.571c24.656-0.109 44.59 22.397 44.59 50.097m0 0zM919.041 771.601c0 27.699-19.935 50.096-44.59 50.096H345.88c-24.655 0-44.59-22.397-44.59-50.096 0-27.591 19.935-49.988 44.59-49.988h528.571c24.656-0.108 44.59 22.397 44.59 49.988m0 0z" p-id="1482"></path></svg>
|
After Width: | Height: | Size: 1.7 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="1683450447995" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2005" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M728.99015111 121.90378667h107.92732444V40.96h-107.92732444v80.94378667z m175.38161778 67.45429333v107.92732445H985.31555555v-107.92732445h-80.94378666z m-67.45429334 175.38161778h-107.92732444v80.94378667h107.92732444v-80.94378667z m-175.38161777-67.45429333v-107.92732445h-80.94378667v107.92732445h80.94378667z m67.45429333 67.45429333c-37.23491555 0-67.45429333-30.21937778-67.45429333-67.45429333h-80.94378667c0 81.97006222 66.42801778 148.39808 148.39808 148.39808v-80.94378667z m175.38161778-67.45429333c0 37.23491555-30.21937778 67.45429333-67.45429334 67.45429333v80.94378667c81.97006222 0 148.39808-66.42801778 148.39808-148.39808h-80.94378666zM836.91747555 121.90151111c37.23491555 0 67.45429333 30.21937778 67.45429334 67.45429334H985.31555555C985.31555555 107.38801778 918.88753778 40.96 836.91747555 40.96v80.94378667zM728.99015111 40.96c-81.97006222 0-148.39808 66.42801778-148.39808 148.39808h80.94378667c0-37.23491555 30.21937778-67.45429333 67.45429333-67.45429333V40.96zM189.35808 661.53585778h107.92732445v-80.94378667h-107.92732445v80.94378667z m175.38161778 67.45429333v107.92732444h80.94378667v-107.92732444h-80.94378667z m-67.45429333 175.38161778h-107.92732445V985.31555555h107.92732445v-80.94378666zM121.90151111 836.91747555v-107.92732444H40.96v107.92732444h80.94378667z m67.45429334 67.45429334c-37.23491555 0-67.45429333-30.21937778-67.45429334-67.45429334H40.96C40.96 918.88753778 107.38801778 985.31555555 189.35808 985.31555555v-80.94378666z m175.38161777-67.45429334c0 37.23491555-30.21937778 67.45429333-67.45429333 67.45429334V985.31555555c81.97006222 0 148.39808-66.42801778 148.39808-148.39808h-80.94378667z m-67.45429333-175.38161777c37.23491555 0 67.45429333 30.21937778 67.45429333 67.45429333h80.94378667c0-81.97006222-66.42801778-148.39808-148.39808-148.39808v80.94378667z m-107.92732444-80.94378667C107.38801778 580.59207111 40.96 647.02008889 40.96 728.99015111h80.94378667c0-37.23491555 30.21937778-67.45429333 67.45429333-67.45429333v-80.94378667z m0-458.68828444h107.92732444V40.96h-107.92732444v80.94378667z m175.38161777 67.45429333v107.92732445h80.94378667v-107.92732445h-80.94378667z m-67.45429333 175.38161778h-107.92732444v80.94378667h107.92732444v-80.94378667zM121.90151111 297.28540445v-107.92732445H40.96v107.92732445h80.94378667z m67.45429334 67.45429333c-37.23491555 0-67.45429333-30.21937778-67.45429334-67.45429333H40.96c0 81.97006222 66.42801778 148.39808 148.39808 148.39808v-80.94378667z m175.38161777-67.45429333c0 37.23491555-30.21937778 67.45429333-67.45429333 67.45429333v80.94378667c81.97006222 0 148.39808-66.42801778 148.39808-148.39808h-80.94378667zM297.28540445 121.90151111c37.23491555 0 67.45429333 30.21937778 67.45429333 67.45429334h80.94378667c0-81.97006222-66.42801778-148.39808-148.39808-148.39808v80.94378666zM189.35808 40.96C107.38801778 40.96 40.96 107.38801778 40.96 189.35808h80.94378667c0-37.23491555 30.21937778-67.45429333 67.45429333-67.45429333V40.96z m539.63207111 620.57585778h107.92732444v-80.94378667h-107.92732444v80.94378667z m175.38161778 67.45429333v107.92732444H985.31555555v-107.92732444h-80.94378666z m-67.45429334 175.38161778h-107.92732444V985.31555555h107.92732444v-80.94378666z m-175.38161777-67.45429334v-107.92732444h-80.94378667v107.92732444h80.94378667z m67.45429333 67.45429334c-37.23491555 0-67.45429333-30.21937778-67.45429333-67.45429334h-80.94378667c0 81.97006222 66.42801778 148.39808 148.39808 148.39808v-80.94378666z m175.38161778-67.45429334c0 37.23491555-30.21937778 67.45429333-67.45429334 67.45429334V985.31555555C918.88753778 985.31555555 985.31555555 918.88753778 985.31555555 836.91747555h-80.94378666z m-67.45429334-175.38161777c37.23491555 0 67.45429333 30.21937778 67.45429334 67.45429333H985.31555555c0-81.97006222-66.42801778-148.39808-148.39808-148.39808v80.94378667z m-107.92732444-80.94378667c-81.97006222 0-148.39808 66.42801778-148.39808 148.39808h80.94378667c0-37.23491555 30.21937778-67.45429333 67.45429333-67.45429333v-80.94378667z" p-id="2006"></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="1684739068105" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7879" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M817.87 556.31h-63.58v-66.24A42.27 42.27 0 0 0 712 447.8h-84.81a42.27 42.27 0 0 0-42.27 42.27v66.24H436.57v-66.24a42.27 42.27 0 0 0-42.27-42.27h-84.83a42.27 42.27 0 0 0-42.27 42.27v66.24h-61.83A22.39 22.39 0 0 0 183 578.7a22.39 22.39 0 0 0 22.39 22.39h61.81v65.55a42.27 42.27 0 0 0 42.27 42.27h84.83a42.27 42.27 0 0 0 42.27-42.27v-65.55h148.36v65.55a42.27 42.27 0 0 0 42.27 42.27H712a42.27 42.27 0 0 0 42.27-42.27v-65.55h63.58a22.39 22.39 0 0 0 22.39-22.39 22.39 22.39 0 0 0-22.37-22.39z m-438.64 95.26h-54.69V505.14h54.69z m317.72 0h-54.69V505.14H697z" p-id="7880"></path><path d="M823 202.58h-90.81v-63.09a71.88 71.88 0 0 0-71.88-71.88H363.19a71.88 71.88 0 0 0-71.88 71.88v63.08h-90.12A137.17 137.17 0 0 0 64 339.75v479a137.17 137.17 0 0 0 137.19 137.14H823a137.17 137.17 0 0 0 137.19-137.17v-479A137.17 137.17 0 0 0 823 202.58z m-474.36-54.1A23.52 23.52 0 0 1 372.17 125h279.16a23.52 23.52 0 0 1 23.52 23.52v54.1h-326.2z m554.23 673.31a76.76 76.76 0 0 1-76.76 76.76h-628a76.76 76.76 0 0 1-76.76-76.76V336.67a76.76 76.76 0 0 1 76.76-76.76h628a76.76 76.76 0 0 1 76.76 76.76z" p-id="7881"></path></svg>
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1 +0,0 @@
|
||||
<?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="1683254591061" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1213" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M389.5296 650.78613333a204.8 204.8 0 1 1-10.82026667-288.49493333L557.43146667 245.76a153.6 153.6 0 1 1 39.25333333 55.9104l-176.46933333 115.02933333c15.01866667 28.50133333 23.48373333 60.928 23.48373333 95.3344a204.11733333 204.11733333 0 0 1-16.86186667 81.47626667l257.1264 144.62293333a153.6 153.6 0 1 1-30.9248 60.928l-263.54346666-148.24106666z" p-id="1214"></path></svg>
|
Before Width: | Height: | Size: 710 B |
@@ -12,7 +12,6 @@ const map = {
|
||||
delete: require('./icons/delete.svg').default,
|
||||
withdraw: require('./icons/withdraw.svg').default,
|
||||
stop: require('./icons/stop.svg').default,
|
||||
shareMarket: require('./icons/shareMarket.svg').default,
|
||||
collectionLight: require('./icons/collectionLight.svg').default,
|
||||
collectionSolid: require('./icons/collectionSolid.svg').default,
|
||||
chat: require('./icons/chat.svg').default,
|
||||
@@ -27,7 +26,9 @@ const map = {
|
||||
wx: require('./icons/wx.svg').default,
|
||||
out: require('./icons/out.svg').default,
|
||||
git: require('./icons/git.svg').default,
|
||||
kb: require('./icons/kb.svg').default
|
||||
kb: require('./icons/kb.svg').default,
|
||||
appStore: require('./icons/appStore.svg').default,
|
||||
menu: require('./icons/menu.svg').default
|
||||
};
|
||||
|
||||
export type IconName = keyof typeof map;
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Box, Flex, Image, Tooltip } from '@chakra-ui/react';
|
||||
import { Box, Flex, Tooltip } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import MyIcon from '../Icon';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import Avatar from '../Avatar';
|
||||
|
||||
export enum NavbarTypeEnum {
|
||||
normal = 'normal',
|
||||
@@ -23,7 +24,7 @@ const Navbar = () => {
|
||||
activeLink: ['/chat']
|
||||
},
|
||||
{
|
||||
label: 'AI助手',
|
||||
label: '我的应用',
|
||||
icon: 'model',
|
||||
link: `/model?modelId=${lastModelId}`,
|
||||
activeLink: ['/model']
|
||||
@@ -35,8 +36,8 @@ const Navbar = () => {
|
||||
activeLink: ['/kb']
|
||||
},
|
||||
{
|
||||
label: '共享',
|
||||
icon: 'shareMarket',
|
||||
label: '应用市场',
|
||||
icon: 'appStore',
|
||||
link: '/model/share',
|
||||
activeLink: ['/model/share']
|
||||
},
|
||||
@@ -82,13 +83,7 @@ const Navbar = () => {
|
||||
cursor={'pointer'}
|
||||
onClick={() => router.push('/number')}
|
||||
>
|
||||
<Image
|
||||
src={userInfo?.avatar || '/icon/human.png'}
|
||||
objectFit={'contain'}
|
||||
w={'36px'}
|
||||
h={'36px'}
|
||||
alt=""
|
||||
/>
|
||||
<Avatar w={'36px'} h={'36px'} src={userInfo?.avatar} fallbackSrc={'/icon/human.png'} />
|
||||
</Box>
|
||||
{/* 导航列表 */}
|
||||
<Box flex={1}>
|
||||
|
@@ -10,25 +10,21 @@ const NavbarPhone = () => {
|
||||
const navbarList = useMemo(
|
||||
() => [
|
||||
{
|
||||
label: '聊天',
|
||||
icon: 'tabbarChat',
|
||||
link: `/chat?modelId=${lastChatModelId}&chatId=${lastChatId}`,
|
||||
activeLink: ['/chat']
|
||||
},
|
||||
{
|
||||
label: 'AI助手',
|
||||
icon: 'tabbarModel',
|
||||
link: `/model`,
|
||||
activeLink: ['/model']
|
||||
},
|
||||
{
|
||||
label: '发现',
|
||||
icon: 'tabbarMore',
|
||||
link: '/tools',
|
||||
activeLink: ['/tools']
|
||||
},
|
||||
{
|
||||
label: '我',
|
||||
icon: 'tabbarMe',
|
||||
link: '/number',
|
||||
activeLink: ['/number']
|
||||
|
@@ -339,9 +339,12 @@
|
||||
text-align: justify;
|
||||
tab-size: 4;
|
||||
word-spacing: normal;
|
||||
word-break: break-all;
|
||||
width: 100%;
|
||||
|
||||
* {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
p {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
@@ -27,6 +27,6 @@ export enum PromotionEnum {
|
||||
|
||||
export const PromotionTypeMap = {
|
||||
[PromotionEnum.invite]: '好友充值',
|
||||
[PromotionEnum.shareModel]: 'AI助手分享',
|
||||
[PromotionEnum.shareModel]: '应用分享',
|
||||
[PromotionEnum.withdraw]: '提现'
|
||||
};
|
||||
|
@@ -4,7 +4,6 @@ import { IconButton, Flex, Box, Input } from '@chakra-ui/react';
|
||||
import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useToast } from './useToast';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
export const usePagination = <T = any,>({
|
||||
api,
|
||||
|
@@ -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'
|
||||
},
|
||||
{
|
||||
|
@@ -11,6 +11,28 @@ import { ERROR_ENUM } from '../errorCode';
|
||||
import { ChatModelType, OpenAiChatEnum } from '@/constants/model';
|
||||
import { hashPassword } from '@/service/utils/tools';
|
||||
|
||||
export const parseCookie = (cookie?: string): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 获取 cookie
|
||||
const cookies = Cookie.parse(cookie || '');
|
||||
const token = cookies.token;
|
||||
|
||||
if (!token) {
|
||||
return reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
const key = process.env.TOKEN_KEY as string;
|
||||
|
||||
jwt.verify(token, key, function (err, decoded: any) {
|
||||
if (err || !decoded?.userId) {
|
||||
reject(ERROR_ENUM.unAuthorization);
|
||||
return;
|
||||
}
|
||||
resolve(decoded.userId);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/* uniform auth user */
|
||||
export const authUser = async ({
|
||||
req,
|
||||
@@ -23,27 +45,6 @@ export const authUser = async ({
|
||||
authOpenApi?: boolean;
|
||||
authRoot?: boolean;
|
||||
}) => {
|
||||
const parseCookie = (cookie?: string): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 获取 cookie
|
||||
const cookies = Cookie.parse(cookie || '');
|
||||
const token = cookies.token;
|
||||
|
||||
if (!token) {
|
||||
return reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
const key = process.env.TOKEN_KEY as string;
|
||||
|
||||
jwt.verify(token, key, function (err, decoded: any) {
|
||||
if (err || !decoded?.userId) {
|
||||
reject(ERROR_ENUM.unAuthorization);
|
||||
return;
|
||||
}
|
||||
resolve(decoded.userId);
|
||||
});
|
||||
});
|
||||
};
|
||||
const parseOpenApiKey = async (apiKey?: string) => {
|
||||
if (!apiKey) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
|
Reference in New Issue
Block a user