mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 21:13:50 +00:00
perf: my models
This commit is contained in:
@@ -1,76 +0,0 @@
|
||||
import React, { useRef, useEffect, useMemo } from 'react';
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { throttle } from 'lodash';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
|
||||
interface Props extends BoxProps {
|
||||
nextPage: () => void;
|
||||
isLoadAll: boolean;
|
||||
requesting: boolean;
|
||||
children: React.ReactNode;
|
||||
initRequesting?: boolean;
|
||||
}
|
||||
|
||||
const ScrollData = ({
|
||||
children,
|
||||
nextPage,
|
||||
isLoadAll,
|
||||
requesting,
|
||||
initRequesting,
|
||||
...props
|
||||
}: Props) => {
|
||||
const { Loading } = useLoading({ defaultLoading: true });
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
const loadText = useMemo(() => {
|
||||
if (requesting) return '请求中……';
|
||||
if (isLoadAll) return '已加载全部';
|
||||
return '点击加载更多';
|
||||
}, [isLoadAll, requesting]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!elementRef.current) return;
|
||||
|
||||
const scrolling = throttle((e: Event) => {
|
||||
const element = e.target as HTMLDivElement;
|
||||
if (!element) return;
|
||||
// 当前滚动位置
|
||||
const scrollTop = element.scrollTop;
|
||||
// 可视高度
|
||||
const clientHeight = element.clientHeight;
|
||||
// 内容总高度
|
||||
const scrollHeight = element.scrollHeight;
|
||||
// 判断是否滚动到底部
|
||||
if (scrollTop + clientHeight + 100 >= scrollHeight) {
|
||||
nextPage();
|
||||
}
|
||||
}, 100);
|
||||
elementRef.current.addEventListener('scroll', scrolling);
|
||||
return () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
elementRef.current?.removeEventListener('scroll', scrolling);
|
||||
};
|
||||
}, [elementRef, nextPage]);
|
||||
|
||||
return (
|
||||
<Box {...props} ref={elementRef} overflowY={'auto'} position={'relative'}>
|
||||
{children}
|
||||
<Box
|
||||
mt={2}
|
||||
fontSize={'xs'}
|
||||
color={'blackAlpha.500'}
|
||||
textAlign={'center'}
|
||||
cursor={loadText === '点击加载更多' ? 'pointer' : 'default'}
|
||||
onClick={() => {
|
||||
if (loadText !== '点击加载更多') return;
|
||||
nextPage();
|
||||
}}
|
||||
>
|
||||
{loadText}
|
||||
</Box>
|
||||
{initRequesting && <Loading fixed={false} />}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScrollData;
|
@@ -1,21 +1,27 @@
|
||||
import { useState, useCallback, useMemo, useEffect } from 'react';
|
||||
import { useRef, useState, useCallback, useLayoutEffect, useMemo, useEffect } from 'react';
|
||||
import type { PagingData } from '../types/index';
|
||||
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 { throttle } from 'lodash';
|
||||
|
||||
const thresholdVal = 100;
|
||||
|
||||
export const usePagination = <T = any,>({
|
||||
api,
|
||||
pageSize = 10,
|
||||
params = {},
|
||||
defaultRequest = true
|
||||
defaultRequest = true,
|
||||
type = 'button'
|
||||
}: {
|
||||
api: (data: any) => any;
|
||||
pageSize?: number;
|
||||
params?: Record<string, any>;
|
||||
defaultRequest?: boolean;
|
||||
type?: 'button' | 'scroll';
|
||||
}) => {
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
const { toast } = useToast();
|
||||
const [pageNum, setPageNum] = useState(1);
|
||||
const [total, setTotal] = useState(0);
|
||||
@@ -108,6 +114,60 @@ export const usePagination = <T = any,>({
|
||||
);
|
||||
}, [isLoading, maxPage, mutate, pageNum]);
|
||||
|
||||
const ScrollData = useCallback(
|
||||
({ children, ...props }: { children: React.ReactNode }) => {
|
||||
const loadText = useMemo(() => {
|
||||
if (isLoading) return '请求中……';
|
||||
if (total <= data.length) return '已加载全部';
|
||||
return '点击加载更多';
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box {...props} ref={elementRef} overflow={'overlay'}>
|
||||
{children}
|
||||
<Box
|
||||
mt={2}
|
||||
fontSize={'xs'}
|
||||
color={'blackAlpha.500'}
|
||||
textAlign={'center'}
|
||||
cursor={loadText === '点击加载更多' ? 'pointer' : 'default'}
|
||||
onClick={() => {
|
||||
if (loadText !== '点击加载更多') return;
|
||||
mutate(pageNum + 1);
|
||||
}}
|
||||
>
|
||||
{loadText}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
},
|
||||
[data.length, isLoading, mutate, pageNum, total]
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!elementRef.current || type !== 'scroll') return;
|
||||
|
||||
const scrolling = throttle((e: Event) => {
|
||||
const element = e.target as HTMLDivElement;
|
||||
if (!element) return;
|
||||
// 当前滚动位置
|
||||
const scrollTop = element.scrollTop;
|
||||
// 可视高度
|
||||
const clientHeight = element.clientHeight;
|
||||
// 内容总高度
|
||||
const scrollHeight = element.scrollHeight;
|
||||
// 判断是否滚动到底部
|
||||
if (scrollTop + clientHeight + thresholdVal >= scrollHeight) {
|
||||
mutate(pageNum + 1);
|
||||
}
|
||||
}, 100);
|
||||
elementRef.current.addEventListener('scroll', scrolling);
|
||||
return () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
elementRef.current?.removeEventListener('scroll', scrolling);
|
||||
};
|
||||
}, [elementRef, mutate, pageNum, type]);
|
||||
|
||||
useEffect(() => {
|
||||
defaultRequest && mutate(1);
|
||||
}, []);
|
||||
@@ -119,6 +179,7 @@ export const usePagination = <T = any,>({
|
||||
data,
|
||||
isLoading,
|
||||
Pagination,
|
||||
ScrollData,
|
||||
getData: mutate
|
||||
};
|
||||
};
|
||||
|
@@ -24,8 +24,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
const authCount = await Model.countDocuments({
|
||||
userId
|
||||
});
|
||||
if (authCount >= 30) {
|
||||
throw new Error('上限 30 个应用');
|
||||
if (authCount >= 50) {
|
||||
throw new Error('上限 50 个应用');
|
||||
}
|
||||
|
||||
// 创建模型
|
||||
|
@@ -18,14 +18,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
{
|
||||
userId
|
||||
},
|
||||
'_id avatar name chat.systemPrompt'
|
||||
'_id avatar name intro'
|
||||
).sort({
|
||||
updateTime: -1
|
||||
}),
|
||||
Collection.find({ userId })
|
||||
.populate({
|
||||
path: 'modelId',
|
||||
select: '_id avatar name chat.systemPrompt',
|
||||
select: '_id avatar name intro',
|
||||
match: { 'share.isShare': true }
|
||||
})
|
||||
.then((res) => res.filter((item) => item.modelId))
|
||||
@@ -37,14 +37,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
_id: item._id,
|
||||
name: item.name,
|
||||
avatar: item.avatar,
|
||||
systemPrompt: item.chat.systemPrompt
|
||||
intro: item.intro
|
||||
})),
|
||||
myCollectionModels: myCollections
|
||||
.map((item: any) => ({
|
||||
_id: item.modelId?._id,
|
||||
name: item.modelId?.name,
|
||||
avatar: item.modelId?.avatar,
|
||||
systemPrompt: item.modelId?.chat.systemPrompt
|
||||
intro: item.modelId?.intro
|
||||
}))
|
||||
.filter((item) => !myModels.find((model) => String(model._id) === String(item._id))) // 去重
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { Box, Flex, Input, IconButton, Tooltip, Tab, useTheme } from '@chakra-ui/react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { Box, Flex, Input, IconButton, Tooltip, useTheme } from '@chakra-ui/react';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import { useRouter } from 'next/router';
|
||||
import MyIcon from '@/components/Icon';
|
||||
@@ -55,14 +55,12 @@ const ModelList = ({ modelId }: { modelId: string }) => {
|
||||
const currentModels = useMemo(() => {
|
||||
const map = {
|
||||
[MyModelsTypeEnum.my]: {
|
||||
list: myModels.filter((item) =>
|
||||
new RegExp(searchText, 'ig').test(item.name + item.systemPrompt)
|
||||
),
|
||||
list: myModels.filter((item) => new RegExp(searchText, 'ig').test(item.name + item.intro)),
|
||||
emptyText: '还没有 AI 应用~\n快来创建一个吧'
|
||||
},
|
||||
[MyModelsTypeEnum.collection]: {
|
||||
list: myCollectionModels.filter((item) =>
|
||||
new RegExp(searchText, 'ig').test(item.name + item.systemPrompt)
|
||||
new RegExp(searchText, 'ig').test(item.name + item.intro)
|
||||
),
|
||||
emptyText: '收藏的 AI 应用为空~\n快去市场找一个吧'
|
||||
}
|
||||
|
@@ -24,7 +24,8 @@ const Settings = ({ modelId }: { modelId: string }) => {
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
const { Loading, setIsLoading } = useLoading();
|
||||
const { userInfo, modelDetail, loadModelDetail, refreshModel, setLastModelId } = useUserStore();
|
||||
const { userInfo, modelDetail, myModels, loadModelDetail, refreshModel, setLastModelId } =
|
||||
useUserStore();
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
fileType: '.jpg,.png',
|
||||
multiple: false
|
||||
@@ -119,7 +120,7 @@ const Settings = ({ modelId }: { modelId: string }) => {
|
||||
status: 'success'
|
||||
});
|
||||
refreshModel.removeModelDetail(modelDetail._id);
|
||||
router.replace('/model');
|
||||
router.replace(`/model?modelId=${myModels[1]?._id}`);
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: err?.message || '删除失败',
|
||||
@@ -127,7 +128,7 @@ const Settings = ({ modelId }: { modelId: string }) => {
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
}, [modelDetail, setIsLoading, toast, refreshModel, router]);
|
||||
}, [modelDetail, setIsLoading, toast, refreshModel, router, myModels]);
|
||||
|
||||
const onSelectFile = useCallback(
|
||||
async (e: File[]) => {
|
||||
|
2
client/src/types/model.d.ts
vendored
2
client/src/types/model.d.ts
vendored
@@ -5,7 +5,7 @@ export type ModelListItemType = {
|
||||
_id: string;
|
||||
name: string;
|
||||
avatar: string;
|
||||
systemPrompt: string;
|
||||
intro: string;
|
||||
};
|
||||
|
||||
export interface ModelUpdateParams {
|
||||
|
Reference in New Issue
Block a user