Commit ce362302 by Archer Committed by GitHub

perf: page ui (#5469)

* perf: page ui

* fix: icon

* limit chat items

* limit chat items
parent 76dc23c2
# 背景
一个通用表格多选 hook/component, 它可以实现表格每一行数据的选择,并且在触发一次选择后,会有特殊的按键进行批量操作。
# 具体描述
当有一行被选中时,底部会出现悬浮层,可以进行批量操作(具体有哪些批量操作由外部决定)
![alt text](./image-1.png)
# 预期封装
1. 选中的值存储在 hook 里,便于判断是否触发底部悬浮层
2. 悬浮层外层 Box 在 hook 里,child 由调用组件实现
3. FastGPT/packages/web/hooks/useTableMultipleSelect.tsx 在这个文件下实现
\ No newline at end of file
...@@ -3,7 +3,6 @@ title: 'V4.12.1(进行中)' ...@@ -3,7 +3,6 @@ title: 'V4.12.1(进行中)'
description: 'FastGPT V4.12.1 更新说明' description: 'FastGPT V4.12.1 更新说明'
--- ---
## 🚀 新增内容 ## 🚀 新增内容
1. Prompt 自动生成和优化。 1. Prompt 自动生成和优化。
...@@ -14,6 +13,7 @@ description: 'FastGPT V4.12.1 更新说明' ...@@ -14,6 +13,7 @@ description: 'FastGPT V4.12.1 更新说明'
1. 工作流响应优化,主动指定响应值进入历史记录,而不是根据 key 决定。 1. 工作流响应优化,主动指定响应值进入历史记录,而不是根据 key 决定。
2. 避免工作流中,变量替换导致的死循环或深度递归风险。 2. 避免工作流中,变量替换导致的死循环或深度递归风险。
3. 对话日志导出,固定导出对话详情。 3. 对话日志导出,固定导出对话详情。
4. 分页器 UI 优化。
## 🐛 修复 ## 🐛 修复
......
...@@ -103,7 +103,7 @@ ...@@ -103,7 +103,7 @@
"document/content/docs/upgrading/4-11/4110.mdx": "2025-08-05T23:20:39+08:00", "document/content/docs/upgrading/4-11/4110.mdx": "2025-08-05T23:20:39+08:00",
"document/content/docs/upgrading/4-11/4111.mdx": "2025-08-07T22:49:09+08:00", "document/content/docs/upgrading/4-11/4111.mdx": "2025-08-07T22:49:09+08:00",
"document/content/docs/upgrading/4-12/4120.mdx": "2025-08-12T22:45:19+08:00", "document/content/docs/upgrading/4-12/4120.mdx": "2025-08-12T22:45:19+08:00",
"document/content/docs/upgrading/4-12/4121.mdx": "2025-08-15T14:27:32+08:00", "document/content/docs/upgrading/4-12/4121.mdx": "2025-08-15T17:10:04+08:00",
"document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00",
......
...@@ -52,6 +52,7 @@ export const iconPaths = { ...@@ -52,6 +52,7 @@ export const iconPaths = {
'common/errorFill': () => import('./icons/common/errorFill.svg'), 'common/errorFill': () => import('./icons/common/errorFill.svg'),
'common/file/move': () => import('./icons/common/file/move.svg'), 'common/file/move': () => import('./icons/common/file/move.svg'),
'common/fileNotFound': () => import('./icons/common/fileNotFound.svg'), 'common/fileNotFound': () => import('./icons/common/fileNotFound.svg'),
'common/first_page': () => import('./icons/common/first_page.svg'),
'common/folderFill': () => import('./icons/common/folderFill.svg'), 'common/folderFill': () => import('./icons/common/folderFill.svg'),
'common/folderImport': () => import('./icons/common/folderImport.svg'), 'common/folderImport': () => import('./icons/common/folderImport.svg'),
'common/fullScreenLight': () => import('./icons/common/fullScreenLight.svg'), 'common/fullScreenLight': () => import('./icons/common/fullScreenLight.svg'),
...@@ -67,6 +68,7 @@ export const iconPaths = { ...@@ -67,6 +68,7 @@ export const iconPaths = {
'common/language/China': () => import('./icons/common/language/China.svg'), 'common/language/China': () => import('./icons/common/language/China.svg'),
'common/language/en': () => import('./icons/common/language/en.svg'), 'common/language/en': () => import('./icons/common/language/en.svg'),
'common/language/zh': () => import('./icons/common/language/zh.svg'), 'common/language/zh': () => import('./icons/common/language/zh.svg'),
'common/latest_page': () => import('./icons/common/latest_page.svg'),
'common/layer': () => import('./icons/common/layer.svg'), 'common/layer': () => import('./icons/common/layer.svg'),
'common/leftArrowLight': () => import('./icons/common/leftArrowLight.svg'), 'common/leftArrowLight': () => import('./icons/common/leftArrowLight.svg'),
'common/line': () => import('./icons/common/line.svg'), 'common/line': () => import('./icons/common/line.svg'),
......
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 7 6" fill="none">
<path d="M6.705 5.295L4.41 3L6.705 0.705L6 0L3 3L6 6L6.705 5.295ZM0.5 0H1.5V6H0.5V0Z" />
</svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 7 6" fill="none">
<path d="M0.295044 0.705L2.59004 3L0.295044 5.295L1.00004 6L4.00004 3L1.00004 0L0.295044 0.705ZM5.50004 0H6.50004V6H5.50004V0Z" />
</svg>
\ No newline at end of file
<svg viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 8" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" <path d="M1.914 3.99987L4.389 6.47487L3.682 7.18187L0.5 3.99987L3.682 0.817871L4.389 1.52487L1.914 3.99987Z" />
d="M15.7071 5.79289C16.0976 6.18342 16.0976 6.81658 15.7071 7.20711L10.4142 12.5L15.7071 17.7929C16.0976 18.1834 16.0976 18.8166 15.7071 19.2071C15.3166 19.5976 14.6834 19.5976 14.2929 19.2071L8.29289 13.2071C7.90237 12.8166 7.90237 12.1834 8.29289 11.7929L14.2929 5.79289C14.6834 5.40237 15.3166 5.40237 15.7071 5.79289Z" />
</svg> </svg>
\ No newline at end of file
<svg t="1706532590649" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 8" fill="none">
p-id="3146"> <path d="M3.08596 3.99987L0.610962 1.52487L1.31796 0.817871L4.49996 3.99987L1.31796 7.18187L0.610962 6.47487L3.08596 3.99987Z" />
<path
d="M236.552013 926.853955a55.805997 55.805997 0 0 0 0 80.454996 59.682997 59.682997 0 0 0 82.794996 0l468.099978-455.081978a55.805997 55.805997 0 0 0 0-80.453996L319.348009 16.689999a59.682997 59.682997 0 0 0-82.794996 0 55.805997 55.805997 0 0 0 0 80.454996L663.401993 511.999975 236.624013 926.853955z"
p-id="3147"></path>
</svg> </svg>
\ No newline at end of file
import { useRef, useState, useCallback, type RefObject, type ReactNode, useMemo } from 'react'; import {
import { IconButton, Flex, Box, Input, type BoxProps } from '@chakra-ui/react'; useRef,
import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons'; useState,
useCallback,
type RefObject,
type ReactNode,
useMemo,
useEffect
} from 'react';
import type { FlexProps } from '@chakra-ui/react';
import { Flex, Box, type BoxProps } from '@chakra-ui/react';
import MyIcon from '../components/common/Icon';
import type { IconNameType } from '../components/common/Icon/type';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useToast } from './useToast'; import { useToast } from './useToast';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { import {
useBoolean, useBoolean,
useLockFn, useCreation,
useMemoizedFn, useMemoizedFn,
useRequest, useRequest,
useScroll, useScroll,
...@@ -14,13 +24,16 @@ import { ...@@ -14,13 +24,16 @@ import {
} from 'ahooks'; } from 'ahooks';
import { type PaginationProps, type PaginationResponse } from '../common/fetch/type'; import { type PaginationProps, type PaginationResponse } from '../common/fetch/type';
import MyMenu from '../components/common/MyMenu';
import { useSystem } from './useSystem';
const thresholdVal = 200; const thresholdVal = 200;
export function usePagination<DataT, ResT = {}>( export function usePagination<DataT, ResT = {}>(
api: (data: PaginationProps<DataT>) => Promise<PaginationResponse<ResT>>, api: (data: PaginationProps<DataT>) => Promise<PaginationResponse<ResT>>,
{ {
pageSize = 10, defaultPageSize = 10,
pageSizeOptions: defaultPageSizeOptions,
params, params,
defaultRequest = true, defaultRequest = true,
type = 'button', type = 'button',
...@@ -31,7 +44,8 @@ export function usePagination<DataT, ResT = {}>( ...@@ -31,7 +44,8 @@ export function usePagination<DataT, ResT = {}>(
pollingInterval, pollingInterval,
pollingWhenHidden = false pollingWhenHidden = false
}: { }: {
pageSize?: number; defaultPageSize?: number;
pageSizeOptions?: number[];
params?: DataT; params?: DataT;
defaultRequest?: boolean; defaultRequest?: boolean;
type?: 'button' | 'scroll'; type?: 'button' | 'scroll';
...@@ -45,11 +59,18 @@ export function usePagination<DataT, ResT = {}>( ...@@ -45,11 +59,18 @@ export function usePagination<DataT, ResT = {}>(
} }
) { ) {
const { toast } = useToast(); const { toast } = useToast();
const { isPc } = useSystem();
const { t } = useTranslation(); const { t } = useTranslation();
const [isLoading, { setTrue, setFalse }] = useBoolean(false); const [isLoading, { setTrue, setFalse }] = useBoolean(false);
const [pageNum, setPageNum] = useState(1); const [pageNum, setPageNum] = useState(1);
const [pageSize, setPageSize] = useState(defaultPageSize);
const pageSizeOptions = useCreation(
() => defaultPageSizeOptions || [10, 20, 50, 100],
[defaultPageSizeOptions]
);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [data, setData] = useState<ResT[]>([]); const [data, setData] = useState<ResT[]>([]);
const totalDataLength = useMemo(() => Math.max(total, data.length), [total, data.length]); const totalDataLength = useMemo(() => Math.max(total, data.length), [total, data.length]);
...@@ -119,67 +140,110 @@ export function usePagination<DataT, ResT = {}>( ...@@ -119,67 +140,110 @@ export function usePagination<DataT, ResT = {}>(
const Pagination = useCallback(() => { const Pagination = useCallback(() => {
const maxPage = Math.ceil(totalDataLength / pageSize); const maxPage = Math.ceil(totalDataLength / pageSize);
return ( const IconButton = ({
<Flex alignItems={'center'} justifyContent={'end'}> icon,
<IconButton isDisabled,
isDisabled={pageNum === 1} onClick,
icon={<ArrowBackIcon />} ...props
aria-label={'left'} }: {
size={'smSquare'} icon: IconNameType;
isLoading={isLoading} isDisabled?: boolean;
onClick={() => fetchData(pageNum - 1)} onClick: () => void;
/> } & FlexProps) => {
<Flex mx={2} alignItems={'center'}> isDisabled = isDisabled || isLoading;
<Input return (
defaultValue={pageNum} <Flex
w={'50px'} alignItems={'center'}
h={'30px'} justifyContent={'center'}
size={'xs'} borderRadius={'full'}
type={'number'} w={'24px'}
min={1} h={'24px'}
max={maxPage} cursor={'pointer'}
onBlur={(e) => { bg={'myGray.150'}
const val = +e.target.value; {...(isDisabled
if (val === pageNum) return; ? {
if (val >= maxPage) { opacity: 0.5
fetchData(maxPage);
} else if (val < 1) {
fetchData(1);
} else {
fetchData(+e.target.value);
}
}}
onKeyDown={(e) => {
// @ts-ignore
const val = +e.target.value;
if (val && e.key === 'Enter') {
if (val === pageNum) return;
if (val >= maxPage) {
fetchData(maxPage);
} else if (val < 1) {
fetchData(1);
} else {
fetchData(val);
}
} }
}} : {
onClick
})}
{...props}
>
<MyIcon name={icon} w={'6px'} color={'myGray.900'} />
</Flex>
);
};
return (
<Flex alignItems={'center'} justifyContent={'center'} fontSize={'sm'} userSelect={'none'}>
{isPc && <Box color={'myGray.500'}>{t('common:total_num', { num: totalDataLength })}</Box>}
<Flex alignItems={'center'} ml={6} mr={4}>
{isPc && (
<IconButton
mr={2}
isDisabled={pageNum === 1}
icon="common/first_page"
onClick={() => fetchData(1)}
/>
)}
<IconButton
isDisabled={pageNum === 1}
icon="common/leftArrowLight"
onClick={() => fetchData(pageNum - 1)}
/>
<Box ml={4} color={'myGray.500'}>
{pageNum}
</Box>
<Box mx={1} color={'myGray.500'}>
/
</Box>
<Box mr={4} color={'myGray.900'}>
{maxPage}
</Box>
<IconButton
isDisabled={pageNum === maxPage}
icon="common/rightArrowLight"
onClick={() => fetchData(pageNum + 1)}
/> />
<Box mx={2}>/</Box> {isPc && (
{maxPage} <IconButton
ml={2}
isDisabled={pageNum === maxPage}
icon="common/latest_page"
onClick={() => fetchData(maxPage)}
/>
)}
</Flex> </Flex>
<IconButton
isDisabled={pageNum === maxPage} {isPc && (
icon={<ArrowForwardIcon />} <MyMenu
aria-label={'left'} menuList={[
size={'sm'} {
isLoading={isLoading} label: '',
w={'28px'} children: pageSizeOptions.map((item) => ({
h={'28px'} label: `${item}`,
onClick={() => fetchData(pageNum + 1)} isActive: pageSize === item,
/> onClick: () => setPageSize(item)
}))
}
]}
Button={
<Flex alignItems={'center'} cursor={'pointer'}>
<Box color={'myGray.900'}>{pageSize}</Box>
<Box mx={1} color={'myGray.500'}>
/
</Box>
<Box color={'myGray.500'}>{t('common:page')}</Box>
<MyIcon ml={1} name={'core/chat/chevronDown'} w={'14px'} color={'myGray.900'} />
</Flex>
}
/>
)}
</Flex> </Flex>
); );
}, [isLoading, totalDataLength, pageSize, fetchData, pageNum]); }, [totalDataLength, isPc, pageSize, t, pageNum, pageSizeOptions, isLoading, fetchData]);
// Scroll pagination // Scroll pagination
const DefaultRef = useRef<HTMLDivElement>(null); const DefaultRef = useRef<HTMLDivElement>(null);
...@@ -259,6 +323,9 @@ export function usePagination<DataT, ResT = {}>( ...@@ -259,6 +323,9 @@ export function usePagination<DataT, ResT = {}>(
throttleWait: 100 throttleWait: 100
} }
); );
useEffect(() => {
data.length > 0 && fetchData();
}, [pageSize]);
useRequest( useRequest(
async () => { async () => {
......
import type { ReactNode } from 'react';
import React, { useState, useCallback, useMemo } from 'react';
import type { FlexProps } from '@chakra-ui/react';
import { Box, Checkbox, Flex } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
export type TableMultipleSelectHookProps<T = any> = {
list: T[];
getItemId: (item: T) => string | number;
};
export const useTableMultipleSelect = <T = any,>({
list,
getItemId
}: TableMultipleSelectHookProps<T>) => {
const { t } = useTranslation();
const [selectedItems, setSelectedItems] = useState<T[]>([]);
// Toggle single item selection
const toggleSelect = useCallback(
(item: T) => {
const itemId = getItemId(item);
setSelectedItems((prev) => {
const isSelected = prev.some((selected) => getItemId(selected) === itemId);
if (isSelected) {
return prev.filter((selected) => getItemId(selected) !== itemId);
} else {
return [...prev, item];
}
});
},
[getItemId]
);
// Check if item is selected
const isSelected = useCallback(
(item: T) => {
const itemId = getItemId(item);
return selectedItems.some((selected) => getItemId(selected) === itemId);
},
[selectedItems, getItemId]
);
const isSelecteAll = useMemo(() => {
return list.length > 0 && list.every((item) => isSelected(item));
}, [list, isSelected]);
// Select all items
const selectAllTrigger = useCallback(() => {
if (isSelecteAll) {
setSelectedItems([]);
} else {
setSelectedItems((pre) => [...pre, ...list.filter((item) => !isSelected(item))]);
}
}, [isSelecteAll, list, isSelected]);
const selectedCount = selectedItems.length;
// Check if has selections
const hasSelections = selectedCount > 0;
// Floating Action Bar component
const FloatingActionBar = useCallback(
({
children,
Controler,
...props
}: { children: ReactNode; Controler: ReactNode } & FlexProps) => {
return (
<Flex w={'100%'} bg="white" px={6} pt={4} pb={2} alignItems="center" {...props}>
{hasSelections && (
<>
<Checkbox size="sm" isChecked={isSelecteAll} onChange={selectAllTrigger} />
<Box ml={2} fontSize="sm" color="gray.600">
{t('common:select_count_num', { num: selectedCount })}
</Box>
<Box flex={'1 0 0'} ml={4}>
{Controler}
</Box>
</>
)}
<Box flex={hasSelections ? '' : '1 0 0'}>{children}</Box>
</Flex>
);
},
[hasSelections, isSelecteAll, selectAllTrigger, selectedCount, t]
);
return {
selectedItems,
isSelecteAll,
selectAllTrigger,
hasSelections,
toggleSelect,
isSelected,
FloatingActionBar,
setSelectedItems
};
};
...@@ -939,6 +939,7 @@ ...@@ -939,6 +939,7 @@
"not_yet_introduced": "No Introduction Yet", "not_yet_introduced": "No Introduction Yet",
"open_folder": "Open Folder", "open_folder": "Open Folder",
"option": "Option", "option": "Option",
"page": "Page",
"page_center": "Page Center", "page_center": "Page Center",
"pay.amount": "Amount", "pay.amount": "Amount",
"pay.error_desc": "There was a problem when converting payment routes", "pay.error_desc": "There was a problem when converting payment routes",
...@@ -1024,6 +1025,7 @@ ...@@ -1024,6 +1025,7 @@
"search_tool": "Search Tools", "search_tool": "Search Tools",
"secret_key": "Secret", "secret_key": "Secret",
"secret_tips": "The value will not return plaintext again after saving", "secret_tips": "The value will not return plaintext again after saving",
"select_count_num": "{{num}} item selected",
"select_file_failed": "File Selection Failed", "select_file_failed": "File Selection Failed",
"select_reference_variable": "Select Reference Variable", "select_reference_variable": "Select Reference Variable",
"select_template": "Select Template", "select_template": "Select Template",
...@@ -1238,6 +1240,7 @@ ...@@ -1238,6 +1240,7 @@
"template_market": "Template Market", "template_market": "Template Market",
"textarea_variable_picker_tip": "Enter \"/\" to select a variable", "textarea_variable_picker_tip": "Enter \"/\" to select a variable",
"to_dataset": "To dataset", "to_dataset": "To dataset",
"total_num": "Total: {{num}}",
"ui.textarea.Magnifying": "Magnifying", "ui.textarea.Magnifying": "Magnifying",
"un_used": "Unused", "un_used": "Unused",
"unauth_token": "The certificate has expired, please log in again", "unauth_token": "The certificate has expired, please log in again",
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"backup_dataset_tip": "You can reimport the downloaded csv file when exporting the knowledge base.", "backup_dataset_tip": "You can reimport the downloaded csv file when exporting the knowledge base.",
"backup_mode": "Backup import", "backup_mode": "Backup import",
"backup_template_invalid": "The backup file format is incorrect, it should be the csv file with the first column as q,a,indexes", "backup_template_invalid": "The backup file format is incorrect, it should be the csv file with the first column as q,a,indexes",
"batch_delete": "Delete",
"chunk_max_tokens": "max_tokens", "chunk_max_tokens": "max_tokens",
"chunk_process_params": "Block processing parameters", "chunk_process_params": "Block processing parameters",
"chunk_size": "Block size", "chunk_size": "Block size",
...@@ -41,6 +42,7 @@ ...@@ -41,6 +42,7 @@
"common_dataset_desc": "Building a knowledge base by importing files, web page links, or manual entry", "common_dataset_desc": "Building a knowledge base by importing files, web page links, or manual entry",
"condition": "condition", "condition": "condition",
"config_sync_schedule": "Configure scheduled synchronization", "config_sync_schedule": "Configure scheduled synchronization",
"confirm_delete_collection": "Confirm to delete {{num }} files?",
"confirm_import_images": "Total {{num}} | Confirm create", "confirm_import_images": "Total {{num}} | Confirm create",
"confirm_to_rebuild_embedding_tip": "Are you sure you want to switch the index for the Dataset?\nSwitching the index is a significant operation that requires re-indexing all data in your Dataset, which may take a long time. Please ensure your account has sufficient remaining points.\n\nAdditionally, you need to update the applications that use this Dataset to avoid conflicts with other indexed model Datasets.", "confirm_to_rebuild_embedding_tip": "Are you sure you want to switch the index for the Dataset?\nSwitching the index is a significant operation that requires re-indexing all data in your Dataset, which may take a long time. Please ensure your account has sufficient remaining points.\n\nAdditionally, you need to update the applications that use this Dataset to avoid conflicts with other indexed model Datasets.",
"core.dataset.Image collection": "Image dataset", "core.dataset.Image collection": "Image dataset",
......
...@@ -665,10 +665,10 @@ ...@@ -665,10 +665,10 @@
"core.module.template.System Plugin": "系统插件", "core.module.template.System Plugin": "系统插件",
"core.module.template.System input module": "系统输入", "core.module.template.System input module": "系统输入",
"core.module.template.Team app": "团队应用", "core.module.template.Team app": "团队应用",
"core.module.template.all_team_app": "全部",
"core.module.template.UnKnow Module": "未知模块", "core.module.template.UnKnow Module": "未知模块",
"core.module.template.ai_chat": "AI 对话", "core.module.template.ai_chat": "AI 对话",
"core.module.template.ai_chat_intro": "AI 大模型对话", "core.module.template.ai_chat_intro": "AI 大模型对话",
"core.module.template.all_team_app": "全部",
"core.module.template.config_params": "可以配置应用的系统参数", "core.module.template.config_params": "可以配置应用的系统参数",
"core.module.template.empty_plugin": "空白插件", "core.module.template.empty_plugin": "空白插件",
"core.module.template.empty_workflow": "空白工作流", "core.module.template.empty_workflow": "空白工作流",
...@@ -939,6 +939,7 @@ ...@@ -939,6 +939,7 @@
"not_yet_introduced": "暂无介绍", "not_yet_introduced": "暂无介绍",
"open_folder": "打开文件夹", "open_folder": "打开文件夹",
"option": "选项", "option": "选项",
"page": "页",
"page_center": "页面居中", "page_center": "页面居中",
"pay.amount": "金额", "pay.amount": "金额",
"pay.error_desc": "转换支付途径时出现了问题", "pay.error_desc": "转换支付途径时出现了问题",
...@@ -1024,6 +1025,7 @@ ...@@ -1024,6 +1025,7 @@
"search_tool": "搜索工具", "search_tool": "搜索工具",
"secret_key": "密钥", "secret_key": "密钥",
"secret_tips": "值保存后不会再次明文返回", "secret_tips": "值保存后不会再次明文返回",
"select_count_num": "已选 {{num}} 项",
"select_file_failed": "选择文件异常", "select_file_failed": "选择文件异常",
"select_reference_variable": "选择引用变量", "select_reference_variable": "选择引用变量",
"select_template": "选择模板", "select_template": "选择模板",
...@@ -1239,6 +1241,7 @@ ...@@ -1239,6 +1241,7 @@
"template_market": "模板市场", "template_market": "模板市场",
"textarea_variable_picker_tip": "输入\"/\"可选择变量", "textarea_variable_picker_tip": "输入\"/\"可选择变量",
"to_dataset": "前往知识库", "to_dataset": "前往知识库",
"total_num": "总数: {{num}}",
"ui.textarea.Magnifying": "放大", "ui.textarea.Magnifying": "放大",
"un_used": "未使用", "un_used": "未使用",
"unauth_token": "凭证已过期,请重新登录", "unauth_token": "凭证已过期,请重新登录",
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"backup_dataset_tip": "可以将导出知识库时,下载的 csv 文件重新导入。", "backup_dataset_tip": "可以将导出知识库时,下载的 csv 文件重新导入。",
"backup_mode": "备份导入", "backup_mode": "备份导入",
"backup_template_invalid": "备份文件格式不正确,应该是首列为 q,a,indexes 的 csv 文件", "backup_template_invalid": "备份文件格式不正确,应该是首列为 q,a,indexes 的 csv 文件",
"batch_delete": "批量删除",
"chunk_max_tokens": "分块上限", "chunk_max_tokens": "分块上限",
"chunk_process_params": "分块处理参数", "chunk_process_params": "分块处理参数",
"chunk_size": "分块大小", "chunk_size": "分块大小",
...@@ -41,6 +42,7 @@ ...@@ -41,6 +42,7 @@
"common_dataset_desc": "通过导入文件、网页链接或手动录入形式构建知识库", "common_dataset_desc": "通过导入文件、网页链接或手动录入形式构建知识库",
"condition": "条件", "condition": "条件",
"config_sync_schedule": "配置定时同步", "config_sync_schedule": "配置定时同步",
"confirm_delete_collection": "确认删除 {{num }} 个文件?",
"confirm_import_images": "共 {{num}} 张图片 | 确认创建", "confirm_import_images": "共 {{num}} 张图片 | 确认创建",
"confirm_to_rebuild_embedding_tip": "确认为知识库切换索引?\n切换索引是一个非常重量的操作,需要对您知识库内所有数据进行重新索引,时间可能较长,请确保账号内剩余积分充足。\n\n此外,你还需要注意修改选择该知识库的应用,避免它们与其他索引模型知识库混用。", "confirm_to_rebuild_embedding_tip": "确认为知识库切换索引?\n切换索引是一个非常重量的操作,需要对您知识库内所有数据进行重新索引,时间可能较长,请确保账号内剩余积分充足。\n\n此外,你还需要注意修改选择该知识库的应用,避免它们与其他索引模型知识库混用。",
"core.dataset.Image collection": "图片数据集", "core.dataset.Image collection": "图片数据集",
......
...@@ -938,6 +938,7 @@ ...@@ -938,6 +938,7 @@
"not_yet_introduced": "暫無介紹", "not_yet_introduced": "暫無介紹",
"open_folder": "開啟資料夾", "open_folder": "開啟資料夾",
"option": "選項", "option": "選項",
"page": "頁",
"page_center": "頁面置中", "page_center": "頁面置中",
"pay.amount": "金額", "pay.amount": "金額",
"pay.error_desc": "轉換支付途徑時出現了問題", "pay.error_desc": "轉換支付途徑時出現了問題",
...@@ -1023,6 +1024,7 @@ ...@@ -1023,6 +1024,7 @@
"search_tool": "搜索工具", "search_tool": "搜索工具",
"secret_key": "密鑰", "secret_key": "密鑰",
"secret_tips": "值保存後不會再次明文返回", "secret_tips": "值保存後不會再次明文返回",
"select_count_num": "已選 {{num}} 項",
"select_file_failed": "選擇檔案失敗", "select_file_failed": "選擇檔案失敗",
"select_reference_variable": "選擇引用變數", "select_reference_variable": "選擇引用變數",
"select_template": "選擇範本", "select_template": "選擇範本",
...@@ -1237,6 +1239,7 @@ ...@@ -1237,6 +1239,7 @@
"template_market": "模板市場", "template_market": "模板市場",
"textarea_variable_picker_tip": "輸入「/」以選擇變數", "textarea_variable_picker_tip": "輸入「/」以選擇變數",
"to_dataset": "前往知識庫", "to_dataset": "前往知識庫",
"total_num": "總數: {{num}}",
"ui.textarea.Magnifying": "放大", "ui.textarea.Magnifying": "放大",
"un_used": "未使用", "un_used": "未使用",
"unauth_token": "憑證已過期,請重新登入", "unauth_token": "憑證已過期,請重新登入",
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"backup_dataset_tip": "可以將導出知識庫時,下載的 csv 文件重新導入。", "backup_dataset_tip": "可以將導出知識庫時,下載的 csv 文件重新導入。",
"backup_mode": "備份導入", "backup_mode": "備份導入",
"backup_template_invalid": "備份文件格式不正確,應該是首列為 q,a,indexes 的 csv 文件", "backup_template_invalid": "備份文件格式不正確,應該是首列為 q,a,indexes 的 csv 文件",
"batch_delete": "批量刪除",
"chunk_max_tokens": "分塊上限", "chunk_max_tokens": "分塊上限",
"chunk_process_params": "分塊處理參數", "chunk_process_params": "分塊處理參數",
"chunk_size": "分塊大小", "chunk_size": "分塊大小",
...@@ -41,6 +42,7 @@ ...@@ -41,6 +42,7 @@
"common_dataset_desc": "通過導入文件、網頁鏈接或手動錄入形式構建知識庫", "common_dataset_desc": "通過導入文件、網頁鏈接或手動錄入形式構建知識庫",
"condition": "條件", "condition": "條件",
"config_sync_schedule": "設定定時同步", "config_sync_schedule": "設定定時同步",
"confirm_delete_collection": "確認刪除 {{num }} 個文件?",
"confirm_import_images": "共 {{num}} 張圖片 | 確認創建", "confirm_import_images": "共 {{num}} 張圖片 | 確認創建",
"confirm_to_rebuild_embedding_tip": "確定要為資料集切換索引嗎?\n切換索引是一個重要的操作,需要對您資料集內所有資料重新建立索引,可能需要較長時間,請確保帳號內剩餘點數充足。\n\n此外,您還需要注意修改使用此資料集的應用程式,避免與其他索引模型資料集混用。", "confirm_to_rebuild_embedding_tip": "確定要為資料集切換索引嗎?\n切換索引是一個重要的操作,需要對您資料集內所有資料重新建立索引,可能需要較長時間,請確保帳號內剩餘點數充足。\n\n此外,您還需要注意修改使用此資料集的應用程式,避免與其他索引模型資料集混用。",
"core.dataset.Image collection": "圖片數據集", "core.dataset.Image collection": "圖片數據集",
......
...@@ -549,25 +549,37 @@ const Checkbox = checkBoxMultiStyle({ ...@@ -549,25 +549,37 @@ const Checkbox = checkBoxMultiStyle({
sizes: { sizes: {
sm: checkBoxPart({ sm: checkBoxPart({
control: { control: {
width: '18px', width: '16px',
height: '18px', height: '16px',
borderWidth: '2px' borderWidth: '2px'
},
icon: {
fontSize: '10px'
} }
}), }),
md: checkBoxPart({ md: checkBoxPart({
control: { control: {
width: '20px', width: '18px',
height: '20px', height: '18px',
borderWidth: '2px' borderWidth: '2px'
},
icon: {
fontSize: '12px'
} }
}), }),
lg: checkBoxPart({ lg: checkBoxPart({
control: { control: {
width: '24px', width: '20px',
height: '24px', height: '20px',
borderWidth: '2px' borderWidth: '2px'
},
icon: {
fontSize: '14px'
} }
}) })
},
defaultProps: {
size: 'sm'
} }
}); });
......
...@@ -60,9 +60,10 @@ const BillTable = () => { ...@@ -60,9 +60,10 @@ const BillTable = () => {
isLoading, isLoading,
Pagination, Pagination,
getData, getData,
total total,
pageSize
} = usePagination(getBills, { } = usePagination(getBills, {
pageSize: 20, defaultPageSize: 20,
params: { params: {
type: billType type: billType
}, },
...@@ -95,8 +96,8 @@ const BillTable = () => { ...@@ -95,8 +96,8 @@ const BillTable = () => {
}, [billType]); }, [billType]);
return ( return (
<MyBox isLoading={isLoading} position={'relative'} h={'100%'} minH={'50vh'}> <MyBox isLoading={isLoading} display={'flex'} flexDir={'column'} h={'100%'}>
<TableContainer> <TableContainer flex={'1 0 0'} h={0} overflowY={'auto'}>
<Table> <Table>
<Thead> <Thead>
<Tr> <Tr>
...@@ -147,11 +148,6 @@ const BillTable = () => { ...@@ -147,11 +148,6 @@ const BillTable = () => {
))} ))}
</Tbody> </Tbody>
</Table> </Table>
{total >= 20 && (
<Flex mt={3} justifyContent={'flex-end'}>
<Pagination />
</Flex>
)}
{!isLoading && bills.length === 0 && ( {!isLoading && bills.length === 0 && (
<Flex <Flex
mt={'20vh'} mt={'20vh'}
...@@ -167,6 +163,11 @@ const BillTable = () => { ...@@ -167,6 +163,11 @@ const BillTable = () => {
)} )}
</TableContainer> </TableContainer>
{total >= pageSize && (
<Flex mt={3} justifyContent={'center'}>
<Pagination />
</Flex>
)}
{!!billDetail && ( {!!billDetail && (
<BillDetailModal bill={billDetail} onClose={() => setBillDetail(undefined)} /> <BillDetailModal bill={billDetail} onClose={() => setBillDetail(undefined)} />
)} )}
......
...@@ -32,14 +32,15 @@ const InvoiceTable = () => { ...@@ -32,14 +32,15 @@ const InvoiceTable = () => {
data: invoices, data: invoices,
isLoading, isLoading,
Pagination, Pagination,
total total,
pageSize
} = usePagination(getInvoiceRecords, { } = usePagination(getInvoiceRecords, {
pageSize: 20 defaultPageSize: 10
}); });
return ( return (
<MyBox isLoading={isLoading} position={'relative'} h={'100%'} overflow={'overlay'}> <MyBox isLoading={isLoading} position={'relative'} minH={'50vh'} overflow={'overlay'}>
<TableContainer minH={'50vh'}> <TableContainer>
<Table> <Table>
<Thead h="3rem"> <Thead h="3rem">
<Tr> <Tr>
...@@ -101,11 +102,6 @@ const InvoiceTable = () => { ...@@ -101,11 +102,6 @@ const InvoiceTable = () => {
))} ))}
</Tbody> </Tbody>
</Table> </Table>
{total >= 20 && (
<Flex mt={3} justifyContent={'flex-end'}>
<Pagination />
</Flex>
)}
{!isLoading && invoices.length === 0 && ( {!isLoading && invoices.length === 0 && (
<Flex <Flex
mt={'20vh'} mt={'20vh'}
...@@ -120,6 +116,11 @@ const InvoiceTable = () => { ...@@ -120,6 +116,11 @@ const InvoiceTable = () => {
</Flex> </Flex>
)} )}
</TableContainer> </TableContainer>
{total >= pageSize && (
<Flex mt={3} justifyContent={'center'}>
<Pagination />
</Flex>
)}
{!!invoiceDetailData && ( {!!invoiceDetailData && (
<InvoiceDetailModal invoice={invoiceDetailData} onClose={() => setInvoiceDetailData('')} /> <InvoiceDetailModal invoice={invoiceDetailData} onClose={() => setInvoiceDetailData('')} />
)} )}
......
...@@ -68,7 +68,7 @@ const UsageTableList = ({ ...@@ -68,7 +68,7 @@ const UsageTableList = ({
Pagination, Pagination,
total total
} = usePagination(getUserUsages, { } = usePagination(getUserUsages, {
pageSize: 20, defaultPageSize: 20,
params: requestParams, params: requestParams,
refreshDeps: [requestParams] refreshDeps: [requestParams]
}); });
...@@ -111,7 +111,7 @@ const UsageTableList = ({ ...@@ -111,7 +111,7 @@ const UsageTableList = ({
); );
return ( return (
<> <MyBox display={'flex'} flexDirection={'column'} h={'100%'} isLoading={isLoading}>
<Box>{Tabs}</Box> <Box>{Tabs}</Box>
<Flex mt={4} w={'100%'}> <Flex mt={4} w={'100%'}>
<Box>{Selectors}</Box> <Box>{Selectors}</Box>
...@@ -123,54 +123,46 @@ const UsageTableList = ({ ...@@ -123,54 +123,46 @@ const UsageTableList = ({
onConfirm={exportUsage} onConfirm={exportUsage}
/> />
</Flex> </Flex>
<MyBox mt={3} flex={'1 0 0'} h={0} isLoading={isLoading}> <TableContainer mt={3} flex={'1 0 0'} h={0} overflowY={'auto'}>
<Box h={'100%'} overflow={'auto'}> <Table>
<TableContainer> <Thead>
<Table> <Tr>
<Thead> <Th>{t('common:user.Time')}</Th>
<Tr> <Th>{t('account_usage:member')}</Th>
<Th>{t('common:user.Time')}</Th> <Th>{t('account_usage:user_type')}</Th>
<Th>{t('account_usage:member')}</Th> <Th>{t('account_usage:project_name')}</Th>
<Th>{t('account_usage:user_type')}</Th> <Th>{t('account_usage:total_points')}</Th>
<Th>{t('account_usage:project_name')}</Th> <Th></Th>
<Th>{t('account_usage:total_points')}</Th> </Tr>
<Th></Th> </Thead>
</Tr> <Tbody fontSize={'sm'}>
</Thead> {usages.map((item) => (
<Tbody fontSize={'sm'}> <Tr key={item.id}>
{usages.map((item) => ( <Td>{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}</Td>
<Tr key={item.id}> <Td>
<Td>{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}</Td> <Flex alignItems={'center'} color={'myGray.500'}>
<Td> <Avatar src={item.sourceMember.avatar} w={'20px'} mr={1} rounded={'full'} />
<Flex alignItems={'center'} color={'myGray.500'}> {item.sourceMember.name}
<Avatar src={item.sourceMember.avatar} w={'20px'} mr={1} rounded={'full'} /> </Flex>
{item.sourceMember.name} </Td>
</Flex> <Td>{t(UsageSourceMap[item.source]?.label as any) || '-'}</Td>
</Td> <Td className="textEllipsis" maxW={'400px'} title={t(item.appName as any)}>
<Td>{t(UsageSourceMap[item.source]?.label as any) || '-'}</Td> {t(item.appName as any) || '-'}
<Td className="textEllipsis" maxW={'400px'} title={t(item.appName as any)}> </Td>
{t(item.appName as any) || '-'} <Td>{formatNumber(item.totalPoints) || 0}</Td>
</Td> <Td>
<Td>{formatNumber(item.totalPoints) || 0}</Td> <Button size={'sm'} variant={'whitePrimary'} onClick={() => setUsageDetail(item)}>
<Td> {t('account_usage:details')}
<Button </Button>
size={'sm'} </Td>
variant={'whitePrimary'} </Tr>
onClick={() => setUsageDetail(item)} ))}
> </Tbody>
{t('account_usage:details')} </Table>
</Button> {!isLoading && usages.length === 0 && (
</Td> <EmptyTip text={t('account_usage:no_usage_records')}></EmptyTip>
</Tr> )}
))} </TableContainer>
</Tbody>
</Table>
{!isLoading && usages.length === 0 && (
<EmptyTip text={t('account_usage:no_usage_records')}></EmptyTip>
)}
</TableContainer>
</Box>
</MyBox>
<Flex mt={3} justifyContent={'center'}> <Flex mt={3} justifyContent={'center'}>
<Pagination /> <Pagination />
</Flex> </Flex>
...@@ -178,7 +170,7 @@ const UsageTableList = ({ ...@@ -178,7 +170,7 @@ const UsageTableList = ({
{!!usageDetail && ( {!!usageDetail && (
<UsageDetail usage={usageDetail} onClose={() => setUsageDetail(undefined)} /> <UsageDetail usage={usageDetail} onClose={() => setUsageDetail(undefined)} />
)} )}
</> </MyBox>
); );
}; };
......
...@@ -42,6 +42,7 @@ import { ...@@ -42,6 +42,7 @@ import {
} from '@fastgpt/global/core/app/logs/constants'; } from '@fastgpt/global/core/app/logs/constants';
import { formatDateByTimespan } from '@fastgpt/global/core/app/logs/utils'; import { formatDateByTimespan } from '@fastgpt/global/core/app/logs/utils';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyBox from '@fastgpt/web/components/common/MyBox';
export type HeaderControlProps = { export type HeaderControlProps = {
appId: string; appId: string;
...@@ -149,7 +150,7 @@ const LogChart = ({ ...@@ -149,7 +150,7 @@ const LogChart = ({
const [offset, setOffset] = useState<string>(offsetOptions[0].value); const [offset, setOffset] = useState<string>(offsetOptions[0].value);
const { data: chartData } = useRequest2( const { data: chartData, loading } = useRequest2(
async () => { async () => {
return getAppChartData({ return getAppChartData({
appId, appId,
...@@ -331,7 +332,7 @@ const LogChart = ({ ...@@ -331,7 +332,7 @@ const LogChart = ({
]); ]);
return ( return (
<Flex flexDir={'column'} h={'full'}> <MyBox isLoading={loading} display={'flex'} flexDir={'column'} h={'full'}>
<HeaderControl <HeaderControl
appId={appId} appId={appId}
chatSources={chatSources} chatSources={chatSources}
...@@ -751,7 +752,7 @@ const LogChart = ({ ...@@ -751,7 +752,7 @@ const LogChart = ({
</AccordionItem> </AccordionItem>
</Accordion> </Accordion>
</Flex> </Flex>
</Flex> </MyBox>
); );
}; };
......
...@@ -49,6 +49,7 @@ import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; ...@@ -49,6 +49,7 @@ import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import type { HeaderControlProps } from './LogChart'; import type { HeaderControlProps } from './LogChart';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyBox from '@fastgpt/web/components/common/MyBox';
const DetailLogsModal = dynamic(() => import('./DetailLogsModal')); const DetailLogsModal = dynamic(() => import('./DetailLogsModal'));
...@@ -196,9 +197,10 @@ const LogTable = ({ ...@@ -196,9 +197,10 @@ const LogTable = ({
Pagination, Pagination,
getData, getData,
pageNum, pageNum,
total total,
pageSize
} = usePagination(getAppChatLogs, { } = usePagination(getAppChatLogs, {
pageSize: 20, defaultPageSize: 20,
params, params,
refreshDeps: [params] refreshDeps: [params]
}); });
...@@ -338,7 +340,7 @@ const LogTable = ({ ...@@ -338,7 +340,7 @@ const LogTable = ({
}); });
return ( 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'}> <Flex alignItems={'center'} gap={3} flexWrap={'wrap'}>
{showSourceSelector && ( {showSourceSelector && (
<Flex> <Flex>
...@@ -465,7 +467,7 @@ const LogTable = ({ ...@@ -465,7 +467,7 @@ const LogTable = ({
/> />
</Flex> </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'}> <Table variant={'simple'} fontSize={'sm'}>
<Thead> <Thead>
<Tr> <Tr>
...@@ -496,9 +498,11 @@ const LogTable = ({ ...@@ -496,9 +498,11 @@ const LogTable = ({
{logs.length === 0 && !isLoading && <EmptyTip text={t('app:logs_empty')}></EmptyTip>} {logs.length === 0 && !isLoading && <EmptyTip text={t('app:logs_empty')}></EmptyTip>}
</TableContainer> </TableContainer>
<HStack w={'100%'} mt={3} justifyContent={'center'}> {total >= pageSize && (
<Pagination /> <Flex mt={3} justifyContent={'center'}>
</HStack> <Pagination />
</Flex>
)}
{!!detailLogsId && ( {!!detailLogsId && (
<DetailLogsModal <DetailLogsModal
...@@ -510,7 +514,7 @@ const LogTable = ({ ...@@ -510,7 +514,7 @@ const LogTable = ({
}} }}
/> />
)} )}
</Flex> </MyBox>
); );
}; };
......
...@@ -149,11 +149,12 @@ const Header = () => { ...@@ -149,11 +149,12 @@ const Header = () => {
p={0.5} p={0.5}
borderRadius={'sm'} borderRadius={'sm'}
> >
<MyIcon <IconButton
name={'common/leftArrowLight'} icon={<MyIcon name={'common/leftArrowLight'} color={'myGray.600'} w={'0.8rem'} />}
w={6} aria-label={''}
cursor={'pointer'} size={'xs'}
onClick={isSaved ? onBack : onOpenBackConfirm} w={'1rem'}
variant={'ghost'}
/> />
</Box> </Box>
...@@ -213,8 +214,6 @@ const Header = () => { ...@@ -213,8 +214,6 @@ const Header = () => {
isPc, isPc,
currentTab, currentTab,
isSaved, isSaved,
onBack,
onOpenBackConfirm,
isV2Workflow, isV2Workflow,
t, t,
showHistoryModal, showHistoryModal,
......
...@@ -149,11 +149,12 @@ const Header = () => { ...@@ -149,11 +149,12 @@ const Header = () => {
p={0.5} p={0.5}
borderRadius={'sm'} borderRadius={'sm'}
> >
<MyIcon <IconButton
name={'common/leftArrowLight'} icon={<MyIcon name={'common/leftArrowLight'} color={'myGray.600'} w={'0.8rem'} />}
w={6} aria-label={''}
cursor={'pointer'} size={'xs'}
onClick={isSaved ? onBack : onOpenBackConfirm} w={'1rem'}
variant={'ghost'}
/> />
</Box> </Box>
...@@ -213,8 +214,6 @@ const Header = () => { ...@@ -213,8 +214,6 @@ const Header = () => {
isPc, isPc,
currentTab, currentTab,
isSaved, isSaved,
onBack,
onOpenBackConfirm,
isV2Workflow, isV2Workflow,
t, t,
showHistoryModal, showHistoryModal,
......
...@@ -97,9 +97,11 @@ const EvaluationDetailModal = ({ ...@@ -97,9 +97,11 @@ const EvaluationDetailModal = ({
const { const {
data: evalItemsList, data: evalItemsList,
Pagination, Pagination,
pageSize,
total,
getData: fetchData getData: fetchData
} = usePagination(getEvalItemsList, { } = usePagination(getEvalItemsList, {
pageSize: 10, defaultPageSize: 20,
params: { params: {
evalId: evalDetail._id evalId: evalDetail._id
}, },
...@@ -440,9 +442,11 @@ const EvaluationDetailModal = ({ ...@@ -440,9 +442,11 @@ const EvaluationDetailModal = ({
})} })}
</Box> </Box>
<Box px={6} py={3}> {total >= pageSize && (
<Pagination /> <Flex my={3} justifyContent={'center'}>
</Box> <Pagination />
</Flex>
)}
</Box> </Box>
</Flex> </Flex>
{evalItem ? ( {evalItem ? (
......
...@@ -128,7 +128,7 @@ const CollectionPageContextProvider = ({ children }: { children: ReactNode }) => ...@@ -128,7 +128,7 @@ const CollectionPageContextProvider = ({ children }: { children: ReactNode }) =>
pageNum, pageNum,
pageSize pageSize
} = usePagination(getDatasetCollections, { } = usePagination(getDatasetCollections, {
pageSize: 20, defaultPageSize: 20,
params: { params: {
datasetId, datasetId,
parentId, parentId,
......
...@@ -10,14 +10,16 @@ import { ...@@ -10,14 +10,16 @@ import {
Td, Td,
Tbody, Tbody,
MenuButton, MenuButton,
Switch Switch,
Checkbox,
HStack,
Button
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
delDatasetCollectionById, delDatasetCollectionById,
putDatasetCollectionById, putDatasetCollectionById,
postLinkCollectionSync postLinkCollectionSync
} from '@/web/core/dataset/api'; } from '@/web/core/dataset/api';
import { useQuery } from '@tanstack/react-query';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
...@@ -49,6 +51,7 @@ import { useFolderDrag } from '@/components/common/folder/useFolderDrag'; ...@@ -49,6 +51,7 @@ import { useFolderDrag } from '@/components/common/folder/useFolderDrag';
import TagsPopOver from './TagsPopOver'; import TagsPopOver from './TagsPopOver';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import TrainingStates from './TrainingStates'; import TrainingStates from './TrainingStates';
import { useTableMultipleSelect } from '@fastgpt/web/hooks/useTableMultipleSelect';
const Header = dynamic(() => import('./Header')); const Header = dynamic(() => import('./Header'));
const EmptyCollectionTip = dynamic(() => import('./EmptyCollectionTip')); const EmptyCollectionTip = dynamic(() => import('./EmptyCollectionTip'));
...@@ -103,6 +106,19 @@ const CollectionCard = () => { ...@@ -103,6 +106,19 @@ const CollectionCard = () => {
[collections, t] [collections, t]
); );
const {
selectedItems,
toggleSelect,
isSelected,
setSelectedItems,
FloatingActionBar,
isSelecteAll,
selectAllTrigger
} = useTableMultipleSelect({
list: formatCollections,
getItemId: (e) => e._id
});
const [moveCollectionData, setMoveCollectionData] = useState<{ collectionId: string }>(); const [moveCollectionData, setMoveCollectionData] = useState<{ collectionId: string }>();
const { onOpenModal: onOpenEditTitleModal, EditModal: EditTitleModal } = useEditTitle({ const { onOpenModal: onOpenEditTitleModal, EditModal: EditTitleModal } = useEditTitle({
...@@ -123,9 +139,9 @@ const CollectionCard = () => { ...@@ -123,9 +139,9 @@ const CollectionCard = () => {
type: 'delete' type: 'delete'
}); });
const { runAsync: onDelCollection } = useRequest2( const { runAsync: onDelCollection } = useRequest2(
(collectionId: string) => { (collectionIds: string[]) => {
return delDatasetCollectionById({ return delDatasetCollectionById({
id: collectionId collectionIds
}); });
}, },
{ {
...@@ -156,18 +172,17 @@ const CollectionCard = () => { ...@@ -156,18 +172,17 @@ const CollectionCard = () => {
[formatCollections] [formatCollections]
); );
useQuery( useRequest2(
['refreshCollection'], async () => {
() => { if (!hasTrainingData && datasetDetail.status === DatasetStatusEnum.active) return;
getData(pageNum); getData(pageNum);
if (datasetDetail.status !== DatasetStatusEnum.active) { if (datasetDetail.status !== DatasetStatusEnum.active) {
loadDatasetDetail(datasetDetail._id); loadDatasetDetail(datasetDetail._id);
} }
return null;
}, },
{ {
refetchInterval: 6000, retryInterval: 6000,
enabled: hasTrainingData || datasetDetail.status !== DatasetStatusEnum.active refreshDeps: [hasTrainingData, datasetDetail.status]
} }
); );
...@@ -186,21 +201,25 @@ const CollectionCard = () => { ...@@ -186,21 +201,25 @@ const CollectionCard = () => {
} }
}); });
const isLoading = const isLoading = isUpdating || isSyncing || isGetting || isDropping;
isUpdating || isSyncing || (isGetting && collections.length === 0) || isDropping;
return ( 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]}> <Flex ref={BoxRef} flexDirection={'column'} py={[1, 0]} h={'100%'} px={[2, 6]}>
{/* header */} {/* header */}
<Header hasTrainingData={hasTrainingData} /> <Header hasTrainingData={hasTrainingData} />
{/* collection table */} {/* 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}> <Table variant={'simple'} draggable={false}>
<Thead draggable={false}> <Thead draggable={false}>
<Tr> <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.training_type')}</Th>
<Th py={4}>{t('dataset:collection_data_count')}</Th> <Th py={4}>{t('dataset:collection_data_count')}</Th>
<Th py={4}>{t('dataset:collection.Create update time')}</Th> <Th py={4}>{t('dataset:collection.Create update time')}</Th>
...@@ -241,17 +260,27 @@ const CollectionCard = () => { ...@@ -241,17 +260,27 @@ const CollectionCard = () => {
}} }}
> >
<Td minW={'150px'} maxW={['200px', '300px']} draggable py={2}> <Td minW={'150px'} maxW={['200px', '300px']} draggable py={2}>
<Flex alignItems={'center'}> <HStack>
<MyIcon name={collection.icon as any} w={'1.25rem'} mr={2} /> <Box onClick={(e) => e.stopPropagation()}>
<MyTooltip label={t('common:click_drag_tip')} shouldWrapChildren={false}> <Checkbox
<Box color={'myGray.900'} fontWeight={'500'} className="textEllipsis"> isChecked={isSelected(collection)}
{collection.name} onChange={(e) => toggleSelect(collection)}
</Box> />
</MyTooltip> </Box>
</Flex> <Box>
{feConfigs?.isPlus && !!collection.tags?.length && ( <Flex alignItems={'center'}>
<TagsPopOver currentCollection={collection} /> <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>
<Td py={2}> <Td py={2}>
{collection.trainingType {collection.trainingType
...@@ -394,7 +423,7 @@ const CollectionCard = () => { ...@@ -394,7 +423,7 @@ const CollectionCard = () => {
type: 'danger', type: 'danger',
onClick: () => onClick: () =>
openDeleteConfirm( openDeleteConfirm(
() => onDelCollection(collection._id), () => onDelCollection([collection._id]),
undefined, undefined,
collection.type === DatasetCollectionTypeEnum.folder collection.type === DatasetCollectionTypeEnum.folder
? t('common:dataset.collections.Confirm to delete the folder') ? t('common:dataset.collections.Confirm to delete the folder')
...@@ -411,13 +440,39 @@ const CollectionCard = () => { ...@@ -411,13 +440,39 @@ const CollectionCard = () => {
))} ))}
</Tbody> </Tbody>
</Table> </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 && ( {total > pageSize && (
<Flex mt={2} justifyContent={'center'}> <Flex justifyContent={'center'}>
<Pagination /> <Pagination />
</Flex> </Flex>
)} )}
{total === 0 && <EmptyCollectionTip />} </FloatingActionBar>
</TableContainer>
<ConfirmDeleteModal /> <ConfirmDeleteModal />
<ConfirmSyncModal /> <ConfirmSyncModal />
......
...@@ -27,9 +27,10 @@ const BillAndInvoice = () => { ...@@ -27,9 +27,10 @@ const BillAndInvoice = () => {
return ( return (
<AccountContainer> <AccountContainer>
<Box p={['1rem', '2rem']}> <Flex h={'100%'} flexDirection={'column'} p={['2', '4']}>
<Flex justifyContent={'space-between'} alignItems={'center'} pb={'0.75rem'}> <Flex justifyContent={'space-between'} alignItems={'center'} pb={'0.75rem'}>
<FillRowTabs <FillRowTabs
py={1}
list={[ list={[
{ label: t('account_bill:bill_record'), value: InvoiceTabEnum.bill }, { label: t('account_bill:bill_record'), value: InvoiceTabEnum.bill },
{ {
...@@ -61,7 +62,7 @@ const BillAndInvoice = () => { ...@@ -61,7 +62,7 @@ const BillAndInvoice = () => {
</Button> </Button>
)} )}
</Flex> </Flex>
<Box h={'100%'}> <Box flex={'1 0 0'} h={0} overflow={'auto'}>
{invoiceTab === InvoiceTabEnum.bill && <BillTable />} {invoiceTab === InvoiceTabEnum.bill && <BillTable />}
{invoiceTab === InvoiceTabEnum.invoice && <InvoiceTable />} {invoiceTab === InvoiceTabEnum.invoice && <InvoiceTable />}
{invoiceTab === InvoiceTabEnum.invoiceHeader && <InvoiceHeaderForm />} {invoiceTab === InvoiceTabEnum.invoiceHeader && <InvoiceHeaderForm />}
...@@ -73,7 +74,7 @@ const BillAndInvoice = () => { ...@@ -73,7 +74,7 @@ const BillAndInvoice = () => {
}} }}
/> />
)} )}
</Box> </Flex>
</AccountContainer> </AccountContainer>
); );
}; };
......
...@@ -44,7 +44,7 @@ const InformTable = () => { ...@@ -44,7 +44,7 @@ const InformTable = () => {
getData, getData,
pageNum pageNum
} = usePagination(getInforms, { } = usePagination(getInforms, {
pageSize: 20 defaultPageSize: 20
}); });
return ( return (
...@@ -147,7 +147,7 @@ const InformTable = () => { ...@@ -147,7 +147,7 @@ const InformTable = () => {
)} )}
{total > pageSize && ( {total > pageSize && (
<Flex w="100%" mt={4} px={[3, 8]} justifyContent="flex-end"> <Flex mt={4} justifyContent="center">
<Pagination /> <Pagination />
</Flex> </Flex>
)} )}
......
...@@ -43,7 +43,7 @@ const Promotion = () => { ...@@ -43,7 +43,7 @@ const Promotion = () => {
pageSize, pageSize,
Pagination Pagination
} = usePagination(getPromotionRecords, { } = usePagination(getPromotionRecords, {
pageSize: 20 defaultPageSize: 20
}); });
const { data: { invitedAmount = 0, earningsAmount = 0 } = {} } = useQuery( const { data: { invitedAmount = 0, earningsAmount = 0 } = {} } = useQuery(
...@@ -131,7 +131,7 @@ const Promotion = () => { ...@@ -131,7 +131,7 @@ const Promotion = () => {
<EmptyTip text={t('account_promotion:no_invite_records')}></EmptyTip> <EmptyTip text={t('account_promotion:no_invite_records')}></EmptyTip>
)} )}
{total > pageSize && ( {total > pageSize && (
<Flex mt={4} justifyContent={'flex-end'}> <Flex mt={3} justifyContent={'center'}>
<Pagination /> <Pagination />
</Flex> </Flex>
)} )}
......
...@@ -7,6 +7,7 @@ import { MongoAppChatLog } from '@fastgpt/service/core/app/logs/chatLogsSchema'; ...@@ -7,6 +7,7 @@ import { MongoAppChatLog } from '@fastgpt/service/core/app/logs/chatLogsSchema';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { addLog } from '@fastgpt/service/common/system/log'; import { addLog } from '@fastgpt/service/common/system/log';
import type { ChatSchemaType } from '@fastgpt/global/core/chat/type'; import type { ChatSchemaType } from '@fastgpt/global/core/chat/type';
import { surrenderProcess } from '@fastgpt/service/common/system/tools';
export type SyncAppChatLogQuery = {}; export type SyncAppChatLogQuery = {};
...@@ -63,7 +64,9 @@ async function handler( ...@@ -63,7 +64,9 @@ async function handler(
async function processChatRecord(chat: ChatSchemaType) { async function processChatRecord(chat: ChatSchemaType) {
async function calculateChatItemStats() { async function calculateChatItemStats() {
const chatItems = await MongoChatItem.find({ appId: chat.appId, chatId: chat.chatId }).lean(); const chatItems = await MongoChatItem.find({ appId: chat.appId, chatId: chat.chatId })
.limit(1000)
.lean();
let chatItemCount = chatItems.length; let chatItemCount = chatItems.length;
let errorCount = 0; let errorCount = 0;
...@@ -73,6 +76,8 @@ async function processChatRecord(chat: ChatSchemaType) { ...@@ -73,6 +76,8 @@ async function processChatRecord(chat: ChatSchemaType) {
let totalResponseTime = 0; let totalResponseTime = 0;
for (const item of chatItems) { for (const item of chatItems) {
await surrenderProcess();
const itemData = item as any; const itemData = item as any;
if (itemData.userGoodFeedback && itemData.userGoodFeedback.trim() !== '') { if (itemData.userGoodFeedback && itemData.userGoodFeedback.trim() !== '') {
......
import type { NextApiRequest } from 'next';
import { findCollectionAndChild } from '@fastgpt/service/core/dataset/collection/utils'; import { findCollectionAndChild } from '@fastgpt/service/core/dataset/collection/utils';
import { delCollection } from '@fastgpt/service/core/dataset/collection/controller'; import { delCollection } from '@fastgpt/service/core/dataset/collection/controller';
import { authDatasetCollection } from '@fastgpt/service/support/permission/dataset/auth'; import { authDatasetCollection } from '@fastgpt/service/support/permission/dataset/auth';
...@@ -9,28 +8,50 @@ import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; ...@@ -9,28 +8,50 @@ import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { addAuditLog } from '@fastgpt/service/support/user/audit/util'; import { addAuditLog } from '@fastgpt/service/support/user/audit/util';
import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants'; import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/user/audit/util'; import { getI18nDatasetType } from '@fastgpt/service/support/user/audit/util';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
async function handler(req: NextApiRequest) { export type DelCollectionBody = {
const { id: collectionId } = req.query as { id: string }; collectionIds: string[];
};
if (!collectionId) { async function handler(req: ApiRequestProps<DelCollectionBody>) {
const { collectionIds } = req.body;
if (!collectionIds) {
return Promise.reject(CommonErrEnum.missingParams); return Promise.reject(CommonErrEnum.missingParams);
} }
const { teamId, collection, tmbId } = await authDatasetCollection({ const [{ teamId, collection, tmbId }] = await Promise.all(
req, collectionIds.map(async (collectionId) => {
authToken: true, return await authDatasetCollection({
authApiKey: true, req,
collectionId, authToken: true,
per: WritePermissionVal authApiKey: true,
}); collectionId,
per: WritePermissionVal
});
})
);
// find all delete id // find all delete id
const collections = await findCollectionAndChild({ const collections = await Promise.all(
teamId, collectionIds.map(async (collectionId) => {
datasetId: collection.datasetId, return await findCollectionAndChild({
collectionId, teamId,
fields: '_id teamId datasetId fileId metadata' datasetId: collection.datasetId,
collectionId,
fields: '_id teamId datasetId fileId metadata'
});
})
).then((res) => {
const flattened = res.flat();
console.log(flattened.length, 22);
// Remove duplicates based on _id
const uniqueCollections = flattened.filter(
(collection, index, arr) =>
arr.findIndex((item) => item._id.toString() === collection._id.toString()) === index
);
return uniqueCollections;
}); });
// delete // delete
......
...@@ -23,7 +23,7 @@ async function handler(req: NextApiRequest) { ...@@ -23,7 +23,7 @@ async function handler(req: NextApiRequest) {
simple = false simple = false
} = req.body as any; } = req.body as any;
searchText = searchText?.replace(/'/g, ''); searchText = searchText?.replace(/'/g, '');
pageSize = Math.min(pageSize, 30); pageSize = Math.min(pageSize, 100);
// auth dataset and get my role // auth dataset and get my role
const { teamId, permission } = await authDataset({ const { teamId, permission } = await authDataset({
......
...@@ -28,7 +28,7 @@ async function handler( ...@@ -28,7 +28,7 @@ async function handler(
simple = false simple = false
} = req.body as GetDatasetCollectionsProps; } = req.body as GetDatasetCollectionsProps;
let { pageSize, offset } = parsePaginationRequest(req); let { pageSize, offset } = parsePaginationRequest(req);
pageSize = Math.min(pageSize, 30); pageSize = Math.min(pageSize, 100);
searchText = searchText?.replace(/'/g, ''); searchText = searchText?.replace(/'/g, '');
// auth dataset and get my role // auth dataset and get my role
......
...@@ -46,9 +46,11 @@ const Evaluation = () => { ...@@ -46,9 +46,11 @@ const Evaluation = () => {
const { const {
data: evaluationList, data: evaluationList,
Pagination, Pagination,
getData: fetchData getData: fetchData,
total,
pageSize
} = usePagination(getEvaluationList, { } = usePagination(getEvaluationList, {
pageSize: 20, defaultPageSize: 20,
pollingInterval, pollingInterval,
pollingWhenHidden: true, pollingWhenHidden: true,
params: { params: {
...@@ -185,90 +187,90 @@ const Evaluation = () => { ...@@ -185,90 +187,90 @@ const Evaluation = () => {
<Flex h={'full'} bg={'white'} p={6} flexDirection="column"> <Flex h={'full'} bg={'white'} p={6} flexDirection="column">
{renderHeader(MenuIcon)} {renderHeader(MenuIcon)}
<MyBox flex={'1 0 0'} overflow="auto"> <TableContainer mt={3} fontSize={'sm'} flex={'1 0 0'} overflowY="auto">
<TableContainer mt={3} fontSize={'sm'}> <Table variant={'simple'}>
<Table variant={'simple'}> <Thead>
<Thead> <Tr color={'myGray.600'}>
<Tr color={'myGray.600'}> <Th fontWeight={'400'}>{t('dashboard_evaluation:Task_name')}</Th>
<Th fontWeight={'400'}>{t('dashboard_evaluation:Task_name')}</Th> <Th fontWeight={'400'}>{t('dashboard_evaluation:Progress')}</Th>
<Th fontWeight={'400'}>{t('dashboard_evaluation:Progress')}</Th> <Th fontWeight={'400'}>{t('dashboard_evaluation:Executor')}</Th>
<Th fontWeight={'400'}>{t('dashboard_evaluation:Executor')}</Th> <Th fontWeight={'400'}>{t('dashboard_evaluation:Evaluation_app')}</Th>
<Th fontWeight={'400'}>{t('dashboard_evaluation:Evaluation_app')}</Th> <Th fontWeight={'400'}>{t('dashboard_evaluation:Start_end_time')}</Th>
<Th fontWeight={'400'}>{t('dashboard_evaluation:Start_end_time')}</Th> <Th fontWeight={'400'}>{t('dashboard_evaluation:Overall_score')}</Th>
<Th fontWeight={'400'}>{t('dashboard_evaluation:Overall_score')}</Th> <Th fontWeight={'400'}>{t('dashboard_evaluation:Action')}</Th>
<Th fontWeight={'400'}>{t('dashboard_evaluation:Action')}</Th> </Tr>
</Tr> </Thead>
</Thead> <Tbody>
<Tbody> <Tr h={'5px'} />
<Tr h={'5px'} /> {evaluationList.map((item) => {
{evaluationList.map((item) => { return (
return ( <Tr key={item._id}>
<Tr key={item._id}> <Td fontWeight={'medium'} color={'myGray.900'}>
<Td fontWeight={'medium'} color={'myGray.900'}> {item.name}
{item.name} </Td>
</Td> <Td>{renderProgress(item)}</Td>
<Td>{renderProgress(item)}</Td> <Td>
<Td> <Flex alignItems={'center'} gap={1.5}>
<Flex alignItems={'center'} gap={1.5}> <Avatar
<Avatar src={item.executorAvatar}
src={item.executorAvatar} w={5}
w={5} borderRadius={'full'}
borderRadius={'full'} border={'1px solid'}
border={'1px solid'} borderColor={'myGray.200'}
borderColor={'myGray.200'}
/>
<Box color={'myGray.900'}>{item.executorName}</Box>
</Flex>
</Td>
<Td>
<Flex alignItems={'center'} gap={1.5}>
<Avatar src={item.appAvatar} w={5} borderRadius={'4px'} />
<Box color={'myGray.900'}>{item.appName}</Box>
</Flex>
</Td>
<Td color={'myGray.900'}>
<Box>{formatTime2YMDHM(item.createTime)}</Box>
<Box>{formatTime2YMDHM(item.finishTime)}</Box>
</Td>
<Td color={item.score ? 'myGray.600' : 'myGray.900'}>
{typeof item.score === 'number' ? (item.score * 100).toFixed(2) : '-'}
</Td>
<Td>
<Button
variant={'whiteBase'}
leftIcon={<MyIcon name={'common/detail'} w={4} />}
fontSize={'12px'}
fontWeight={'medium'}
mr={2}
onClick={() => setEvalDetailId(item._id)}
>
{t('dashboard_evaluation:detail')}
</Button>
<PopoverConfirm
type="delete"
Trigger={
<IconButton
aria-label="delete"
size={'mdSquare'}
variant={'whiteDanger'}
icon={<MyIcon name={'delete'} w={4} />}
/>
}
content={t('dashboard_evaluation:comfirm_delete_task')}
onConfirm={() => onDeleteEval({ evalId: item._id })}
/> />
</Td> <Box color={'myGray.900'}>{item.executorName}</Box>
</Tr> </Flex>
); </Td>
})} <Td>
</Tbody> <Flex alignItems={'center'} gap={1.5}>
</Table> <Avatar src={item.appAvatar} w={5} borderRadius={'4px'} />
</TableContainer> <Box color={'myGray.900'}>{item.appName}</Box>
</MyBox> </Flex>
<Flex mt={4} justifyContent="center"> </Td>
<Pagination /> <Td color={'myGray.900'}>
</Flex> <Box>{formatTime2YMDHM(item.createTime)}</Box>
<Box>{formatTime2YMDHM(item.finishTime)}</Box>
</Td>
<Td color={item.score ? 'myGray.600' : 'myGray.900'}>
{typeof item.score === 'number' ? (item.score * 100).toFixed(2) : '-'}
</Td>
<Td>
<Button
variant={'whiteBase'}
leftIcon={<MyIcon name={'common/detail'} w={4} />}
fontSize={'12px'}
fontWeight={'medium'}
mr={2}
onClick={() => setEvalDetailId(item._id)}
>
{t('dashboard_evaluation:detail')}
</Button>
<PopoverConfirm
type="delete"
Trigger={
<IconButton
aria-label="delete"
size={'mdSquare'}
variant={'whiteDanger'}
icon={<MyIcon name={'delete'} w={4} />}
/>
}
content={t('dashboard_evaluation:comfirm_delete_task')}
onConfirm={() => onDeleteEval({ evalId: item._id })}
/>
</Td>
</Tr>
);
})}
</Tbody>
</Table>
</TableContainer>
{total >= pageSize && (
<Flex mt={4} justifyContent="center">
<Pagination />
</Flex>
)}
</Flex> </Flex>
)} )}
</DashboardContainer> </DashboardContainer>
......
...@@ -79,6 +79,7 @@ import type { ...@@ -79,6 +79,7 @@ import type {
GetApiDatasetPathBody, GetApiDatasetPathBody,
GetApiDatasetPathResponse GetApiDatasetPathResponse
} from '@/pages/api/core/dataset/apiDataset/getPathNames'; } from '@/pages/api/core/dataset/apiDataset/getPathNames';
import type { DelCollectionBody } from '@/pages/api/core/dataset/collection/delete';
/* ======================== dataset ======================= */ /* ======================== dataset ======================= */
export const getDatasets = (data: GetDatasetListBody) => export const getDatasets = (data: GetDatasetListBody) =>
...@@ -216,8 +217,8 @@ export const postCreateDatasetApiDatasetCollection = ( ...@@ -216,8 +217,8 @@ export const postCreateDatasetApiDatasetCollection = (
export const putDatasetCollectionById = (data: UpdateDatasetCollectionParams) => export const putDatasetCollectionById = (data: UpdateDatasetCollectionParams) =>
POST(`/core/dataset/collection/update`, data); POST(`/core/dataset/collection/update`, data);
export const delDatasetCollectionById = (params: { id: string }) => export const delDatasetCollectionById = (params: DelCollectionBody) =>
DELETE(`/core/dataset/collection/delete`, params); POST(`/core/dataset/collection/delete`, params);
export const postLinkCollectionSync = (collectionId: string) => export const postLinkCollectionSync = (collectionId: string) =>
POST<DatasetCollectionSyncResultEnum>(`/core/dataset/collection/sync`, { POST<DatasetCollectionSyncResultEnum>(`/core/dataset/collection/sync`, {
collectionId collectionId
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment