perf: my models

This commit is contained in:
archer
2023-06-19 21:05:33 +08:00
parent dd4ca27dc7
commit a02a528737
7 changed files with 78 additions and 94 deletions

View File

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

View File

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

View File

@@ -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 个应用');
}
// 创建模型

View File

@@ -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))) // 去重
}

View File

@@ -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快去市场找一个吧'
}

View File

@@ -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[]) => {

View File

@@ -5,7 +5,7 @@ export type ModelListItemType = {
_id: string;
name: string;
avatar: string;
systemPrompt: string;
intro: string;
};
export interface ModelUpdateParams {