mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-29 09:44:47 +00:00
add app
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
import { GET, POST, DELETE, PUT } from './request';
|
import { GET, POST, DELETE, PUT } from './request';
|
||||||
import type { AppSchema } from '@/types/mongoSchema';
|
import type { AppSchema } from '@/types/mongoSchema';
|
||||||
import type { AppUpdateParams } from '@/types/app';
|
import type { AppModuleItemType, AppUpdateParams } from '@/types/app';
|
||||||
import { RequestPaging } from '../types/index';
|
import { RequestPaging } from '../types/index';
|
||||||
import type { AppListResponse } from './response/app';
|
import type { AppListResponse } from './response/app';
|
||||||
|
import type { Props as CreateAppProps } from '@/pages/api/app/create';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取模型列表
|
* 获取模型列表
|
||||||
@@ -12,7 +13,7 @@ export const getMyModels = () => GET<AppListResponse>('/app/list');
|
|||||||
/**
|
/**
|
||||||
* 创建一个模型
|
* 创建一个模型
|
||||||
*/
|
*/
|
||||||
export const postCreateModel = (data: { name: string }) => POST<string>('/app/create', data);
|
export const postCreateApp = (data: CreateAppProps) => POST<string>('/app/create', data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 ID 删除模型
|
* 根据 ID 删除模型
|
||||||
|
@@ -20,8 +20,8 @@ const NavbarPhone = ({ unread }: { unread: number }) => {
|
|||||||
{
|
{
|
||||||
label: '应用',
|
label: '应用',
|
||||||
icon: 'tabbarModel',
|
icon: 'tabbarModel',
|
||||||
link: `/model`,
|
link: `/app/list`,
|
||||||
activeLink: ['/model'],
|
activeLink: ['/app/list'],
|
||||||
unread: 0
|
unread: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
22
client/src/components/PageContainer/index.tsx
Normal file
22
client/src/components/PageContainer/index.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box, useTheme, type BoxProps } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
const PageContainer = ({ children, ...props }: BoxProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
return (
|
||||||
|
<Box bg={'myGray.100'} h={'100%'} p={[0, 5]} {...props}>
|
||||||
|
<Box
|
||||||
|
flex={1}
|
||||||
|
h={'100%'}
|
||||||
|
bg={'white'}
|
||||||
|
borderRadius={['', '2xl']}
|
||||||
|
border={['', theme.borders.lg]}
|
||||||
|
overflowY={'auto'}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PageContainer;
|
@@ -42,20 +42,19 @@ const SlideTabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) =
|
|||||||
px={3}
|
px={3}
|
||||||
mb={2}
|
mb={2}
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
_hover={{
|
|
||||||
bg: 'myWhite.600'
|
|
||||||
}}
|
|
||||||
{...(activeId === item.id
|
{...(activeId === item.id
|
||||||
? {
|
? {
|
||||||
// backgroundImage: 'linear-gradient(to right, #85b1ff 0%, #EBF7FD 100%)',
|
bg: ' myBlue.300 !important',
|
||||||
bg: ' myBlue.300',
|
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: 'myBlue.700',
|
color: 'myBlue.700 ',
|
||||||
cursor: 'default'
|
cursor: 'default'
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
cursor: 'pointer'
|
cursor: 'pointer'
|
||||||
})}
|
})}
|
||||||
|
_hover={{
|
||||||
|
bg: 'myWhite.600'
|
||||||
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (activeId === item.id) return;
|
if (activeId === item.id) return;
|
||||||
onChange(item.id);
|
onChange(item.id);
|
||||||
|
@@ -4,7 +4,7 @@ import { useRouter } from 'next/router';
|
|||||||
const NonePage = () => {
|
const NonePage = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
router.push('/model');
|
router.push('/app/list');
|
||||||
}, [router]);
|
}, [router]);
|
||||||
|
|
||||||
return <div></div>;
|
return <div></div>;
|
||||||
|
@@ -50,10 +50,10 @@ function App({ Component, pageProps }: AppProps) {
|
|||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<Script src="/js/particles.js"></Script>
|
<Script src="/js/particles.js"></Script>
|
||||||
<Script src="/js/qrcode.min.js" strategy="afterInteractive"></Script>
|
<Script src="/js/qrcode.min.js" strategy="lazyOnload"></Script>
|
||||||
<Script src="/js/pdf.js" strategy="afterInteractive"></Script>
|
<Script src="/js/pdf.js" strategy="lazyOnload"></Script>
|
||||||
<Script src="/js/html2pdf.bundle.min.js" strategy="afterInteractive"></Script>
|
<Script src="/js/html2pdf.bundle.min.js" strategy="lazyOnload"></Script>
|
||||||
{baiduTongji && <Script src="/js/baidutongji.js" strategy="afterInteractive"></Script>}
|
{baiduTongji && <Script src="/js/baidutongji.js" strategy="lazyOnload"></Script>}
|
||||||
{googleVerKey && (
|
{googleVerKey && (
|
||||||
<>
|
<>
|
||||||
<Script
|
<Script
|
||||||
|
@@ -4,14 +4,19 @@ import { jsonRes } from '@/service/response';
|
|||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import { App } from '@/service/models/app';
|
import { App } from '@/service/models/app';
|
||||||
|
import { AppModuleItemType } from '@/types/app';
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
name: string;
|
||||||
|
avatar?: string;
|
||||||
|
modules: AppModuleItemType[];
|
||||||
|
};
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
const { name } = req.body as {
|
const { name, avatar, modules } = req.body as Props;
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!name) {
|
if (!name || !Array.isArray(modules)) {
|
||||||
throw new Error('缺少参数');
|
throw new Error('缺少参数');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,8 +35,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
|
|
||||||
// 创建模型
|
// 创建模型
|
||||||
const response = await App.create({
|
const response = await App.create({
|
||||||
|
avatar,
|
||||||
name,
|
name,
|
||||||
userId
|
userId,
|
||||||
|
modules
|
||||||
});
|
});
|
||||||
|
|
||||||
jsonRes(res, {
|
jsonRes(res, {
|
||||||
|
@@ -20,8 +20,7 @@ const Settings = ({ modelId }: { modelId: string }) => {
|
|||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { Loading, setIsLoading } = useLoading();
|
const { Loading, setIsLoading } = useLoading();
|
||||||
const { userInfo, appDetail, myApps, loadAppDetail, refreshModel, setLastModelId } =
|
const { userInfo, appDetail, myApps, loadAppDetail, setLastModelId } = useUserStore();
|
||||||
useUserStore();
|
|
||||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||||
fileType: '.jpg,.png',
|
fileType: '.jpg,.png',
|
||||||
multiple: false
|
multiple: false
|
||||||
@@ -61,8 +60,6 @@ const Settings = ({ modelId }: { modelId: string }) => {
|
|||||||
chat: data.chat,
|
chat: data.chat,
|
||||||
share: data.share
|
share: data.share
|
||||||
});
|
});
|
||||||
|
|
||||||
refreshModel.updateModelDetail(data);
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
toast({
|
toast({
|
||||||
title: err?.message || '更新失败',
|
title: err?.message || '更新失败',
|
||||||
@@ -71,7 +68,7 @@ const Settings = ({ modelId }: { modelId: string }) => {
|
|||||||
}
|
}
|
||||||
setBtnLoading(false);
|
setBtnLoading(false);
|
||||||
},
|
},
|
||||||
[refreshModel, toast]
|
[toast]
|
||||||
);
|
);
|
||||||
// 提交保存表单失败
|
// 提交保存表单失败
|
||||||
const saveSubmitError = useCallback(() => {
|
const saveSubmitError = useCallback(() => {
|
||||||
@@ -106,8 +103,7 @@ const Settings = ({ modelId }: { modelId: string }) => {
|
|||||||
title: '删除成功',
|
title: '删除成功',
|
||||||
status: 'success'
|
status: 'success'
|
||||||
});
|
});
|
||||||
refreshModel.removeModelDetail(appDetail._id);
|
router.replace(`/app/list`);
|
||||||
router.replace(`/model?modelId=${myApps[1]?._id}`);
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
toast({
|
toast({
|
||||||
title: err?.message || '删除失败',
|
title: err?.message || '删除失败',
|
||||||
@@ -115,7 +111,7 @@ const Settings = ({ modelId }: { modelId: string }) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}, [appDetail, setIsLoading, toast, refreshModel, router, myApps]);
|
}, [appDetail, setIsLoading, toast, router]);
|
||||||
|
|
||||||
const onSelectFile = useCallback(
|
const onSelectFile = useCallback(
|
||||||
async (e: File[]) => {
|
async (e: File[]) => {
|
||||||
@@ -152,7 +148,6 @@ const Settings = ({ modelId }: { modelId: string }) => {
|
|||||||
status: 'error'
|
status: 'error'
|
||||||
});
|
});
|
||||||
setLastModelId('');
|
setLastModelId('');
|
||||||
refreshModel.freshMyModels();
|
|
||||||
router.replace('/model');
|
router.replace('/model');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -9,6 +9,7 @@ import SlideTabs from '@/components/SlideTabs';
|
|||||||
import Settings from './components/Settings';
|
import Settings from './components/Settings';
|
||||||
import { defaultApp } from '@/constants/model';
|
import { defaultApp } from '@/constants/model';
|
||||||
import Avatar from '@/components/Avatar';
|
import Avatar from '@/components/Avatar';
|
||||||
|
import PageContainer from '@/components/PageContainer';
|
||||||
|
|
||||||
const EditApp = dynamic(() => import('./components/edit'), {
|
const EditApp = dynamic(() => import('./components/edit'), {
|
||||||
ssr: false
|
ssr: false
|
||||||
@@ -78,14 +79,8 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
}, [appId, loadAppDetail]);
|
}, [appId, loadAppDetail]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexDirection={'column'} bg={'myGray.100'} h={'100%'} p={[0, 5]}>
|
<PageContainer>
|
||||||
<Box
|
<Box display={['block', 'flex']} h={'100%'}>
|
||||||
display={['block', 'flex']}
|
|
||||||
flex={1}
|
|
||||||
bg={'white'}
|
|
||||||
borderRadius={['', '2xl']}
|
|
||||||
border={['', theme.borders.lg]}
|
|
||||||
>
|
|
||||||
{/* pc tab */}
|
{/* pc tab */}
|
||||||
<Box display={['none', 'block']} p={4} w={'200px'} borderRight={theme.borders.base}>
|
<Box display={['none', 'block']} p={4} w={'200px'} borderRight={theme.borders.base}>
|
||||||
<Flex mb={4}>
|
<Flex mb={4}>
|
||||||
@@ -141,7 +136,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
{currentTab === TabEnum.share && <Share modelId={appId} />}
|
{currentTab === TabEnum.share && <Share modelId={appId} />}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
217
client/src/pages/app/list/component/CreateModal.tsx
Normal file
217
client/src/pages/app/list/component/CreateModal.tsx
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
import React, { useCallback, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
ModalOverlay,
|
||||||
|
ModalContent,
|
||||||
|
ModalHeader,
|
||||||
|
ModalFooter,
|
||||||
|
ModalBody,
|
||||||
|
Input,
|
||||||
|
Grid,
|
||||||
|
useTheme,
|
||||||
|
Card
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { useSelectFile } from '@/hooks/useSelectFile';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { compressImg } from '@/utils/file';
|
||||||
|
import { getErrText } from '@/utils/tools';
|
||||||
|
import { useToast } from '@/hooks/useToast';
|
||||||
|
import { postCreateApp } from '@/api/app';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import { chatAppDemo } from '@/constants/app';
|
||||||
|
import Avatar from '@/components/Avatar';
|
||||||
|
import MyIcon from '@/components/Icon';
|
||||||
|
|
||||||
|
type FormType = {
|
||||||
|
avatar: string;
|
||||||
|
name: string;
|
||||||
|
templateId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const templates = [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
icon: 'settings',
|
||||||
|
name: '简单的对话',
|
||||||
|
intro: '一个极其简单的 AI 对话应用',
|
||||||
|
modules: chatAppDemo.modules
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
icon: 'settings',
|
||||||
|
name: '基础知识库',
|
||||||
|
intro: '每次提问时进行一次知识库搜索,将搜索结果注入 LLM 模型进行参考回答',
|
||||||
|
modules: chatAppDemo.modules
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
icon: 'settings',
|
||||||
|
name: '问答前引导',
|
||||||
|
intro: '可以在每次对话开始前提示用户填写一些内容,作为本次对话的永久内容',
|
||||||
|
modules: chatAppDemo.modules
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
icon: 'settings',
|
||||||
|
name: '意图识别 + 知识库',
|
||||||
|
intro: '先对用户的问题进行分类,再根据不同类型问题,执行不同的操作',
|
||||||
|
modules: chatAppDemo.modules
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: () => void }) => {
|
||||||
|
const [refresh, setRefresh] = useState(false);
|
||||||
|
const [creating, setCreating] = useState(false);
|
||||||
|
const { toast } = useToast();
|
||||||
|
const router = useRouter();
|
||||||
|
const theme = useTheme();
|
||||||
|
const { register, setValue, getValues, handleSubmit } = useForm<FormType>({
|
||||||
|
defaultValues: {
|
||||||
|
avatar: '/icon/logo.png',
|
||||||
|
name: '',
|
||||||
|
templateId: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||||
|
fileType: '.jpg,.png',
|
||||||
|
multiple: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSelectFile = useCallback(
|
||||||
|
async (e: File[]) => {
|
||||||
|
const file = e[0];
|
||||||
|
if (!file) return;
|
||||||
|
try {
|
||||||
|
const src = await compressImg({
|
||||||
|
file,
|
||||||
|
maxW: 100,
|
||||||
|
maxH: 100
|
||||||
|
});
|
||||||
|
setValue('avatar', src);
|
||||||
|
setRefresh((state) => !state);
|
||||||
|
} catch (err: any) {
|
||||||
|
toast({
|
||||||
|
title: getErrText(err, '头像选择异常'),
|
||||||
|
status: 'warning'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[setValue, toast]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onclickCreate = useCallback(
|
||||||
|
async (data: FormType) => {
|
||||||
|
setCreating(true);
|
||||||
|
try {
|
||||||
|
const id = await postCreateApp({
|
||||||
|
avatar: data.avatar,
|
||||||
|
name: data.name,
|
||||||
|
modules: templates.find((item) => item.id === data.templateId)?.modules || []
|
||||||
|
});
|
||||||
|
toast({
|
||||||
|
title: '创建成功',
|
||||||
|
status: 'success'
|
||||||
|
});
|
||||||
|
router.push(`/app/detail?appId=${id}`);
|
||||||
|
onClose();
|
||||||
|
onSuccess();
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
title: getErrText(error, '创建应用异常'),
|
||||||
|
status: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setCreating(false);
|
||||||
|
},
|
||||||
|
[onClose, onSuccess, router, toast]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen onClose={onClose}>
|
||||||
|
<ModalOverlay />
|
||||||
|
<ModalContent w={'700px'} maxW={'90vw'}>
|
||||||
|
<ModalHeader fontSize={'2xl'}>开始创建你的 AI 应用</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<Box color={'myGray.800'}>取个响亮的名字</Box>
|
||||||
|
<Flex mt={3} alignItems={'center'}>
|
||||||
|
<Avatar
|
||||||
|
src={getValues('avatar')}
|
||||||
|
w={['32px', '36px']}
|
||||||
|
h={['32px', '36px']}
|
||||||
|
cursor={'pointer'}
|
||||||
|
title={'点击选择头像'}
|
||||||
|
borderRadius={'md'}
|
||||||
|
onClick={onOpenSelectFile}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
ml={4}
|
||||||
|
bg={'myWhite.600'}
|
||||||
|
{...register('name', {
|
||||||
|
required: '应用名不能为空~'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Box mt={7} mb={3} color={'myGray.800'}>
|
||||||
|
从模板中选择
|
||||||
|
</Box>
|
||||||
|
<Grid
|
||||||
|
userSelect={'none'}
|
||||||
|
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)']}
|
||||||
|
gridGap={4}
|
||||||
|
>
|
||||||
|
{templates.map((item) => (
|
||||||
|
<Card
|
||||||
|
key={item.id}
|
||||||
|
border={theme.borders.base}
|
||||||
|
p={3}
|
||||||
|
borderRadius={'md'}
|
||||||
|
cursor={'pointer'}
|
||||||
|
boxShadow={'sm'}
|
||||||
|
{...(getValues('templateId') === item.id
|
||||||
|
? {
|
||||||
|
bg: 'myBlue.300'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
_hover: {
|
||||||
|
boxShadow: 'md'
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
onClick={() => {
|
||||||
|
setValue('templateId', item.id);
|
||||||
|
setRefresh((state) => !state);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex alignItems={'center'}>
|
||||||
|
<MyIcon name={'apikey'} w={'16px'} />
|
||||||
|
<Box ml={3} fontWeight={'bold'}>
|
||||||
|
{item.name}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
<Box fontSize={'sm'} mt={4}>
|
||||||
|
{item.intro}
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button variant={'base'} mr={3} onClick={onClose}>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button isLoading={creating} onClick={handleSubmit(onclickCreate)}>
|
||||||
|
确认创建
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
|
||||||
|
<File onSelect={onSelectFile} />
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CreateModal;
|
@@ -1,29 +1,76 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Box, Grid, Card, useTheme, Flex, IconButton, Button } from '@chakra-ui/react';
|
import {
|
||||||
|
Box,
|
||||||
|
Grid,
|
||||||
|
Card,
|
||||||
|
useTheme,
|
||||||
|
Flex,
|
||||||
|
IconButton,
|
||||||
|
Button,
|
||||||
|
useDisclosure
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useUserStore } from '@/store/user';
|
import { useUserStore } from '@/store/user';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import Avatar from '@/components/Avatar';
|
|
||||||
|
|
||||||
import styles from './index.module.scss';
|
|
||||||
import MyIcon from '@/components/Icon';
|
|
||||||
import { AddIcon } from '@chakra-ui/icons';
|
import { AddIcon } from '@chakra-ui/icons';
|
||||||
|
import { delModelById } from '@/api/app';
|
||||||
|
import { useToast } from '@/hooks/useToast';
|
||||||
|
import { useConfirm } from '@/hooks/useConfirm';
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
|
||||||
|
import MyIcon from '@/components/Icon';
|
||||||
|
import PageContainer from '@/components/PageContainer';
|
||||||
|
import Avatar from '@/components/Avatar';
|
||||||
|
const CreateModal = dynamic(() => import('./component/CreateModal'));
|
||||||
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
const MyApps = () => {
|
const MyApps = () => {
|
||||||
|
const { toast } = useToast();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { myApps, loadMyModels } = useUserStore();
|
const { myApps, loadMyModels } = useUserStore();
|
||||||
|
const { openConfirm, ConfirmChild } = useConfirm({
|
||||||
|
title: '删除提示',
|
||||||
|
content: '确认删除该应用所有信息?'
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
isOpen: isOpenCreateModal,
|
||||||
|
onOpen: onOpenCreateModal,
|
||||||
|
onClose: onCloseCreateModal
|
||||||
|
} = useDisclosure();
|
||||||
|
|
||||||
|
/* 点击删除 */
|
||||||
|
const onclickDelApp = useCallback(
|
||||||
|
async (id: string) => {
|
||||||
|
try {
|
||||||
|
await delModelById(id);
|
||||||
|
toast({
|
||||||
|
title: '删除成功',
|
||||||
|
status: 'success'
|
||||||
|
});
|
||||||
|
loadMyModels();
|
||||||
|
} catch (err: any) {
|
||||||
|
toast({
|
||||||
|
title: err?.message || '删除失败',
|
||||||
|
status: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[toast, loadMyModels]
|
||||||
|
);
|
||||||
|
|
||||||
/* 加载模型 */
|
/* 加载模型 */
|
||||||
useQuery(['loadModels'], () => loadMyModels(false));
|
useQuery(['loadModels'], loadMyModels, {
|
||||||
|
refetchOnMount: true
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<PageContainer>
|
||||||
<Flex py={3} px={5} borderBottom={theme.borders.base} alignItems={'center'}>
|
<Flex pt={3} px={5} alignItems={'center'}>
|
||||||
<Box flex={1} className="textlg" letterSpacing={1} fontSize={'24px'} fontWeight={'bold'}>
|
<Box flex={1} className="textlg" letterSpacing={1} fontSize={'24px'} fontWeight={'bold'}>
|
||||||
我的应用
|
我的应用
|
||||||
</Box>
|
</Box>
|
||||||
<Button leftIcon={<AddIcon />} variant={'base'}>
|
<Button leftIcon={<AddIcon />} variant={'base'} onClick={onOpenCreateModal}>
|
||||||
新建
|
新建
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -43,8 +90,7 @@ const MyApps = () => {
|
|||||||
boxShadow={'none'}
|
boxShadow={'none'}
|
||||||
userSelect={'none'}
|
userSelect={'none'}
|
||||||
_hover={{
|
_hover={{
|
||||||
boxShadow: 'xl',
|
boxShadow: '1px 1px 10px rgba(0,0,0,0.2)',
|
||||||
transform: 'scale(1.03)',
|
|
||||||
borderColor: 'transparent',
|
borderColor: 'transparent',
|
||||||
'& .delete': {
|
'& .delete': {
|
||||||
display: 'block'
|
display: 'block'
|
||||||
@@ -64,10 +110,14 @@ const MyApps = () => {
|
|||||||
variant={'base'}
|
variant={'base'}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
aria-label={'delete'}
|
aria-label={'delete'}
|
||||||
display={'none'}
|
display={['', 'none']}
|
||||||
_hover={{
|
_hover={{
|
||||||
bg: 'myGray.100'
|
bg: 'myGray.100'
|
||||||
}}
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openConfirm(() => onclickDelApp(app._id))();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box className={styles.intro} py={2} fontSize={'sm'} color={'myGray.600'}>
|
<Box className={styles.intro} py={2} fontSize={'sm'} color={'myGray.600'}>
|
||||||
@@ -76,7 +126,9 @@ const MyApps = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
<ConfirmChild />
|
||||||
|
{isOpenCreateModal && <CreateModal onClose={onCloseCreateModal} onSuccess={loadMyModels} />}
|
||||||
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -94,7 +94,7 @@ const PcSliderBar = ({
|
|||||||
[isPc]
|
[isPc]
|
||||||
);
|
);
|
||||||
|
|
||||||
useQuery(['loadModels'], () => loadMyModels(false));
|
useQuery(['loadModels'], loadMyModels);
|
||||||
|
|
||||||
const { isLoading: isLoadingHistory } = useQuery(['loadingHistory'], () =>
|
const { isLoading: isLoadingHistory } = useQuery(['loadingHistory'], () =>
|
||||||
loadHistory({ pageNum: 1 })
|
loadHistory({ pageNum: 1 })
|
||||||
|
@@ -39,7 +39,7 @@ const PhoneSliderBar = ({
|
|||||||
const { isOpen: isOpenWx, onOpen: onOpenWx, onClose: onCloseWx } = useDisclosure();
|
const { isOpen: isOpenWx, onOpen: onOpenWx, onClose: onCloseWx } = useDisclosure();
|
||||||
|
|
||||||
const models = useMemo(() => [...myApps, ...myCollectionApps], [myCollectionApps, myApps]);
|
const models = useMemo(() => [...myApps, ...myCollectionApps], [myCollectionApps, myApps]);
|
||||||
useQuery(['loadModels'], () => loadMyModels(false));
|
useQuery(['loadModels'], loadMyModels);
|
||||||
|
|
||||||
const { history, loadHistory } = useChatStore();
|
const { history, loadHistory } = useChatStore();
|
||||||
useQuery(['loadingHistory'], () => loadHistory({ pageNum: 1 }));
|
useQuery(['loadingHistory'], () => loadHistory({ pageNum: 1 }));
|
||||||
|
@@ -106,7 +106,7 @@ const Chat = () => {
|
|||||||
const { copyData } = useCopyData();
|
const { copyData } = useCopyData();
|
||||||
const { isPc } = useGlobalStore();
|
const { isPc } = useGlobalStore();
|
||||||
const { Loading, setIsLoading } = useLoading();
|
const { Loading, setIsLoading } = useLoading();
|
||||||
const { userInfo, loadMyModels } = useUserStore();
|
const { userInfo } = useUserStore();
|
||||||
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
||||||
|
|
||||||
// close contextMenu
|
// close contextMenu
|
||||||
@@ -232,7 +232,6 @@ const Chat = () => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
generatingMessage();
|
generatingMessage();
|
||||||
loadHistory({ pageNum: 1, init: true });
|
loadHistory({ pageNum: 1, init: true });
|
||||||
loadMyModels(true);
|
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
if (errMsg) {
|
if (errMsg) {
|
||||||
@@ -252,7 +251,6 @@ const Chat = () => {
|
|||||||
chatData.systemPrompt,
|
chatData.systemPrompt,
|
||||||
chatData.limitPrompt,
|
chatData.limitPrompt,
|
||||||
loadHistory,
|
loadHistory,
|
||||||
loadMyModels,
|
|
||||||
toast
|
toast
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@@ -205,7 +205,7 @@ const Home = () => {
|
|||||||
fontSize={['xl', '3xl']}
|
fontSize={['xl', '3xl']}
|
||||||
h={'auto'}
|
h={'auto'}
|
||||||
py={[2, 3]}
|
py={[2, 3]}
|
||||||
onClick={() => router.push(`/model`)}
|
onClick={() => router.push(`/app/list`)}
|
||||||
>
|
>
|
||||||
立即开始
|
立即开始
|
||||||
</Button>
|
</Button>
|
||||||
|
@@ -7,7 +7,8 @@ import { useSendCode } from '@/hooks/useSendCode';
|
|||||||
import type { ResLogin } from '@/api/response/user';
|
import type { ResLogin } from '@/api/response/user';
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { postCreateModel } from '@/api/app';
|
import { postCreateApp } from '@/api/app';
|
||||||
|
import { chatAppDemo } from '@/constants/app';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
loginSuccess: (e: ResLogin) => void;
|
loginSuccess: (e: ResLogin) => void;
|
||||||
@@ -64,8 +65,9 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
status: 'success'
|
status: 'success'
|
||||||
});
|
});
|
||||||
// aut register a model
|
// aut register a model
|
||||||
postCreateModel({
|
postCreateApp({
|
||||||
name: '应用1'
|
name: '应用1',
|
||||||
|
modules: chatAppDemo.modules
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast({
|
toast({
|
||||||
|
@@ -17,7 +17,7 @@ const Login = () => {
|
|||||||
const { lastRoute = '' } = router.query as { lastRoute: string };
|
const { lastRoute = '' } = router.query as { lastRoute: string };
|
||||||
const { isPc } = useGlobalStore();
|
const { isPc } = useGlobalStore();
|
||||||
const [pageType, setPageType] = useState<`${PageTypeEnum}`>(PageTypeEnum.login);
|
const [pageType, setPageType] = useState<`${PageTypeEnum}`>(PageTypeEnum.login);
|
||||||
const { setUserInfo, setLastModelId, loadMyModels, loadKbList, setLastKbId } = useUserStore();
|
const { setUserInfo, setLastModelId, loadKbList, setLastKbId } = useUserStore();
|
||||||
const { setLastChatId, setLastChatModelId, loadHistory } = useChatStore();
|
const { setLastChatId, setLastChatModelId, loadHistory } = useChatStore();
|
||||||
|
|
||||||
const loginSuccess = useCallback(
|
const loginSuccess = useCallback(
|
||||||
@@ -27,7 +27,6 @@ const Login = () => {
|
|||||||
setLastModelId('');
|
setLastModelId('');
|
||||||
setLastChatModelId('');
|
setLastChatModelId('');
|
||||||
setLastKbId('');
|
setLastKbId('');
|
||||||
loadMyModels(true);
|
|
||||||
loadKbList(true);
|
loadKbList(true);
|
||||||
loadHistory({ pageNum: 1, init: true });
|
loadHistory({ pageNum: 1, init: true });
|
||||||
|
|
||||||
@@ -40,7 +39,6 @@ const Login = () => {
|
|||||||
lastRoute,
|
lastRoute,
|
||||||
loadHistory,
|
loadHistory,
|
||||||
loadKbList,
|
loadKbList,
|
||||||
loadMyModels,
|
|
||||||
router,
|
router,
|
||||||
setLastChatId,
|
setLastChatId,
|
||||||
setLastChatModelId,
|
setLastChatModelId,
|
||||||
|
@@ -22,14 +22,9 @@ type State = {
|
|||||||
setLastModelId: (id: string) => void;
|
setLastModelId: (id: string) => void;
|
||||||
myApps: AppListItemType[];
|
myApps: AppListItemType[];
|
||||||
myCollectionApps: AppListItemType[];
|
myCollectionApps: AppListItemType[];
|
||||||
loadMyModels: (init?: boolean) => Promise<null>;
|
loadMyModels: () => Promise<null>;
|
||||||
appDetail: AppSchema;
|
appDetail: AppSchema;
|
||||||
loadAppDetail: (id: string, init?: boolean) => Promise<AppSchema>;
|
loadAppDetail: (id: string, init?: boolean) => Promise<AppSchema>;
|
||||||
refreshModel: {
|
|
||||||
freshMyModels(): void;
|
|
||||||
updateModelDetail(model: AppSchema): void;
|
|
||||||
removeModelDetail(modelId: string): void;
|
|
||||||
};
|
|
||||||
// kb
|
// kb
|
||||||
lastKbId: string;
|
lastKbId: string;
|
||||||
setLastKbId: (id: string) => void;
|
setLastKbId: (id: string) => void;
|
||||||
@@ -76,8 +71,7 @@ export const useUserStore = create<State>()(
|
|||||||
},
|
},
|
||||||
myApps: [],
|
myApps: [],
|
||||||
myCollectionApps: [],
|
myCollectionApps: [],
|
||||||
async loadMyModels(init = false) {
|
async loadMyModels() {
|
||||||
if (get().myApps.length > 0 && !init) return null;
|
|
||||||
const res = await getMyModels();
|
const res = await getMyModels();
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.myApps = res.myApps;
|
state.myApps = res.myApps;
|
||||||
@@ -95,26 +89,6 @@ export const useUserStore = create<State>()(
|
|||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
refreshModel: {
|
|
||||||
freshMyModels() {
|
|
||||||
get().loadMyModels(true);
|
|
||||||
},
|
|
||||||
updateModelDetail(model: AppSchema) {
|
|
||||||
set((state) => {
|
|
||||||
state.appDetail = model;
|
|
||||||
});
|
|
||||||
get().loadMyModels(true);
|
|
||||||
},
|
|
||||||
removeModelDetail(modelId: string) {
|
|
||||||
if (modelId === get().appDetail._id) {
|
|
||||||
set((state) => {
|
|
||||||
state.appDetail = defaultApp;
|
|
||||||
state.lastModelId = '';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
get().loadMyModels(true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
lastKbId: '',
|
lastKbId: '',
|
||||||
setLastKbId(id: string) {
|
setLastKbId(id: string) {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
|
Reference in New Issue
Block a user