perf: 聊天页优化

perf: md解析样式

perf: ui调整

perf: 懒加载和动态加载优化

perf: 去除console,

perf: 图片cdn

feat: 图片地址

perf: 登录顺序

feat: 流优化
This commit is contained in:
Archer
2023-03-09 20:44:13 +08:00
parent 2390823282
commit 17364e9da3
47 changed files with 1384 additions and 852 deletions

View File

@@ -25,11 +25,9 @@ interface CreateFormType {
}
const CreateModel = ({
isOpen,
setCreateModelOpen,
onSuccess
}: {
isOpen: boolean;
setCreateModelOpen: Dispatch<boolean>;
onSuccess: Dispatch<ModelType>;
}) => {
@@ -72,7 +70,7 @@ const CreateModel = ({
return (
<>
<Modal isOpen={isOpen} onClose={() => setCreateModelOpen(false)}>
<Modal isOpen={true} onClose={() => setCreateModelOpen(false)}>
<ModalOverlay />
<ModalContent>
<ModalHeader></ModalHeader>

View File

@@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import React, { useCallback, useEffect, useRef } from 'react';
import { Grid, Box, Card, Flex, Button, FormControl, Input, Textarea } from '@chakra-ui/react';
import type { ModelType } from '@/types/model';
import { useForm } from 'react-hook-form';
@@ -7,17 +7,17 @@ import { putModelById } from '@/api/model';
import { useScreen } from '@/hooks/useScreen';
import { useGlobalStore } from '@/store/global';
const ModelEditForm = ({ model }: { model: ModelType }) => {
const ModelEditForm = ({ model }: { model?: ModelType }) => {
const isInit = useRef(false);
const {
register,
handleSubmit,
reset,
formState: { errors }
} = useForm<ModelType>({
defaultValues: model
});
} = useForm<ModelType>();
const { setLoading } = useGlobalStore();
const { toast } = useToast();
const { isPc } = useScreen();
const { media } = useScreen();
const onclickSave = useCallback(
async (data: ModelType) => {
@@ -34,7 +34,7 @@ const ModelEditForm = ({ model }: { model: ModelType }) => {
status: 'success'
});
} catch (err) {
console.log(err);
console.error(err);
toast({
title: err as string,
status: 'success'
@@ -61,8 +61,16 @@ const ModelEditForm = ({ model }: { model: ModelType }) => {
});
}, [errors, toast]);
/* model 只会改变一次 */
useEffect(() => {
if (model && !isInit.current) {
reset(model);
isInit.current = true;
}
}, [model, reset]);
return (
<Grid gridTemplateColumns={isPc ? '1fr 1fr' : '1fr'} gridGap={5}>
<Grid gridTemplateColumns={media('1fr 1fr', '1fr')} gridGap={5}>
<Card p={4}>
<Flex justifyContent={'space-between'} alignItems={'center'}>
<Box fontWeight={'bold'} fontSize={'lg'}>
@@ -83,7 +91,7 @@ const ModelEditForm = ({ model }: { model: ModelType }) => {
<FormControl mt={5}>
<Flex alignItems={'center'}>
<Box flex={'0 0 80px'}>:</Box>
<Box>{model.service.modelName}</Box>
<Box>{model?.service.modelName}</Box>
</Flex>
</FormControl>
<FormControl mt={5}>

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useCallback, useState } from 'react';
import { Box, Card, TableContainer, Table, Thead, Tbody, Tr, Th, Td } from '@chakra-ui/react';
import { Box, TableContainer, Table, Thead, Tbody, Tr, Th, Td } from '@chakra-ui/react';
import { ModelType } from '@/types/model';
import { getModelTrainings } from '@/api/model';
import type { TrainingItemType } from '@/types/training';
@@ -29,7 +29,7 @@ const Training = ({ model }: { model: ModelType }) => {
const res = await getModelTrainings(id);
setRecords(res);
} catch (error) {
console.log(error);
console.error(error);
}
}, []);
@@ -38,7 +38,7 @@ const Training = ({ model }: { model: ModelType }) => {
}, [loadTrainingRecords, model]);
return (
<Card p={4} h={'100%'}>
<>
<Box fontWeight={'bold'} fontSize={'lg'}>
: {model.trainingTimes}
</Box>
@@ -63,7 +63,7 @@ const Training = ({ model }: { model: ModelType }) => {
</Tbody>
</Table>
</TableContainer>
</Card>
</>
);
};

View File

@@ -11,12 +11,14 @@ import { useGlobalStore } from '@/store/global';
import { useScreen } from '@/hooks/useScreen';
import ModelEditForm from './components/ModelEditForm';
import Icon from '@/components/Icon';
import Training from './components/Training';
import dynamic from 'next/dynamic';
const Training = dynamic(() => import('./components/Training'));
const ModelDetail = () => {
const { toast } = useToast();
const router = useRouter();
const { isPc } = useScreen();
const { isPc, media } = useScreen();
const { setLoading } = useGlobalStore();
const { openConfirm, ConfirmChild } = useConfirm({
content: '确认删除该模型?'
@@ -39,10 +41,8 @@ const ModelDetail = () => {
const res = await getModelById(modelId as string);
res.security.expiredTime /= 60 * 60 * 1000;
setModel(res);
console.log(res);
} catch (err) {
console.log(err);
console.error(err);
}
setLoading(false);
}, [modelId, setLoading]);
@@ -63,7 +63,7 @@ const ModelDetail = () => {
});
router.replace('/model/list');
} catch (err) {
console.log(err);
console.error(err);
}
setLoading(false);
}, [setLoading, model, router, toast]);
@@ -77,7 +77,7 @@ const ModelDetail = () => {
router.push(`/chat?chatId=${chatId}`);
} catch (err) {
console.log(err);
console.error(err);
}
setLoading(false);
}, [setLoading, model, router]);
@@ -105,7 +105,7 @@ const ModelDetail = () => {
title: typeof err === 'string' ? err : '文件格式错误',
status: 'error'
});
console.log(err);
console.error(err);
}
setLoading(false);
},
@@ -121,121 +121,121 @@ const ModelDetail = () => {
await putModelTrainingStatus(model._id);
loadModel();
} catch (error) {
console.log(error);
console.error(error);
}
setLoading(false);
}, [setLoading, loadModel, model]);
return (
<>
{!!model && (
<>
{/* 头部 */}
<Card px={6} py={3}>
{isPc ? (
<Flex alignItems={'center'}>
<Box fontSize={'xl'} fontWeight={'bold'}>
{model.name}
</Box>
<Tag
ml={2}
variant="solid"
colorScheme={formatModelStatus[model.status].colorTheme}
cursor={model.status === ModelStatusEnum.training ? 'pointer' : 'default'}
onClick={handleClickUpdateStatus}
>
{/* 头部 */}
<Card px={6} py={3}>
{isPc ? (
<Flex alignItems={'center'}>
<Box fontSize={'xl'} fontWeight={'bold'}>
{model?.name || '模型'}
</Box>
{!!model && (
<Tag
ml={2}
variant="solid"
colorScheme={formatModelStatus[model.status].colorTheme}
cursor={model.status === ModelStatusEnum.training ? 'pointer' : 'default'}
onClick={handleClickUpdateStatus}
>
{formatModelStatus[model.status].text}
</Tag>
)}
<Box flex={1} />
<Button variant={'outline'} onClick={handlePreviewChat}>
</Button>
</Flex>
) : (
<>
<Flex alignItems={'center'}>
<Box as={'h3'} fontSize={'xl'} fontWeight={'bold'} flex={1}>
{model?.name || '模型'}
</Box>
{!!model && (
<Tag ml={2} colorScheme={formatModelStatus[model.status].colorTheme}>
{formatModelStatus[model.status].text}
</Tag>
<Box flex={1} />
<Button variant={'outline'} onClick={handlePreviewChat}>
</Button>
</Flex>
) : (
<>
<Flex alignItems={'center'}>
<Box as={'h3'} fontSize={'xl'} fontWeight={'bold'} flex={1}>
{model.name}
</Box>
<Tag ml={2} colorScheme={formatModelStatus[model.status].colorTheme}>
{formatModelStatus[model.status].text}
</Tag>
</Flex>
<Box mt={4} textAlign={'right'}>
<Button variant={'outline'} onClick={handlePreviewChat}>
</Button>
</Box>
</>
)}
</Card>
{/* 基本信息编辑 */}
<Box mt={5}>
<ModelEditForm model={model} />
)}
</Flex>
<Box mt={4} textAlign={'right'}>
<Button variant={'outline'} onClick={handlePreviewChat}>
</Button>
</Box>
</>
)}
</Card>
{/* 基本信息编辑 */}
<Box mt={5}>
<ModelEditForm model={model} />
</Box>
{/* 其他配置 */}
<Grid mt={5} gridTemplateColumns={media('1fr 1fr', '1fr')} gridGap={5}>
<Card p={4}>{!!model && <Training model={model} />}</Card>
<Card p={4}>
<Box fontWeight={'bold'} fontSize={'lg'}>
</Box>
{/* 其他配置 */}
<Grid mt={5} gridTemplateColumns={isPc ? '1fr 1fr' : '1fr'} gridGap={5}>
<Training model={model} />
<Card h={'100%'} p={4}>
<Box fontWeight={'bold'} fontSize={'lg'}>
</Box>
<Flex mt={5} alignItems={'center'}>
<Box flex={'0 0 80px'}>:</Box>
<Button
size={'sm'}
onClick={() => {
SelectFileDom.current?.click();
}}
title={!canTrain ? '' : '模型不支持微调'}
isDisabled={!canTrain}
>
</Button>
<Flex
as={'a'}
href="/TrainingTemplate.jsonl"
download
ml={5}
cursor={'pointer'}
alignItems={'center'}
color={'blue.500'}
>
<Icon name={'icon-yunxiazai'} color={'#3182ce'} />
</Flex>
</Flex>
{/* 提示 */}
<Box mt={3} py={3} color={'blackAlpha.500'}>
<Box as={'li'} lineHeight={1.9}>
prompt completion
</Box>
<Box as={'li'} lineHeight={1.9}>
prompt \n\n###\n\n prompt
</Box>
<Box as={'li'} lineHeight={1.9}>
completion ###
</Box>
</Box>
<Flex mt={5} alignItems={'center'}>
<Box flex={'0 0 80px'}>:</Box>
<Button
colorScheme={'red'}
size={'sm'}
onClick={() => {
openConfirm(() => {
handleDelModel();
});
}}
>
</Button>
</Flex>
</Card>
</Grid>
</>
)}
<Flex mt={5} alignItems={'center'}>
<Box flex={'0 0 80px'}>:</Box>
<Button
size={'sm'}
onClick={() => {
SelectFileDom.current?.click();
}}
title={!canTrain ? '' : '模型不支持微调'}
isDisabled={!canTrain}
>
</Button>
<Flex
as={'a'}
href="/TrainingTemplate.jsonl"
download
ml={5}
cursor={'pointer'}
alignItems={'center'}
color={'blue.500'}
>
<Icon name={'icon-yunxiazai'} color={'#3182ce'} />
</Flex>
</Flex>
{/* 提示 */}
<Box mt={3} py={3} color={'blackAlpha.500'}>
<Box as={'li'} lineHeight={1.9}>
prompt completion
</Box>
<Box as={'li'} lineHeight={1.9}>
prompt \n\n###\n\n prompt
</Box>
<Box as={'li'} lineHeight={1.9}>
completion ###
</Box>
</Box>
<Flex mt={5} alignItems={'center'}>
<Box flex={'0 0 80px'}>:</Box>
<Button
colorScheme={'red'}
size={'sm'}
onClick={() => {
openConfirm(() => {
handleDelModel();
});
}}
>
</Button>
</Flex>
</Card>
</Grid>
<Box position={'absolute'} w={0} h={0} overflow={'hidden'}>
<input ref={SelectFileDom} type="file" accept=".jsonl" onChange={startTraining} />
</Box>

View File

@@ -1,36 +1,32 @@
import React, { useState, useEffect, useCallback } from 'react';
import React, { useState, useCallback } from 'react';
import { Box, Button, Flex, Card } from '@chakra-ui/react';
import { getMyModels } from '@/api/model';
import { getChatSiteId } from '@/api/chat';
import { ModelType } from '@/types/model';
import CreateModel from './components/CreateModel';
import { useRouter } from 'next/router';
import ModelTable from './components/ModelTable';
import ModelPhoneList from './components/ModelPhoneList';
import { useScreen } from '@/hooks/useScreen';
import { useGlobalStore } from '@/store/global';
import { useQuery } from '@tanstack/react-query';
import { useLoading } from '@/hooks/useLoading';
import dynamic from 'next/dynamic';
const CreateModel = dynamic(() => import('./components/CreateModel'));
const ModelList = () => {
const { isPc } = useScreen();
const router = useRouter();
const [models, setModels] = useState<ModelType[]>([]);
const [openCreateModel, setOpenCreateModel] = useState(false);
const { setLoading } = useGlobalStore();
const { Loading, setIsLoading } = useLoading();
/* 加载模型 */
const loadModels = useCallback(async () => {
setLoading(true);
try {
const res = await getMyModels();
const { isLoading } = useQuery(['loadModels'], () => getMyModels(), {
onSuccess(res) {
if (!res) return;
setModels(res);
} catch (err) {
console.log(err);
}
setLoading(false);
}, [setLoading]);
useEffect(() => {
loadModels();
}, [loadModels]);
});
/* 创建成功回调 */
const createModelSuccess = useCallback((data: ModelType) => {
@@ -40,7 +36,7 @@ const ModelList = () => {
/* 点前往聊天预览页 */
const handlePreviewChat = useCallback(
async (modelId: string) => {
setLoading(true);
setIsLoading(true);
try {
const chatId = await getChatSiteId(modelId);
@@ -48,11 +44,11 @@ const ModelList = () => {
shallow: true
});
} catch (err) {
console.log(err);
console.error(err);
}
setLoading(false);
setIsLoading(false);
},
[router, setLoading]
[router, setIsLoading]
);
return (
@@ -78,11 +74,11 @@ const ModelList = () => {
)}
</Box>
{/* 创建弹窗 */}
<CreateModel
isOpen={openCreateModel}
setCreateModelOpen={setOpenCreateModel}
onSuccess={createModelSuccess}
/>
{openCreateModel && (
<CreateModel setCreateModelOpen={setOpenCreateModel} onSuccess={createModelSuccess} />
)}
<Loading loading={isLoading} />
</Box>
);
};