perf: page ui (#5469)

* perf: page ui

* fix: icon

* limit chat items

* limit chat items
This commit is contained in:
Archer
2025-08-15 17:56:49 +08:00
committed by GitHub
parent 76dc23c2e4
commit ce36230285
36 changed files with 619 additions and 322 deletions
@@ -60,9 +60,10 @@ const BillTable = () => {
isLoading,
Pagination,
getData,
total
total,
pageSize
} = usePagination(getBills, {
pageSize: 20,
defaultPageSize: 20,
params: {
type: billType
},
@@ -95,8 +96,8 @@ const BillTable = () => {
}, [billType]);
return (
<MyBox isLoading={isLoading} position={'relative'} h={'100%'} minH={'50vh'}>
<TableContainer>
<MyBox isLoading={isLoading} display={'flex'} flexDir={'column'} h={'100%'}>
<TableContainer flex={'1 0 0'} h={0} overflowY={'auto'}>
<Table>
<Thead>
<Tr>
@@ -147,11 +148,6 @@ const BillTable = () => {
))}
</Tbody>
</Table>
{total >= 20 && (
<Flex mt={3} justifyContent={'flex-end'}>
<Pagination />
</Flex>
)}
{!isLoading && bills.length === 0 && (
<Flex
mt={'20vh'}
@@ -167,6 +163,11 @@ const BillTable = () => {
)}
</TableContainer>
{total >= pageSize && (
<Flex mt={3} justifyContent={'center'}>
<Pagination />
</Flex>
)}
{!!billDetail && (
<BillDetailModal bill={billDetail} onClose={() => setBillDetail(undefined)} />
)}
@@ -32,14 +32,15 @@ const InvoiceTable = () => {
data: invoices,
isLoading,
Pagination,
total
total,
pageSize
} = usePagination(getInvoiceRecords, {
pageSize: 20
defaultPageSize: 10
});
return (
<MyBox isLoading={isLoading} position={'relative'} h={'100%'} overflow={'overlay'}>
<TableContainer minH={'50vh'}>
<MyBox isLoading={isLoading} position={'relative'} minH={'50vh'} overflow={'overlay'}>
<TableContainer>
<Table>
<Thead h="3rem">
<Tr>
@@ -101,11 +102,6 @@ const InvoiceTable = () => {
))}
</Tbody>
</Table>
{total >= 20 && (
<Flex mt={3} justifyContent={'flex-end'}>
<Pagination />
</Flex>
)}
{!isLoading && invoices.length === 0 && (
<Flex
mt={'20vh'}
@@ -120,6 +116,11 @@ const InvoiceTable = () => {
</Flex>
)}
</TableContainer>
{total >= pageSize && (
<Flex mt={3} justifyContent={'center'}>
<Pagination />
</Flex>
)}
{!!invoiceDetailData && (
<InvoiceDetailModal invoice={invoiceDetailData} onClose={() => setInvoiceDetailData('')} />
)}
@@ -68,7 +68,7 @@ const UsageTableList = ({
Pagination,
total
} = usePagination(getUserUsages, {
pageSize: 20,
defaultPageSize: 20,
params: requestParams,
refreshDeps: [requestParams]
});
@@ -111,7 +111,7 @@ const UsageTableList = ({
);
return (
<>
<MyBox display={'flex'} flexDirection={'column'} h={'100%'} isLoading={isLoading}>
<Box>{Tabs}</Box>
<Flex mt={4} w={'100%'}>
<Box>{Selectors}</Box>
@@ -123,54 +123,46 @@ const UsageTableList = ({
onConfirm={exportUsage}
/>
</Flex>
<MyBox mt={3} flex={'1 0 0'} h={0} isLoading={isLoading}>
<Box h={'100%'} overflow={'auto'}>
<TableContainer>
<Table>
<Thead>
<Tr>
<Th>{t('common:user.Time')}</Th>
<Th>{t('account_usage:member')}</Th>
<Th>{t('account_usage:user_type')}</Th>
<Th>{t('account_usage:project_name')}</Th>
<Th>{t('account_usage:total_points')}</Th>
<Th></Th>
</Tr>
</Thead>
<Tbody fontSize={'sm'}>
{usages.map((item) => (
<Tr key={item.id}>
<Td>{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}</Td>
<Td>
<Flex alignItems={'center'} color={'myGray.500'}>
<Avatar src={item.sourceMember.avatar} w={'20px'} mr={1} rounded={'full'} />
{item.sourceMember.name}
</Flex>
</Td>
<Td>{t(UsageSourceMap[item.source]?.label as any) || '-'}</Td>
<Td className="textEllipsis" maxW={'400px'} title={t(item.appName as any)}>
{t(item.appName as any) || '-'}
</Td>
<Td>{formatNumber(item.totalPoints) || 0}</Td>
<Td>
<Button
size={'sm'}
variant={'whitePrimary'}
onClick={() => setUsageDetail(item)}
>
{t('account_usage:details')}
</Button>
</Td>
</Tr>
))}
</Tbody>
</Table>
{!isLoading && usages.length === 0 && (
<EmptyTip text={t('account_usage:no_usage_records')}></EmptyTip>
)}
</TableContainer>
</Box>
</MyBox>
<TableContainer mt={3} flex={'1 0 0'} h={0} overflowY={'auto'}>
<Table>
<Thead>
<Tr>
<Th>{t('common:user.Time')}</Th>
<Th>{t('account_usage:member')}</Th>
<Th>{t('account_usage:user_type')}</Th>
<Th>{t('account_usage:project_name')}</Th>
<Th>{t('account_usage:total_points')}</Th>
<Th></Th>
</Tr>
</Thead>
<Tbody fontSize={'sm'}>
{usages.map((item) => (
<Tr key={item.id}>
<Td>{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}</Td>
<Td>
<Flex alignItems={'center'} color={'myGray.500'}>
<Avatar src={item.sourceMember.avatar} w={'20px'} mr={1} rounded={'full'} />
{item.sourceMember.name}
</Flex>
</Td>
<Td>{t(UsageSourceMap[item.source]?.label as any) || '-'}</Td>
<Td className="textEllipsis" maxW={'400px'} title={t(item.appName as any)}>
{t(item.appName as any) || '-'}
</Td>
<Td>{formatNumber(item.totalPoints) || 0}</Td>
<Td>
<Button size={'sm'} variant={'whitePrimary'} onClick={() => setUsageDetail(item)}>
{t('account_usage:details')}
</Button>
</Td>
</Tr>
))}
</Tbody>
</Table>
{!isLoading && usages.length === 0 && (
<EmptyTip text={t('account_usage:no_usage_records')}></EmptyTip>
)}
</TableContainer>
<Flex mt={3} justifyContent={'center'}>
<Pagination />
</Flex>
@@ -178,7 +170,7 @@ const UsageTableList = ({
{!!usageDetail && (
<UsageDetail usage={usageDetail} onClose={() => setUsageDetail(undefined)} />
)}
</>
</MyBox>
);
};
@@ -42,6 +42,7 @@ import {
} from '@fastgpt/global/core/app/logs/constants';
import { formatDateByTimespan } from '@fastgpt/global/core/app/logs/utils';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyBox from '@fastgpt/web/components/common/MyBox';
export type HeaderControlProps = {
appId: string;
@@ -149,7 +150,7 @@ const LogChart = ({
const [offset, setOffset] = useState<string>(offsetOptions[0].value);
const { data: chartData } = useRequest2(
const { data: chartData, loading } = useRequest2(
async () => {
return getAppChartData({
appId,
@@ -331,7 +332,7 @@ const LogChart = ({
]);
return (
<Flex flexDir={'column'} h={'full'}>
<MyBox isLoading={loading} display={'flex'} flexDir={'column'} h={'full'}>
<HeaderControl
appId={appId}
chatSources={chatSources}
@@ -751,7 +752,7 @@ const LogChart = ({
</AccordionItem>
</Accordion>
</Flex>
</Flex>
</MyBox>
);
};
@@ -49,6 +49,7 @@ import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import dynamic from 'next/dynamic';
import type { HeaderControlProps } from './LogChart';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyBox from '@fastgpt/web/components/common/MyBox';
const DetailLogsModal = dynamic(() => import('./DetailLogsModal'));
@@ -196,9 +197,10 @@ const LogTable = ({
Pagination,
getData,
pageNum,
total
total,
pageSize
} = usePagination(getAppChatLogs, {
pageSize: 20,
defaultPageSize: 20,
params,
refreshDeps: [params]
});
@@ -338,7 +340,7 @@ const LogTable = ({
});
return (
<Flex flexDir={'column'} h={'full'} overflow={'auto'} px={[4, 8]}>
<MyBox isLoading={isLoading} display={'flex'} flexDir={'column'} h={'full'} px={[4, 8]}>
<Flex alignItems={'center'} gap={3} flexWrap={'wrap'}>
{showSourceSelector && (
<Flex>
@@ -465,7 +467,7 @@ const LogTable = ({
/>
</Flex>
<TableContainer mt={[2, 4]} flex={['', '1 0 0']} h={['auto', 0]} overflowY={'auto'}>
<TableContainer mt={[2, 4]} flex={'1 0 0'} overflowY={'auto'}>
<Table variant={'simple'} fontSize={'sm'}>
<Thead>
<Tr>
@@ -496,9 +498,11 @@ const LogTable = ({
{logs.length === 0 && !isLoading && <EmptyTip text={t('app:logs_empty')}></EmptyTip>}
</TableContainer>
<HStack w={'100%'} mt={3} justifyContent={'center'}>
<Pagination />
</HStack>
{total >= pageSize && (
<Flex mt={3} justifyContent={'center'}>
<Pagination />
</Flex>
)}
{!!detailLogsId && (
<DetailLogsModal
@@ -510,7 +514,7 @@ const LogTable = ({
}}
/>
)}
</Flex>
</MyBox>
);
};
@@ -149,11 +149,12 @@ const Header = () => {
p={0.5}
borderRadius={'sm'}
>
<MyIcon
name={'common/leftArrowLight'}
w={6}
cursor={'pointer'}
onClick={isSaved ? onBack : onOpenBackConfirm}
<IconButton
icon={<MyIcon name={'common/leftArrowLight'} color={'myGray.600'} w={'0.8rem'} />}
aria-label={''}
size={'xs'}
w={'1rem'}
variant={'ghost'}
/>
</Box>
@@ -213,8 +214,6 @@ const Header = () => {
isPc,
currentTab,
isSaved,
onBack,
onOpenBackConfirm,
isV2Workflow,
t,
showHistoryModal,
@@ -149,11 +149,12 @@ const Header = () => {
p={0.5}
borderRadius={'sm'}
>
<MyIcon
name={'common/leftArrowLight'}
w={6}
cursor={'pointer'}
onClick={isSaved ? onBack : onOpenBackConfirm}
<IconButton
icon={<MyIcon name={'common/leftArrowLight'} color={'myGray.600'} w={'0.8rem'} />}
aria-label={''}
size={'xs'}
w={'1rem'}
variant={'ghost'}
/>
</Box>
@@ -213,8 +214,6 @@ const Header = () => {
isPc,
currentTab,
isSaved,
onBack,
onOpenBackConfirm,
isV2Workflow,
t,
showHistoryModal,
@@ -97,9 +97,11 @@ const EvaluationDetailModal = ({
const {
data: evalItemsList,
Pagination,
pageSize,
total,
getData: fetchData
} = usePagination(getEvalItemsList, {
pageSize: 10,
defaultPageSize: 20,
params: {
evalId: evalDetail._id
},
@@ -440,9 +442,11 @@ const EvaluationDetailModal = ({
})}
</Box>
<Box px={6} py={3}>
<Pagination />
</Box>
{total >= pageSize && (
<Flex my={3} justifyContent={'center'}>
<Pagination />
</Flex>
)}
</Box>
</Flex>
{evalItem ? (
@@ -128,7 +128,7 @@ const CollectionPageContextProvider = ({ children }: { children: ReactNode }) =>
pageNum,
pageSize
} = usePagination(getDatasetCollections, {
pageSize: 20,
defaultPageSize: 20,
params: {
datasetId,
parentId,
@@ -10,14 +10,16 @@ import {
Td,
Tbody,
MenuButton,
Switch
Switch,
Checkbox,
HStack,
Button
} from '@chakra-ui/react';
import {
delDatasetCollectionById,
putDatasetCollectionById,
postLinkCollectionSync
} from '@/web/core/dataset/api';
import { useQuery } from '@tanstack/react-query';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
@@ -49,6 +51,7 @@ import { useFolderDrag } from '@/components/common/folder/useFolderDrag';
import TagsPopOver from './TagsPopOver';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import TrainingStates from './TrainingStates';
import { useTableMultipleSelect } from '@fastgpt/web/hooks/useTableMultipleSelect';
const Header = dynamic(() => import('./Header'));
const EmptyCollectionTip = dynamic(() => import('./EmptyCollectionTip'));
@@ -103,6 +106,19 @@ const CollectionCard = () => {
[collections, t]
);
const {
selectedItems,
toggleSelect,
isSelected,
setSelectedItems,
FloatingActionBar,
isSelecteAll,
selectAllTrigger
} = useTableMultipleSelect({
list: formatCollections,
getItemId: (e) => e._id
});
const [moveCollectionData, setMoveCollectionData] = useState<{ collectionId: string }>();
const { onOpenModal: onOpenEditTitleModal, EditModal: EditTitleModal } = useEditTitle({
@@ -123,9 +139,9 @@ const CollectionCard = () => {
type: 'delete'
});
const { runAsync: onDelCollection } = useRequest2(
(collectionId: string) => {
(collectionIds: string[]) => {
return delDatasetCollectionById({
id: collectionId
collectionIds
});
},
{
@@ -156,18 +172,17 @@ const CollectionCard = () => {
[formatCollections]
);
useQuery(
['refreshCollection'],
() => {
useRequest2(
async () => {
if (!hasTrainingData && datasetDetail.status === DatasetStatusEnum.active) return;
getData(pageNum);
if (datasetDetail.status !== DatasetStatusEnum.active) {
loadDatasetDetail(datasetDetail._id);
}
return null;
},
{
refetchInterval: 6000,
enabled: hasTrainingData || datasetDetail.status !== DatasetStatusEnum.active
retryInterval: 6000,
refreshDeps: [hasTrainingData, datasetDetail.status]
}
);
@@ -186,21 +201,25 @@ const CollectionCard = () => {
}
});
const isLoading =
isUpdating || isSyncing || (isGetting && collections.length === 0) || isDropping;
const isLoading = isUpdating || isSyncing || isGetting || isDropping;
return (
<MyBox isLoading={isLoading} h={'100%'} py={[2, 4]}>
<MyBox isLoading={isLoading} h={'100%'} py={[2, 4]} overflow={'hidden'}>
<Flex ref={BoxRef} flexDirection={'column'} py={[1, 0]} h={'100%'} px={[2, 6]}>
{/* header */}
<Header hasTrainingData={hasTrainingData} />
{/* collection table */}
<TableContainer mt={3} overflowY={'auto'} fontSize={'sm'}>
<TableContainer mt={3} overflowY={'auto'} fontSize={'sm'} flex={'1 0 0'} h={0}>
<Table variant={'simple'} draggable={false}>
<Thead draggable={false}>
<Tr>
<Th py={4}>{t('common:Name')}</Th>
<Th py={4}>
<HStack>
<Checkbox isChecked={isSelecteAll} onChange={selectAllTrigger} />
<Box>{t('common:Name')}</Box>
</HStack>
</Th>
<Th py={4}>{t('dataset:collection.training_type')}</Th>
<Th py={4}>{t('dataset:collection_data_count')}</Th>
<Th py={4}>{t('dataset:collection.Create update time')}</Th>
@@ -241,17 +260,27 @@ const CollectionCard = () => {
}}
>
<Td minW={'150px'} maxW={['200px', '300px']} draggable py={2}>
<Flex alignItems={'center'}>
<MyIcon name={collection.icon as any} w={'1.25rem'} mr={2} />
<MyTooltip label={t('common:click_drag_tip')} shouldWrapChildren={false}>
<Box color={'myGray.900'} fontWeight={'500'} className="textEllipsis">
{collection.name}
</Box>
</MyTooltip>
</Flex>
{feConfigs?.isPlus && !!collection.tags?.length && (
<TagsPopOver currentCollection={collection} />
)}
<HStack>
<Box onClick={(e) => e.stopPropagation()}>
<Checkbox
isChecked={isSelected(collection)}
onChange={(e) => toggleSelect(collection)}
/>
</Box>
<Box>
<Flex alignItems={'center'}>
<MyIcon name={collection.icon as any} w={'1.25rem'} mr={2} />
<MyTooltip label={t('common:click_drag_tip')} shouldWrapChildren={false}>
<Box color={'myGray.900'} fontWeight={'500'} className="textEllipsis">
{collection.name}
</Box>
</MyTooltip>
</Flex>
{feConfigs?.isPlus && !!collection.tags?.length && (
<TagsPopOver currentCollection={collection} />
)}
</Box>
</HStack>
</Td>
<Td py={2}>
{collection.trainingType
@@ -394,7 +423,7 @@ const CollectionCard = () => {
type: 'danger',
onClick: () =>
openDeleteConfirm(
() => onDelCollection(collection._id),
() => onDelCollection([collection._id]),
undefined,
collection.type === DatasetCollectionTypeEnum.folder
? t('common:dataset.collections.Confirm to delete the folder')
@@ -411,13 +440,39 @@ const CollectionCard = () => {
))}
</Tbody>
</Table>
{total === 0 && <EmptyCollectionTip />}
</TableContainer>
<FloatingActionBar
Controler={
<HStack>
<Button
variant={'whiteBase'}
onClick={() =>
openDeleteConfirm(
() =>
onDelCollection(selectedItems.map((e) => e._id)).then(() =>
setSelectedItems([])
),
undefined,
t('dataset:confirm_delete_collection', {
num: selectedItems.length
})
)()
}
>
{t('dataset:batch_delete')}
</Button>
</HStack>
}
>
{total > pageSize && (
<Flex mt={2} justifyContent={'center'}>
<Flex justifyContent={'center'}>
<Pagination />
</Flex>
)}
{total === 0 && <EmptyCollectionTip />}
</TableContainer>
</FloatingActionBar>
<ConfirmDeleteModal />
<ConfirmSyncModal />