mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-13 14:29:40 +00:00
perf: page ui (#5469)
* perf: page ui * fix: icon * limit chat items * limit chat items
This commit is contained in:
15
.claude/design/web/common/hook/tableMultiple/index.md
Normal file
15
.claude/design/web/common/hook/tableMultiple/index.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# 背景
|
||||
|
||||
一个通用表格多选 hook/component, 它可以实现表格每一行数据的选择,并且在触发一次选择后,会有特殊的按键进行批量操作。
|
||||
|
||||
# 具体描述
|
||||
|
||||
当有一行被选中时,底部会出现悬浮层,可以进行批量操作(具体有哪些批量操作由外部决定)
|
||||
|
||||

|
||||
|
||||
# 预期封装
|
||||
|
||||
1. 选中的值存储在 hook 里,便于判断是否触发底部悬浮层
|
||||
2. 悬浮层外层 Box 在 hook 里,child 由调用组件实现
|
||||
3. FastGPT/packages/web/hooks/useTableMultipleSelect.tsx 在这个文件下实现
|
@@ -3,7 +3,6 @@ title: 'V4.12.1(进行中)'
|
||||
description: 'FastGPT V4.12.1 更新说明'
|
||||
---
|
||||
|
||||
|
||||
## 🚀 新增内容
|
||||
|
||||
1. Prompt 自动生成和优化。
|
||||
@@ -14,6 +13,7 @@ description: 'FastGPT V4.12.1 更新说明'
|
||||
1. 工作流响应优化,主动指定响应值进入历史记录,而不是根据 key 决定。
|
||||
2. 避免工作流中,变量替换导致的死循环或深度递归风险。
|
||||
3. 对话日志导出,固定导出对话详情。
|
||||
4. 分页器 UI 优化。
|
||||
|
||||
## 🐛 修复
|
||||
|
||||
|
@@ -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/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/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/41.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 = {
|
||||
'common/errorFill': () => import('./icons/common/errorFill.svg'),
|
||||
'common/file/move': () => import('./icons/common/file/move.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/folderImport': () => import('./icons/common/folderImport.svg'),
|
||||
'common/fullScreenLight': () => import('./icons/common/fullScreenLight.svg'),
|
||||
@@ -67,6 +68,7 @@ export const iconPaths = {
|
||||
'common/language/China': () => import('./icons/common/language/China.svg'),
|
||||
'common/language/en': () => import('./icons/common/language/en.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/leftArrowLight': () => import('./icons/common/leftArrowLight.svg'),
|
||||
'common/line': () => import('./icons/common/line.svg'),
|
||||
|
@@ -0,0 +1,3 @@
|
||||
<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>
|
After Width: | Height: | Size: 169 B |
@@ -0,0 +1,3 @@
|
||||
<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>
|
After Width: | Height: | Size: 210 B |
@@ -1,4 +1,3 @@
|
||||
<svg viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 8" fill="none">
|
||||
<path d="M1.914 3.99987L4.389 6.47487L3.682 7.18187L0.5 3.99987L3.682 0.817871L4.389 1.52487L1.914 3.99987Z" />
|
||||
</svg>
|
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 192 B |
@@ -1,6 +1,3 @@
|
||||
<svg t="1706532590649" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="3146">
|
||||
<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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 8" fill="none">
|
||||
<path d="M3.08596 3.99987L0.610962 1.52487L1.31796 0.817871L4.49996 3.99987L1.31796 7.18187L0.610962 6.47487L3.08596 3.99987Z" />
|
||||
</svg>
|
Before Width: | Height: | Size: 483 B After Width: | Height: | Size: 210 B |
@@ -1,12 +1,22 @@
|
||||
import { useRef, useState, useCallback, type RefObject, type ReactNode, useMemo } from 'react';
|
||||
import { IconButton, Flex, Box, Input, type BoxProps } from '@chakra-ui/react';
|
||||
import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons';
|
||||
import {
|
||||
useRef,
|
||||
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 { useToast } from './useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import {
|
||||
useBoolean,
|
||||
useLockFn,
|
||||
useCreation,
|
||||
useMemoizedFn,
|
||||
useRequest,
|
||||
useScroll,
|
||||
@@ -14,13 +24,16 @@ import {
|
||||
} from 'ahooks';
|
||||
|
||||
import { type PaginationProps, type PaginationResponse } from '../common/fetch/type';
|
||||
import MyMenu from '../components/common/MyMenu';
|
||||
import { useSystem } from './useSystem';
|
||||
|
||||
const thresholdVal = 200;
|
||||
|
||||
export function usePagination<DataT, ResT = {}>(
|
||||
api: (data: PaginationProps<DataT>) => Promise<PaginationResponse<ResT>>,
|
||||
{
|
||||
pageSize = 10,
|
||||
defaultPageSize = 10,
|
||||
pageSizeOptions: defaultPageSizeOptions,
|
||||
params,
|
||||
defaultRequest = true,
|
||||
type = 'button',
|
||||
@@ -31,7 +44,8 @@ export function usePagination<DataT, ResT = {}>(
|
||||
pollingInterval,
|
||||
pollingWhenHidden = false
|
||||
}: {
|
||||
pageSize?: number;
|
||||
defaultPageSize?: number;
|
||||
pageSizeOptions?: number[];
|
||||
params?: DataT;
|
||||
defaultRequest?: boolean;
|
||||
type?: 'button' | 'scroll';
|
||||
@@ -45,11 +59,18 @@ export function usePagination<DataT, ResT = {}>(
|
||||
}
|
||||
) {
|
||||
const { toast } = useToast();
|
||||
const { isPc } = useSystem();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [isLoading, { setTrue, setFalse }] = useBoolean(false);
|
||||
|
||||
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 [data, setData] = useState<ResT[]>([]);
|
||||
const totalDataLength = useMemo(() => Math.max(total, data.length), [total, data.length]);
|
||||
@@ -119,67 +140,110 @@ export function usePagination<DataT, ResT = {}>(
|
||||
const Pagination = useCallback(() => {
|
||||
const maxPage = Math.ceil(totalDataLength / pageSize);
|
||||
|
||||
return (
|
||||
<Flex alignItems={'center'} justifyContent={'end'}>
|
||||
<IconButton
|
||||
isDisabled={pageNum === 1}
|
||||
icon={<ArrowBackIcon />}
|
||||
aria-label={'left'}
|
||||
size={'smSquare'}
|
||||
isLoading={isLoading}
|
||||
onClick={() => fetchData(pageNum - 1)}
|
||||
/>
|
||||
<Flex mx={2} alignItems={'center'}>
|
||||
<Input
|
||||
defaultValue={pageNum}
|
||||
w={'50px'}
|
||||
h={'30px'}
|
||||
size={'xs'}
|
||||
type={'number'}
|
||||
min={1}
|
||||
max={maxPage}
|
||||
onBlur={(e) => {
|
||||
const val = +e.target.value;
|
||||
if (val === pageNum) return;
|
||||
if (val >= maxPage) {
|
||||
fetchData(maxPage);
|
||||
} else if (val < 1) {
|
||||
fetchData(1);
|
||||
} else {
|
||||
fetchData(+e.target.value);
|
||||
const IconButton = ({
|
||||
icon,
|
||||
isDisabled,
|
||||
onClick,
|
||||
...props
|
||||
}: {
|
||||
icon: IconNameType;
|
||||
isDisabled?: boolean;
|
||||
onClick: () => void;
|
||||
} & FlexProps) => {
|
||||
isDisabled = isDisabled || isLoading;
|
||||
return (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
borderRadius={'full'}
|
||||
w={'24px'}
|
||||
h={'24px'}
|
||||
cursor={'pointer'}
|
||||
bg={'myGray.150'}
|
||||
{...(isDisabled
|
||||
? {
|
||||
opacity: 0.5
|
||||
}
|
||||
}}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Box mx={2}>/</Box>
|
||||
{maxPage}
|
||||
: {
|
||||
onClick
|
||||
})}
|
||||
{...props}
|
||||
>
|
||||
<MyIcon name={icon} w={'6px'} color={'myGray.900'} />
|
||||
</Flex>
|
||||
<IconButton
|
||||
isDisabled={pageNum === maxPage}
|
||||
icon={<ArrowForwardIcon />}
|
||||
aria-label={'left'}
|
||||
size={'sm'}
|
||||
isLoading={isLoading}
|
||||
w={'28px'}
|
||||
h={'28px'}
|
||||
onClick={() => fetchData(pageNum + 1)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
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)}
|
||||
/>
|
||||
{isPc && (
|
||||
<IconButton
|
||||
ml={2}
|
||||
isDisabled={pageNum === maxPage}
|
||||
icon="common/latest_page"
|
||||
onClick={() => fetchData(maxPage)}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{isPc && (
|
||||
<MyMenu
|
||||
menuList={[
|
||||
{
|
||||
label: '',
|
||||
children: pageSizeOptions.map((item) => ({
|
||||
label: `${item}`,
|
||||
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>
|
||||
);
|
||||
}, [isLoading, totalDataLength, pageSize, fetchData, pageNum]);
|
||||
}, [totalDataLength, isPc, pageSize, t, pageNum, pageSizeOptions, isLoading, fetchData]);
|
||||
|
||||
// Scroll pagination
|
||||
const DefaultRef = useRef<HTMLDivElement>(null);
|
||||
@@ -259,6 +323,9 @@ export function usePagination<DataT, ResT = {}>(
|
||||
throttleWait: 100
|
||||
}
|
||||
);
|
||||
useEffect(() => {
|
||||
data.length > 0 && fetchData();
|
||||
}, [pageSize]);
|
||||
|
||||
useRequest(
|
||||
async () => {
|
||||
|
98
packages/web/hooks/useTableMultipleSelect.tsx
Normal file
98
packages/web/hooks/useTableMultipleSelect.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
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 @@
|
||||
"not_yet_introduced": "No Introduction Yet",
|
||||
"open_folder": "Open Folder",
|
||||
"option": "Option",
|
||||
"page": "Page",
|
||||
"page_center": "Page Center",
|
||||
"pay.amount": "Amount",
|
||||
"pay.error_desc": "There was a problem when converting payment routes",
|
||||
@@ -1024,6 +1025,7 @@
|
||||
"search_tool": "Search Tools",
|
||||
"secret_key": "Secret",
|
||||
"secret_tips": "The value will not return plaintext again after saving",
|
||||
"select_count_num": "{{num}} item selected",
|
||||
"select_file_failed": "File Selection Failed",
|
||||
"select_reference_variable": "Select Reference Variable",
|
||||
"select_template": "Select Template",
|
||||
@@ -1238,6 +1240,7 @@
|
||||
"template_market": "Template Market",
|
||||
"textarea_variable_picker_tip": "Enter \"/\" to select a variable",
|
||||
"to_dataset": "To dataset",
|
||||
"total_num": "Total: {{num}}",
|
||||
"ui.textarea.Magnifying": "Magnifying",
|
||||
"un_used": "Unused",
|
||||
"unauth_token": "The certificate has expired, please log in again",
|
||||
|
@@ -14,6 +14,7 @@
|
||||
"backup_dataset_tip": "You can reimport the downloaded csv file when exporting the knowledge base.",
|
||||
"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",
|
||||
"batch_delete": "Delete",
|
||||
"chunk_max_tokens": "max_tokens",
|
||||
"chunk_process_params": "Block processing parameters",
|
||||
"chunk_size": "Block size",
|
||||
@@ -41,6 +42,7 @@
|
||||
"common_dataset_desc": "Building a knowledge base by importing files, web page links, or manual entry",
|
||||
"condition": "condition",
|
||||
"config_sync_schedule": "Configure scheduled synchronization",
|
||||
"confirm_delete_collection": "Confirm to delete {{num }} files?",
|
||||
"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.",
|
||||
"core.dataset.Image collection": "Image dataset",
|
||||
|
@@ -665,10 +665,10 @@
|
||||
"core.module.template.System Plugin": "系统插件",
|
||||
"core.module.template.System input module": "系统输入",
|
||||
"core.module.template.Team app": "团队应用",
|
||||
"core.module.template.all_team_app": "全部",
|
||||
"core.module.template.UnKnow Module": "未知模块",
|
||||
"core.module.template.ai_chat": "AI 对话",
|
||||
"core.module.template.ai_chat_intro": "AI 大模型对话",
|
||||
"core.module.template.all_team_app": "全部",
|
||||
"core.module.template.config_params": "可以配置应用的系统参数",
|
||||
"core.module.template.empty_plugin": "空白插件",
|
||||
"core.module.template.empty_workflow": "空白工作流",
|
||||
@@ -939,6 +939,7 @@
|
||||
"not_yet_introduced": "暂无介绍",
|
||||
"open_folder": "打开文件夹",
|
||||
"option": "选项",
|
||||
"page": "页",
|
||||
"page_center": "页面居中",
|
||||
"pay.amount": "金额",
|
||||
"pay.error_desc": "转换支付途径时出现了问题",
|
||||
@@ -1024,6 +1025,7 @@
|
||||
"search_tool": "搜索工具",
|
||||
"secret_key": "密钥",
|
||||
"secret_tips": "值保存后不会再次明文返回",
|
||||
"select_count_num": "已选 {{num}} 项",
|
||||
"select_file_failed": "选择文件异常",
|
||||
"select_reference_variable": "选择引用变量",
|
||||
"select_template": "选择模板",
|
||||
@@ -1239,6 +1241,7 @@
|
||||
"template_market": "模板市场",
|
||||
"textarea_variable_picker_tip": "输入\"/\"可选择变量",
|
||||
"to_dataset": "前往知识库",
|
||||
"total_num": "总数: {{num}}",
|
||||
"ui.textarea.Magnifying": "放大",
|
||||
"un_used": "未使用",
|
||||
"unauth_token": "凭证已过期,请重新登录",
|
||||
|
@@ -14,6 +14,7 @@
|
||||
"backup_dataset_tip": "可以将导出知识库时,下载的 csv 文件重新导入。",
|
||||
"backup_mode": "备份导入",
|
||||
"backup_template_invalid": "备份文件格式不正确,应该是首列为 q,a,indexes 的 csv 文件",
|
||||
"batch_delete": "批量删除",
|
||||
"chunk_max_tokens": "分块上限",
|
||||
"chunk_process_params": "分块处理参数",
|
||||
"chunk_size": "分块大小",
|
||||
@@ -41,6 +42,7 @@
|
||||
"common_dataset_desc": "通过导入文件、网页链接或手动录入形式构建知识库",
|
||||
"condition": "条件",
|
||||
"config_sync_schedule": "配置定时同步",
|
||||
"confirm_delete_collection": "确认删除 {{num }} 个文件?",
|
||||
"confirm_import_images": "共 {{num}} 张图片 | 确认创建",
|
||||
"confirm_to_rebuild_embedding_tip": "确认为知识库切换索引?\n切换索引是一个非常重量的操作,需要对您知识库内所有数据进行重新索引,时间可能较长,请确保账号内剩余积分充足。\n\n此外,你还需要注意修改选择该知识库的应用,避免它们与其他索引模型知识库混用。",
|
||||
"core.dataset.Image collection": "图片数据集",
|
||||
|
@@ -938,6 +938,7 @@
|
||||
"not_yet_introduced": "暫無介紹",
|
||||
"open_folder": "開啟資料夾",
|
||||
"option": "選項",
|
||||
"page": "頁",
|
||||
"page_center": "頁面置中",
|
||||
"pay.amount": "金額",
|
||||
"pay.error_desc": "轉換支付途徑時出現了問題",
|
||||
@@ -1023,6 +1024,7 @@
|
||||
"search_tool": "搜索工具",
|
||||
"secret_key": "密鑰",
|
||||
"secret_tips": "值保存後不會再次明文返回",
|
||||
"select_count_num": "已選 {{num}} 項",
|
||||
"select_file_failed": "選擇檔案失敗",
|
||||
"select_reference_variable": "選擇引用變數",
|
||||
"select_template": "選擇範本",
|
||||
@@ -1237,6 +1239,7 @@
|
||||
"template_market": "模板市場",
|
||||
"textarea_variable_picker_tip": "輸入「/」以選擇變數",
|
||||
"to_dataset": "前往知識庫",
|
||||
"total_num": "總數: {{num}}",
|
||||
"ui.textarea.Magnifying": "放大",
|
||||
"un_used": "未使用",
|
||||
"unauth_token": "憑證已過期,請重新登入",
|
||||
|
@@ -14,6 +14,7 @@
|
||||
"backup_dataset_tip": "可以將導出知識庫時,下載的 csv 文件重新導入。",
|
||||
"backup_mode": "備份導入",
|
||||
"backup_template_invalid": "備份文件格式不正確,應該是首列為 q,a,indexes 的 csv 文件",
|
||||
"batch_delete": "批量刪除",
|
||||
"chunk_max_tokens": "分塊上限",
|
||||
"chunk_process_params": "分塊處理參數",
|
||||
"chunk_size": "分塊大小",
|
||||
@@ -41,6 +42,7 @@
|
||||
"common_dataset_desc": "通過導入文件、網頁鏈接或手動錄入形式構建知識庫",
|
||||
"condition": "條件",
|
||||
"config_sync_schedule": "設定定時同步",
|
||||
"confirm_delete_collection": "確認刪除 {{num }} 個文件?",
|
||||
"confirm_import_images": "共 {{num}} 張圖片 | 確認創建",
|
||||
"confirm_to_rebuild_embedding_tip": "確定要為資料集切換索引嗎?\n切換索引是一個重要的操作,需要對您資料集內所有資料重新建立索引,可能需要較長時間,請確保帳號內剩餘點數充足。\n\n此外,您還需要注意修改使用此資料集的應用程式,避免與其他索引模型資料集混用。",
|
||||
"core.dataset.Image collection": "圖片數據集",
|
||||
|
@@ -549,25 +549,37 @@ const Checkbox = checkBoxMultiStyle({
|
||||
sizes: {
|
||||
sm: checkBoxPart({
|
||||
control: {
|
||||
width: '18px',
|
||||
height: '18px',
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
borderWidth: '2px'
|
||||
},
|
||||
icon: {
|
||||
fontSize: '10px'
|
||||
}
|
||||
}),
|
||||
md: checkBoxPart({
|
||||
control: {
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
width: '18px',
|
||||
height: '18px',
|
||||
borderWidth: '2px'
|
||||
},
|
||||
icon: {
|
||||
fontSize: '12px'
|
||||
}
|
||||
}),
|
||||
lg: checkBoxPart({
|
||||
control: {
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
borderWidth: '2px'
|
||||
},
|
||||
icon: {
|
||||
fontSize: '14px'
|
||||
}
|
||||
})
|
||||
},
|
||||
defaultProps: {
|
||||
size: 'sm'
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -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 />
|
||||
|
@@ -27,9 +27,10 @@ const BillAndInvoice = () => {
|
||||
|
||||
return (
|
||||
<AccountContainer>
|
||||
<Box p={['1rem', '2rem']}>
|
||||
<Flex h={'100%'} flexDirection={'column'} p={['2', '4']}>
|
||||
<Flex justifyContent={'space-between'} alignItems={'center'} pb={'0.75rem'}>
|
||||
<FillRowTabs
|
||||
py={1}
|
||||
list={[
|
||||
{ label: t('account_bill:bill_record'), value: InvoiceTabEnum.bill },
|
||||
{
|
||||
@@ -61,7 +62,7 @@ const BillAndInvoice = () => {
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
<Box h={'100%'}>
|
||||
<Box flex={'1 0 0'} h={0} overflow={'auto'}>
|
||||
{invoiceTab === InvoiceTabEnum.bill && <BillTable />}
|
||||
{invoiceTab === InvoiceTabEnum.invoice && <InvoiceTable />}
|
||||
{invoiceTab === InvoiceTabEnum.invoiceHeader && <InvoiceHeaderForm />}
|
||||
@@ -73,7 +74,7 @@ const BillAndInvoice = () => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
</AccountContainer>
|
||||
);
|
||||
};
|
||||
|
@@ -44,7 +44,7 @@ const InformTable = () => {
|
||||
getData,
|
||||
pageNum
|
||||
} = usePagination(getInforms, {
|
||||
pageSize: 20
|
||||
defaultPageSize: 20
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -147,7 +147,7 @@ const InformTable = () => {
|
||||
)}
|
||||
|
||||
{total > pageSize && (
|
||||
<Flex w="100%" mt={4} px={[3, 8]} justifyContent="flex-end">
|
||||
<Flex mt={4} justifyContent="center">
|
||||
<Pagination />
|
||||
</Flex>
|
||||
)}
|
||||
|
@@ -43,7 +43,7 @@ const Promotion = () => {
|
||||
pageSize,
|
||||
Pagination
|
||||
} = usePagination(getPromotionRecords, {
|
||||
pageSize: 20
|
||||
defaultPageSize: 20
|
||||
});
|
||||
|
||||
const { data: { invitedAmount = 0, earningsAmount = 0 } = {} } = useQuery(
|
||||
@@ -131,7 +131,7 @@ const Promotion = () => {
|
||||
<EmptyTip text={t('account_promotion:no_invite_records')}></EmptyTip>
|
||||
)}
|
||||
{total > pageSize && (
|
||||
<Flex mt={4} justifyContent={'flex-end'}>
|
||||
<Flex mt={3} justifyContent={'center'}>
|
||||
<Pagination />
|
||||
</Flex>
|
||||
)}
|
||||
|
@@ -7,6 +7,7 @@ import { MongoAppChatLog } from '@fastgpt/service/core/app/logs/chatLogsSchema';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import type { ChatSchemaType } from '@fastgpt/global/core/chat/type';
|
||||
import { surrenderProcess } from '@fastgpt/service/common/system/tools';
|
||||
|
||||
export type SyncAppChatLogQuery = {};
|
||||
|
||||
@@ -63,7 +64,9 @@ async function handler(
|
||||
|
||||
async function processChatRecord(chat: ChatSchemaType) {
|
||||
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 errorCount = 0;
|
||||
@@ -73,6 +76,8 @@ async function processChatRecord(chat: ChatSchemaType) {
|
||||
let totalResponseTime = 0;
|
||||
|
||||
for (const item of chatItems) {
|
||||
await surrenderProcess();
|
||||
|
||||
const itemData = item as any;
|
||||
|
||||
if (itemData.userGoodFeedback && itemData.userGoodFeedback.trim() !== '') {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import type { NextApiRequest } from 'next';
|
||||
import { findCollectionAndChild } from '@fastgpt/service/core/dataset/collection/utils';
|
||||
import { delCollection } from '@fastgpt/service/core/dataset/collection/controller';
|
||||
import { authDatasetCollection } from '@fastgpt/service/support/permission/dataset/auth';
|
||||
@@ -9,28 +8,50 @@ import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { addAuditLog } from '@fastgpt/service/support/user/audit/util';
|
||||
import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants';
|
||||
import { getI18nDatasetType } from '@fastgpt/service/support/user/audit/util';
|
||||
import type { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
|
||||
async function handler(req: NextApiRequest) {
|
||||
const { id: collectionId } = req.query as { id: string };
|
||||
export type DelCollectionBody = {
|
||||
collectionIds: string[];
|
||||
};
|
||||
|
||||
if (!collectionId) {
|
||||
async function handler(req: ApiRequestProps<DelCollectionBody>) {
|
||||
const { collectionIds } = req.body;
|
||||
|
||||
if (!collectionIds) {
|
||||
return Promise.reject(CommonErrEnum.missingParams);
|
||||
}
|
||||
|
||||
const { teamId, collection, tmbId } = await authDatasetCollection({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
collectionId,
|
||||
per: WritePermissionVal
|
||||
});
|
||||
const [{ teamId, collection, tmbId }] = await Promise.all(
|
||||
collectionIds.map(async (collectionId) => {
|
||||
return await authDatasetCollection({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
collectionId,
|
||||
per: WritePermissionVal
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
// find all delete id
|
||||
const collections = await findCollectionAndChild({
|
||||
teamId,
|
||||
datasetId: collection.datasetId,
|
||||
collectionId,
|
||||
fields: '_id teamId datasetId fileId metadata'
|
||||
const collections = await Promise.all(
|
||||
collectionIds.map(async (collectionId) => {
|
||||
return await findCollectionAndChild({
|
||||
teamId,
|
||||
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
|
||||
|
@@ -23,7 +23,7 @@ async function handler(req: NextApiRequest) {
|
||||
simple = false
|
||||
} = req.body as any;
|
||||
searchText = searchText?.replace(/'/g, '');
|
||||
pageSize = Math.min(pageSize, 30);
|
||||
pageSize = Math.min(pageSize, 100);
|
||||
|
||||
// auth dataset and get my role
|
||||
const { teamId, permission } = await authDataset({
|
||||
|
@@ -28,7 +28,7 @@ async function handler(
|
||||
simple = false
|
||||
} = req.body as GetDatasetCollectionsProps;
|
||||
let { pageSize, offset } = parsePaginationRequest(req);
|
||||
pageSize = Math.min(pageSize, 30);
|
||||
pageSize = Math.min(pageSize, 100);
|
||||
searchText = searchText?.replace(/'/g, '');
|
||||
|
||||
// auth dataset and get my role
|
||||
|
@@ -46,9 +46,11 @@ const Evaluation = () => {
|
||||
const {
|
||||
data: evaluationList,
|
||||
Pagination,
|
||||
getData: fetchData
|
||||
getData: fetchData,
|
||||
total,
|
||||
pageSize
|
||||
} = usePagination(getEvaluationList, {
|
||||
pageSize: 20,
|
||||
defaultPageSize: 20,
|
||||
pollingInterval,
|
||||
pollingWhenHidden: true,
|
||||
params: {
|
||||
@@ -185,90 +187,90 @@ const Evaluation = () => {
|
||||
<Flex h={'full'} bg={'white'} p={6} flexDirection="column">
|
||||
{renderHeader(MenuIcon)}
|
||||
|
||||
<MyBox flex={'1 0 0'} overflow="auto">
|
||||
<TableContainer mt={3} fontSize={'sm'}>
|
||||
<Table variant={'simple'}>
|
||||
<Thead>
|
||||
<Tr color={'myGray.600'}>
|
||||
<Th fontWeight={'400'}>{t('dashboard_evaluation:Task_name')}</Th>
|
||||
<Th fontWeight={'400'}>{t('dashboard_evaluation:Progress')}</Th>
|
||||
<Th fontWeight={'400'}>{t('dashboard_evaluation:Executor')}</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:Overall_score')}</Th>
|
||||
<Th fontWeight={'400'}>{t('dashboard_evaluation:Action')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
<Tr h={'5px'} />
|
||||
{evaluationList.map((item) => {
|
||||
return (
|
||||
<Tr key={item._id}>
|
||||
<Td fontWeight={'medium'} color={'myGray.900'}>
|
||||
{item.name}
|
||||
</Td>
|
||||
<Td>{renderProgress(item)}</Td>
|
||||
<Td>
|
||||
<Flex alignItems={'center'} gap={1.5}>
|
||||
<Avatar
|
||||
src={item.executorAvatar}
|
||||
w={5}
|
||||
borderRadius={'full'}
|
||||
border={'1px solid'}
|
||||
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 })}
|
||||
<TableContainer mt={3} fontSize={'sm'} flex={'1 0 0'} overflowY="auto">
|
||||
<Table variant={'simple'}>
|
||||
<Thead>
|
||||
<Tr color={'myGray.600'}>
|
||||
<Th fontWeight={'400'}>{t('dashboard_evaluation:Task_name')}</Th>
|
||||
<Th fontWeight={'400'}>{t('dashboard_evaluation:Progress')}</Th>
|
||||
<Th fontWeight={'400'}>{t('dashboard_evaluation:Executor')}</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:Overall_score')}</Th>
|
||||
<Th fontWeight={'400'}>{t('dashboard_evaluation:Action')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
<Tr h={'5px'} />
|
||||
{evaluationList.map((item) => {
|
||||
return (
|
||||
<Tr key={item._id}>
|
||||
<Td fontWeight={'medium'} color={'myGray.900'}>
|
||||
{item.name}
|
||||
</Td>
|
||||
<Td>{renderProgress(item)}</Td>
|
||||
<Td>
|
||||
<Flex alignItems={'center'} gap={1.5}>
|
||||
<Avatar
|
||||
src={item.executorAvatar}
|
||||
w={5}
|
||||
borderRadius={'full'}
|
||||
border={'1px solid'}
|
||||
borderColor={'myGray.200'}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</MyBox>
|
||||
<Flex mt={4} justifyContent="center">
|
||||
<Pagination />
|
||||
</Flex>
|
||||
<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>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
{total >= pageSize && (
|
||||
<Flex mt={4} justifyContent="center">
|
||||
<Pagination />
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
</DashboardContainer>
|
||||
|
@@ -79,6 +79,7 @@ import type {
|
||||
GetApiDatasetPathBody,
|
||||
GetApiDatasetPathResponse
|
||||
} from '@/pages/api/core/dataset/apiDataset/getPathNames';
|
||||
import type { DelCollectionBody } from '@/pages/api/core/dataset/collection/delete';
|
||||
|
||||
/* ======================== dataset ======================= */
|
||||
export const getDatasets = (data: GetDatasetListBody) =>
|
||||
@@ -216,8 +217,8 @@ export const postCreateDatasetApiDatasetCollection = (
|
||||
|
||||
export const putDatasetCollectionById = (data: UpdateDatasetCollectionParams) =>
|
||||
POST(`/core/dataset/collection/update`, data);
|
||||
export const delDatasetCollectionById = (params: { id: string }) =>
|
||||
DELETE(`/core/dataset/collection/delete`, params);
|
||||
export const delDatasetCollectionById = (params: DelCollectionBody) =>
|
||||
POST(`/core/dataset/collection/delete`, params);
|
||||
export const postLinkCollectionSync = (collectionId: string) =>
|
||||
POST<DatasetCollectionSyncResultEnum>(`/core/dataset/collection/sync`, {
|
||||
collectionId
|
||||
|
Reference in New Issue
Block a user