perf: usages list;perf: move components (#3654)

* perf: usages list

* team sub plan load

* perf: usage dashboard code

* perf: dashboard ui

* perf: move components
This commit is contained in:
Archer 2025-01-23 17:29:39 +08:00 committed by GitHub
parent 0c05add8b2
commit 34b510cba1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
271 changed files with 611 additions and 921 deletions

View File

@ -27,3 +27,5 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4820' \
## 完整更新内容 ## 完整更新内容
1. 新增 - 可视化模型配置。预设超过 100 个模型,方便进行模型配置。 1. 新增 - 可视化模型配置。预设超过 100 个模型,方便进行模型配置。
2. 新增 - 使用记录导出和仪表盘。
3. 优化 - 页面组件抽离,减少页面组件路由。

View File

@ -2,12 +2,22 @@ import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils'; import { i18nT } from '../../../../web/i18n/utils';
/* team: 503000 */ /* team: 503000 */
export enum UserErrEnum { export enum UserErrEnum {
notUser = 'notUser',
userExist = 'userExist',
unAuthRole = 'unAuthRole', unAuthRole = 'unAuthRole',
account_psw_error = 'account_psw_error', account_psw_error = 'account_psw_error',
balanceNotEnough = 'balanceNotEnough', balanceNotEnough = 'balanceNotEnough',
unAuthSso = 'unAuthSso' unAuthSso = 'unAuthSso'
} }
const errList = [ const errList = [
{
statusText: UserErrEnum.notUser,
message: i18nT('common:code_error.account_not_found')
},
{
statusText: UserErrEnum.userExist,
message: i18nT('common:code_error.account_exist')
},
{ {
statusText: UserErrEnum.account_psw_error, statusText: UserErrEnum.account_psw_error,
message: i18nT('common:code_error.account_error') message: i18nT('common:code_error.account_error')

View File

@ -6,21 +6,20 @@ export type CreateTrainingUsageProps = {
datasetId: string; datasetId: string;
}; };
export type GetTotalPointsProps = {
dateStart: Date;
dateEnd: Date;
teamMemberIds: string[];
sources: UsageSourceEnum[];
unit: 'day' | 'week' | 'month';
};
export type GetUsageProps = { export type GetUsageProps = {
dateStart: Date; dateStart: Date;
dateEnd: Date; dateEnd: Date;
sources?: UsageSourceEnum[]; sources?: UsageSourceEnum[];
teamMemberIds?: string[]; teamMemberIds?: string[];
projectName?: string; projectName?: string;
isSelectAllTmb?: boolean; };
export type GetUsageDashboardProps = GetUsageProps & {
unit: 'day' | 'month';
};
export type GetUsageDashboardResponseItem = {
date: Date;
totalPoints: number;
}; };
export type ConcatUsageProps = UsageListItemCountType & { export type ConcatUsageProps = UsageListItemCountType & {

View File

@ -9,7 +9,7 @@ import { jsonRes } from '../response';
// unit: times/s // unit: times/s
// how to use? // how to use?
// export default NextAPI(useQPSLimit(10), handler); // limit 10 times per second for a ip // export default NextAPI(useQPSLimit(10), handler); // limit 10 times per second for a ip
export function useReqFrequencyLimit(seconds: number, limit: number, force = false) { export function useIPFrequencyLimit(seconds: number, limit: number, force = false) {
return async (req: ApiRequestProps, res: NextApiResponse) => { return async (req: ApiRequestProps, res: NextApiResponse) => {
const ip = requestIp.getClientIp(req); const ip = requestIp.getClientIp(req);
if (!ip || (process.env.USE_IP_LIMIT !== 'true' && !force)) { if (!ip || (process.env.USE_IP_LIMIT !== 'true' && !force)) {

View File

@ -348,119 +348,6 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
}; };
} }
const searchResults = (
await Promise.all(
datasetIds.map(async (id) => {
return MongoDatasetData.aggregate(
[
{
$match: {
teamId: new Types.ObjectId(teamId),
datasetId: new Types.ObjectId(id),
$text: { $search: jiebaSplit({ text: query }) },
...(filterCollectionIdList
? {
collectionId: {
$in: filterCollectionIdList.map((id) => new Types.ObjectId(id))
}
}
: {}),
...(forbidCollectionIdList && forbidCollectionIdList.length > 0
? {
collectionId: {
$nin: forbidCollectionIdList.map((id) => new Types.ObjectId(id))
}
}
: {})
}
},
{
$sort: {
score: { $meta: 'textScore' }
}
},
{
$limit: limit
},
{
$project: {
_id: 1,
datasetId: 1,
collectionId: 1,
updateTime: 1,
q: 1,
a: 1,
chunkIndex: 1,
score: { $meta: 'textScore' }
}
}
],
{
...readFromSecondary
}
);
})
)
).flat() as (DatasetDataSchemaType & { score: number })[];
// Get data and collections
const collections = await MongoDatasetCollection.find(
{
_id: { $in: searchResults.map((item) => item.collectionId) }
},
'_id name fileId rawLink externalFileId externalFileUrl',
{ ...readFromSecondary }
).lean();
return {
fullTextRecallResults: searchResults
.map((data, index) => {
const collection = collections.find(
(col) => String(col._id) === String(data.collectionId)
);
if (!collection) {
console.log('Collection is not found', data);
return;
}
return {
id: String(data._id),
datasetId: String(data.datasetId),
collectionId: String(data.collectionId),
updateTime: data.updateTime,
q: data.q,
a: data.a,
chunkIndex: data.chunkIndex,
indexes: data.indexes,
...getCollectionSourceData(collection),
score: [{ type: SearchScoreTypeEnum.fullText, value: data.score ?? 0, index }]
};
})
.filter(Boolean) as SearchDataResponseItemType[],
tokenLen: 0
};
};
const fullTextRecall2 = async ({
query,
limit,
filterCollectionIdList,
forbidCollectionIdList
}: {
query: string;
limit: number;
filterCollectionIdList?: string[];
forbidCollectionIdList: string[];
}): Promise<{
fullTextRecallResults: SearchDataResponseItemType[];
tokenLen: number;
}> => {
if (limit === 0) {
return {
fullTextRecallResults: [],
tokenLen: 0
};
}
const searchResults = ( const searchResults = (
await Promise.all( await Promise.all(
datasetIds.map(async (id) => { datasetIds.map(async (id) => {
@ -637,7 +524,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
filterCollectionIdList filterCollectionIdList
}), }),
// FullText tmp // FullText tmp
fullTextRecall2({ fullTextRecall({
query, query,
limit: fullTextLimit, limit: fullTextLimit,
filterCollectionIdList, filterCollectionIdList,

View File

@ -61,7 +61,8 @@ const UsageSchema = new Schema({
}); });
try { try {
UsageSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 }); UsageSchema.index({ teamId: 1, time: 1, tmbId: 1, source: 1 });
UsageSchema.index({ teamId: 1, time: 1, appName: 1 });
// timer task. clear dead team // timer task. clear dead team
// UsageSchema.index({ teamId: 1, time: -1 }); // UsageSchema.index({ teamId: 1, time: -1 });

View File

@ -101,7 +101,7 @@ const DateRangePicker = ({
date.to = date.from; date.to = date.from;
} }
setRange(date); setRange(date);
onChange && onChange(date); onChange?.(date);
}} }}
footer={ footer={
<Flex justifyContent={'flex-end'}> <Flex justifyContent={'flex-end'}>
@ -116,7 +116,7 @@ const DateRangePicker = ({
<Button <Button
size={'sm'} size={'sm'}
onClick={() => { onClick={() => {
onSuccess && onSuccess(range || defaultDate); onSuccess?.(range || defaultDate);
setShowSelected(false); setShowSelected(false);
}} }}
> >

View File

@ -10,29 +10,30 @@ import {
MenuList, MenuList,
useDisclosure useDisclosure
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React, { useMemo, useRef } from 'react'; import React, { useCallback, useMemo, useRef, useState } from 'react';
import MyTag from '../Tag/index'; import MyTag from '../Tag/index';
import MyIcon from '../Icon'; import MyIcon from '../Icon';
import MyAvatar from '../Avatar'; import MyAvatar from '../Avatar';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useScrollPagination } from '../../../hooks/useScrollPagination'; import { useScrollPagination } from '../../../hooks/useScrollPagination';
import MyDivider from '../MyDivider';
export type SelectProps<T = any> = { export type SelectProps<T = any> = {
value: T[];
placeholder?: string;
list: { list: {
icon?: string; icon?: string;
label: string | React.ReactNode; label: string | React.ReactNode;
value: T; value: T;
}[]; }[];
value: T[];
isSelectAll: boolean;
setIsSelectAll: React.Dispatch<React.SetStateAction<boolean>>;
placeholder?: string;
maxH?: number; maxH?: number;
itemWrap?: boolean; itemWrap?: boolean;
onSelect: (val: T[]) => void; onSelect: (val: T[]) => void;
closeable?: boolean; closeable?: boolean;
showCheckedIcon?: boolean;
ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData']; ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
isSelectAll?: boolean;
setIsSelectAll?: React.Dispatch<React.SetStateAction<boolean>>;
} & Omit<ButtonProps, 'onSelect'>; } & Omit<ButtonProps, 'onSelect'>;
const MultipleSelect = <T = any,>({ const MultipleSelect = <T = any,>({
@ -42,7 +43,6 @@ const MultipleSelect = <T = any,>({
maxH = 400, maxH = 400,
onSelect, onSelect,
closeable = false, closeable = false,
showCheckedIcon = true,
itemWrap = true, itemWrap = true,
ScrollData, ScrollData,
isSelectAll, isSelectAll,
@ -65,79 +65,66 @@ const MultipleSelect = <T = any,>({
} }
}; };
const onclickItem = (val: T) => { const onclickItem = useCallback(
if (value.includes(val)) { (val: T) => {
onSelect(value.filter((i) => i !== val)); // 全选状态下value 实际上上空。
} else { if (isSelectAll) {
onSelect([...value, val]); onSelect(list.map((item) => item.value).filter((i) => i !== val));
} setIsSelectAll(false);
}; return;
}
const onSelectAll = () => { if (value.includes(val)) {
if (!setIsSelectAll) { onSelect(value.filter((i) => i !== val));
onSelect(value.length === list.length ? [] : list.map((item) => item.value)); } else {
return; onSelect([...value, val]);
} }
},
[value, isSelectAll, onSelect, setIsSelectAll]
);
const onSelectAll = useCallback(() => {
const hasSelected = isSelectAll || value.length > 0;
onSelect(hasSelected ? [] : list.map((item) => item.value));
if (isSelectAll) {
onSelect([]);
}
setIsSelectAll((state) => !state); setIsSelectAll((state) => !state);
}; }, [value, list, setIsSelectAll, onSelect]);
const ListRender = useMemo(() => { const ListRender = useMemo(() => {
return ( return (
<> <>
{list.map((item, i) => ( {list.map((item, i) => {
<MenuItem const isSelected = isSelectAll || value.includes(item.value);
key={i} return (
{...menuItemStyles} <MenuItem
{...((isSelectAll && !value.includes(item.value)) || key={i}
(!isSelectAll && value.includes(item.value)) {...menuItemStyles}
? { {...(isSelected
color: 'primary.600' ? {
} color: 'primary.600'
: { }
color: 'myGray.900' : {
})} color: 'myGray.900'
onClick={(e) => { })}
e.stopPropagation(); onClick={(e) => {
e.preventDefault(); e.stopPropagation();
onclickItem(item.value); e.preventDefault();
}} onclickItem(item.value);
whiteSpace={'pre-wrap'} }}
fontSize={'sm'} whiteSpace={'pre-wrap'}
gap={2} fontSize={'sm'}
> gap={2}
{!showCheckedIcon && ( >
<Checkbox <Checkbox isChecked={isSelected} />
isChecked={ {item.icon && <MyAvatar src={item.icon} w={'1rem'} borderRadius={'0'} />}
(isSelectAll && !value.includes(item.value)) || <Box flex={'1 0 0'}>{item.label}</Box>
(!isSelectAll && value.includes(item.value)) </MenuItem>
} );
/> })}
)}
{item.icon && <MyAvatar src={item.icon} w={'1rem'} borderRadius={'0'} />}
<Box flex={'1 0 0'}>{item.label}</Box>
{showCheckedIcon && (
<Box w={'0.8rem'} lineHeight={1}>
{(isSelectAll && !value.includes(item.value)) ||
(!isSelectAll && value.includes(item.value) && (
<MyIcon name={'price/right'} w={'1rem'} />
))}
</Box>
)}
</MenuItem>
))}
</> </>
); );
}, [value, list, isSelectAll]); }, [value, list, isSelectAll]);
const isAllSelected = useMemo(
() => (isSelectAll && value.length === 0) || (!isSelectAll && value.length === list.length),
[isSelectAll, value, list]
);
return ( return (
<Box> <Box>
<Menu <Menu
@ -186,44 +173,43 @@ const MultipleSelect = <T = any,>({
overflow={'hidden'} overflow={'hidden'}
flex={1} flex={1}
> >
{isAllSelected ? ( {isSelectAll ? (
<Box fontSize={'mini'} color={'myGray.900'}> <Box fontSize={'mini'} color={'myGray.900'}>
{t('common:common.All')} {t('common:common.All')}
</Box> </Box>
) : ( ) : (
(isSelectAll list
? list.filter((item) => !value.includes(item.value)) .filter((item) => value.includes(item.value))
: list.filter((item) => value.includes(item.value)) .map((item, i) => (
).map((item, i) => ( <MyTag
<MyTag className="tag-icon"
className="tag-icon" key={i}
key={i} bg={'primary.100'}
bg={'primary.100'} color={'primary.700'}
color={'primary.700'} type={'fill'}
type={'fill'} borderRadius={'full'}
borderRadius={'full'} px={2}
px={2} py={0.5}
py={0.5} flexShrink={0}
flexShrink={0} >
> {item.label}
{item.label} {closeable && (
{closeable && ( <MyIcon
<MyIcon name={'common/closeLight'}
name={'common/closeLight'} ml={1}
ml={1} w="0.8rem"
w="0.8rem" cursor={'pointer'}
cursor={'pointer'} _hover={{
_hover={{ color: 'red.500'
color: 'red.500' }}
}} onClick={(e) => {
onClick={(e) => { e.stopPropagation();
e.stopPropagation(); onclickItem(item.value);
onclickItem(item.value); }}
}} />
/> )}
)} </MyTag>
</MyTag> ))
))
)} )}
</Flex> </Flex>
<MyIcon name={'core/chat/chevronDown'} color={'myGray.600'} w={4} h={4} /> <MyIcon name={'core/chat/chevronDown'} color={'myGray.600'} w={4} h={4} />
@ -245,7 +231,7 @@ const MultipleSelect = <T = any,>({
> >
<MenuItem <MenuItem
{...menuItemStyles} {...menuItemStyles}
color={isAllSelected ? 'primary.600' : 'myGray.900'} color={isSelectAll ? 'primary.600' : 'myGray.900'}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
@ -256,15 +242,12 @@ const MultipleSelect = <T = any,>({
gap={2} gap={2}
mb={1} mb={1}
> >
{!showCheckedIcon && <Checkbox isChecked={isAllSelected} />} <Checkbox isChecked={isSelectAll} />
<Box flex={'1 0 0'}>{t('common:common.All')}</Box> <Box flex={'1 0 0'}>{t('common:common.All')}</Box>
{showCheckedIcon && (
<Box w={'0.8rem'} lineHeight={1}>
{isAllSelected && <MyIcon name={'price/right'} w={'1rem'} />}
</Box>
)}
</MenuItem> </MenuItem>
<MyDivider my={1} />
{ScrollData ? <ScrollData>{ListRender}</ScrollData> : ListRender} {ScrollData ? <ScrollData>{ListRender}</ScrollData> : ListRender}
</MenuList> </MenuList>
</Menu> </Menu>
@ -273,3 +256,9 @@ const MultipleSelect = <T = any,>({
}; };
export default MultipleSelect; export default MultipleSelect;
export const useMultipleSelect = <T = any,>(defaultValue: T[] = [], defaultSelectAll = false) => {
const [value, setValue] = useState<T[]>(defaultValue);
const [isSelectAll, setIsSelectAll] = useState<boolean>(defaultSelectAll);
return { value, setValue, isSelectAll, setIsSelectAll };
};

View File

@ -0,0 +1,24 @@
import { useCopyData } from '../../../hooks/useCopyData';
import React from 'react';
import MyTooltip from '../MyTooltip';
import { useTranslation } from 'next-i18next';
import { Box, BoxProps } from '@chakra-ui/react';
const CopyBox = ({
value,
children,
...props
}: { value: string; children: React.ReactNode } & BoxProps) => {
const { copyData } = useCopyData();
const { t } = useTranslation();
return (
<MyTooltip label={t('common:click_to_copy')}>
<Box cursor={'pointer'} onClick={() => copyData(value)} {...props}>
{children}
</Box>
</MyTooltip>
);
};
export default CopyBox;

View File

@ -1,7 +1,7 @@
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useToast } from '@fastgpt/web/hooks/useToast'; import { useToast } from './useToast';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { hasHttps } from '@fastgpt/web/common/system/utils'; import { hasHttps } from '../common/system/utils';
import { isProduction } from '@fastgpt/global/common/system/constants'; import { isProduction } from '@fastgpt/global/common/system/constants';
/** /**

View File

@ -9,7 +9,10 @@
"details": "Details", "details": "Details",
"dingtalk": "DingTalk", "dingtalk": "DingTalk",
"duration_seconds": "Duration (seconds)", "duration_seconds": "Duration (seconds)",
"every_day": "Day",
"every_month": "Moon",
"export_confirm": "Export confirmation", "export_confirm": "Export confirmation",
"export_confirm_tip": "There are currently {{total}} usage records in total. Are you sure to export?",
"feishu": "Feishu", "feishu": "Feishu",
"generation_time": "Generation time", "generation_time": "Generation time",
"input_token_length": "input tokens", "input_token_length": "input tokens",
@ -26,7 +29,6 @@
"select_member_and_source_first": "Please select members and types first", "select_member_and_source_first": "Please select members and types first",
"share": "Share Link", "share": "Share Link",
"source": "source", "source": "source",
"start_export": "Export started",
"text_length": "text length", "text_length": "text length",
"token_length": "token length", "token_length": "token length",
"total_points": "AI points consumption", "total_points": "AI points consumption",

View File

@ -37,9 +37,12 @@
"chose_condition": "Choose Condition", "chose_condition": "Choose Condition",
"chosen": "Chosen", "chosen": "Chosen",
"classification": "Classification", "classification": "Classification",
"click_to_copy": "Click to copy",
"click_to_resume": "Click to Resume", "click_to_resume": "Click to Resume",
"code_editor": "Code Editor", "code_editor": "Code Editor",
"code_error.account_error": "Incorrect account name or password", "code_error.account_error": "Incorrect account name or password",
"code_error.account_exist": "Account has been registered",
"code_error.account_not_found": "User is not registered",
"code_error.app_error.invalid_app_type": "Invalid Application Type", "code_error.app_error.invalid_app_type": "Invalid Application Type",
"code_error.app_error.invalid_owner": "Unauthorized Application Owner", "code_error.app_error.invalid_owner": "Unauthorized Application Owner",
"code_error.app_error.not_exist": "Application Does Not Exist", "code_error.app_error.not_exist": "Application Does Not Exist",
@ -932,14 +935,14 @@
"model_doubao": "Doubao", "model_doubao": "Doubao",
"model_ernie": "Ernie", "model_ernie": "Ernie",
"model_hunyuan": "Hunyuan", "model_hunyuan": "Hunyuan",
"model_intern": "Intern",
"model_moka": "Moka-AI",
"model_moonshot": "Moonshot", "model_moonshot": "Moonshot",
"model_other": "Other", "model_other": "Other",
"model_qwen": "Qwen", "model_qwen": "Qwen",
"model_sparkdesk": "SprkDesk", "model_sparkdesk": "SprkDesk",
"model_stepfun": "StepFun", "model_stepfun": "StepFun",
"model_yi": "Yi", "model_yi": "Yi",
"model_intern": "Intern",
"model_moka": "Moka-AI",
"move.confirm": "Confirm move", "move.confirm": "Confirm move",
"navbar.Account": "Account", "navbar.Account": "Account",
"navbar.Chat": "Chat", "navbar.Chat": "Chat",

View File

@ -9,10 +9,11 @@
"details": "详情", "details": "详情",
"dingtalk": "钉钉", "dingtalk": "钉钉",
"duration_seconds": "时长(秒)", "duration_seconds": "时长(秒)",
"every_day": "天", "every_day": "天",
"every_month": "月", "every_month": "月",
"every_week": "每周", "every_week": "每周",
"export_confirm": "导出确认", "export_confirm": "导出确认",
"export_confirm_tip": "当前共 {{total}} 条使用记录,确认导出?",
"export_success": "导出成功", "export_success": "导出成功",
"feishu": "飞书", "feishu": "飞书",
"generation_time": "生成时间", "generation_time": "生成时间",
@ -30,7 +31,6 @@
"select_member_and_source_first": "请先选中成员和类型", "select_member_and_source_first": "请先选中成员和类型",
"share": "分享链接", "share": "分享链接",
"source": "来源", "source": "来源",
"start_export": "已开始导出",
"text_length": "文本长度", "text_length": "文本长度",
"token_length": "token 长度", "token_length": "token 长度",
"total_points": "AI 积分消耗", "total_points": "AI 积分消耗",

View File

@ -41,9 +41,12 @@
"chose_condition": "选择条件", "chose_condition": "选择条件",
"chosen": "已选", "chosen": "已选",
"classification": "分类", "classification": "分类",
"click_to_copy": "点击复制",
"click_to_resume": "点击恢复", "click_to_resume": "点击恢复",
"code_editor": "代码编辑", "code_editor": "代码编辑",
"code_error.account_error": "账号名或密码错误", "code_error.account_error": "账号名或密码错误",
"code_error.account_exist": "账号已注册",
"code_error.account_not_found": "用户未注册",
"code_error.app_error.invalid_app_type": "错误的应用类型", "code_error.app_error.invalid_app_type": "错误的应用类型",
"code_error.app_error.invalid_owner": "非法的应用所有者", "code_error.app_error.invalid_owner": "非法的应用所有者",
"code_error.app_error.not_exist": "应用不存在", "code_error.app_error.not_exist": "应用不存在",
@ -935,14 +938,14 @@
"model_doubao": "豆包", "model_doubao": "豆包",
"model_ernie": "文心一言", "model_ernie": "文心一言",
"model_hunyuan": "腾讯混元", "model_hunyuan": "腾讯混元",
"model_intern": "书生",
"model_moka": "Moka-AI",
"model_moonshot": "月之暗面", "model_moonshot": "月之暗面",
"model_other": "其他", "model_other": "其他",
"model_qwen": "阿里千问", "model_qwen": "阿里千问",
"model_sparkdesk": "讯飞星火", "model_sparkdesk": "讯飞星火",
"model_stepfun": "阶跃星辰", "model_stepfun": "阶跃星辰",
"model_yi": "零一万物", "model_yi": "零一万物",
"model_intern": "书生",
"model_moka": "Moka-AI",
"move.confirm": "确认移动", "move.confirm": "确认移动",
"navbar.Account": "账号", "navbar.Account": "账号",
"navbar.Chat": "聊天", "navbar.Chat": "聊天",

View File

@ -9,7 +9,10 @@
"details": "詳情", "details": "詳情",
"dingtalk": "釘釘", "dingtalk": "釘釘",
"duration_seconds": "時長(秒)", "duration_seconds": "時長(秒)",
"every_day": "天",
"every_month": "月",
"export_confirm": "導出確認", "export_confirm": "導出確認",
"export_confirm_tip": "當前共 {{total}} 筆使用記錄,確認導出?",
"feishu": "飛書", "feishu": "飛書",
"generation_time": "生成時間", "generation_time": "生成時間",
"input_token_length": "輸入 tokens", "input_token_length": "輸入 tokens",
@ -26,7 +29,6 @@
"select_member_and_source_first": "請先選取成員和類型", "select_member_and_source_first": "請先選取成員和類型",
"share": "分享連結", "share": "分享連結",
"source": "來源", "source": "來源",
"start_export": "已開始匯出",
"text_length": "文字長度", "text_length": "文字長度",
"token_length": "token 長度", "token_length": "token 長度",
"total_points": "AI 積分消耗", "total_points": "AI 積分消耗",

View File

@ -37,9 +37,11 @@
"chose_condition": "選擇條件", "chose_condition": "選擇條件",
"chosen": "已選擇", "chosen": "已選擇",
"classification": "分類", "classification": "分類",
"click_to_copy": "點選複製",
"click_to_resume": "點選繼續", "click_to_resume": "點選繼續",
"code_editor": "程式碼編輯器", "code_editor": "程式碼編輯器",
"code_error.account_error": "帳號名稱或密碼錯誤", "code_error.account_error": "帳號名稱或密碼錯誤",
"code_error.account_not_found": "用戶未註冊",
"code_error.app_error.invalid_app_type": "無效的應用程式類型", "code_error.app_error.invalid_app_type": "無效的應用程式類型",
"code_error.app_error.invalid_owner": "非法的應用程式擁有者", "code_error.app_error.invalid_owner": "非法的應用程式擁有者",
"code_error.app_error.not_exist": "應用程式不存在", "code_error.app_error.not_exist": "應用程式不存在",
@ -932,14 +934,14 @@
"model_doubao": "豆包", "model_doubao": "豆包",
"model_ernie": "文心一言", "model_ernie": "文心一言",
"model_hunyuan": "騰訊混元", "model_hunyuan": "騰訊混元",
"model_intern": "書生",
"model_moka": "Moka-AI",
"model_moonshot": "月之暗面", "model_moonshot": "月之暗面",
"model_other": "其他", "model_other": "其他",
"model_qwen": "阿里千問", "model_qwen": "阿里千問",
"model_sparkdesk": "訊飛星火", "model_sparkdesk": "訊飛星火",
"model_stepfun": "階躍星辰", "model_stepfun": "階躍星辰",
"model_yi": "零一萬物", "model_yi": "零一萬物",
"model_intern": "書生",
"model_moka": "Moka-AI",
"move.confirm": "確認移動", "move.confirm": "確認移動",
"navbar.Account": "帳戶", "navbar.Account": "帳戶",
"navbar.Chat": "對話", "navbar.Chat": "對話",

104
pnpm-lock.yaml generated
View File

@ -22,7 +22,7 @@ importers:
version: 13.3.0 version: 13.3.0
next-i18next: next-i18next:
specifier: 15.3.0 specifier: 15.3.0
version: 15.3.0(i18next@23.11.5)(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) version: 15.3.0(i18next@23.11.5)(next@14.2.5(@babel/core@7.24.9)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
prettier: prettier:
specifier: 3.2.4 specifier: 3.2.4
version: 3.2.4 version: 3.2.4
@ -67,7 +67,7 @@ importers:
version: 4.0.2 version: 4.0.2
next: next:
specifier: 14.2.5 specifier: 14.2.5
version: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) version: 14.2.5(@babel/core@7.24.9)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)
openai: openai:
specifier: 4.61.0 specifier: 4.61.0
version: 4.61.0(encoding@0.1.13) version: 4.61.0(encoding@0.1.13)
@ -210,7 +210,7 @@ importers:
version: 1.4.5-lts.1 version: 1.4.5-lts.1
next: next:
specifier: 14.2.5 specifier: 14.2.5
version: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) version: 14.2.5(@babel/core@7.24.9)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)
nextjs-cors: nextjs-cors:
specifier: ^2.2.0 specifier: ^2.2.0
version: 2.2.0(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)) version: 2.2.0(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))
@ -295,7 +295,7 @@ importers:
version: 2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1) version: 2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1)
'@chakra-ui/next-js': '@chakra-ui/next-js':
specifier: 2.1.5 specifier: 2.1.5
version: 2.1.5(@chakra-ui/react@2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react@18.3.1) version: 2.1.5(@chakra-ui/react@2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.5(@babel/core@7.24.9)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react@18.3.1)
'@chakra-ui/react': '@chakra-ui/react':
specifier: 2.8.1 specifier: 2.8.1
version: 2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -358,7 +358,7 @@ importers:
version: 4.17.21 version: 4.17.21
next-i18next: next-i18next:
specifier: 15.3.0 specifier: 15.3.0
version: 15.3.0(i18next@23.11.5)(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) version: 15.3.0(i18next@23.11.5)(next@14.2.5(@babel/core@7.24.9)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
papaparse: papaparse:
specifier: ^5.4.1 specifier: ^5.4.1
version: 5.4.1 version: 5.4.1
@ -3201,8 +3201,8 @@ packages:
'@tanstack/react-query@4.36.1': '@tanstack/react-query@4.36.1':
resolution: {integrity: sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==} resolution: {integrity: sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==}
peerDependencies: peerDependencies:
react: 18.3.1 react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: 18.3.1 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
react-native: '*' react-native: '*'
peerDependenciesMeta: peerDependenciesMeta:
react-dom: react-dom:
@ -5288,8 +5288,8 @@ packages:
fast-deep-equal@3.1.3: fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
fast-equals@5.2.0: fast-equals@5.2.2:
resolution: {integrity: sha512-3VpaQYf+CDFdRQfgsb+3vY7XaKjM35WCMoQTTE8h4S/eUkHzyJFOOA/gATYgoLejy4FBrEQD/sXe5Auk4cW/AQ==} resolution: {integrity: sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
fast-fifo@1.3.2: fast-fifo@1.3.2:
@ -10537,14 +10537,6 @@ snapshots:
next: 14.2.5(@babel/core@7.24.9)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) next: 14.2.5(@babel/core@7.24.9)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)
react: 18.3.1 react: 18.3.1
'@chakra-ui/next-js@2.1.5(@chakra-ui/react@2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react@18.3.1)':
dependencies:
'@chakra-ui/react': 2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@emotion/cache': 11.11.0
'@emotion/react': 11.11.1(@types/react@18.3.1)(react@18.3.1)
next: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)
react: 18.3.1
'@chakra-ui/number-input@2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1)': '@chakra-ui/number-input@2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@chakra-ui/counter': 2.1.0(react@18.3.1) '@chakra-ui/counter': 2.1.0(react@18.3.1)
@ -13284,7 +13276,7 @@ snapshots:
axios@1.7.7: axios@1.7.7:
dependencies: dependencies:
follow-redirects: 1.15.9 follow-redirects: 1.15.9(debug@4.3.7)
form-data: 4.0.1 form-data: 4.0.1
proxy-from-env: 1.1.0 proxy-from-env: 1.1.0
transitivePeerDependencies: transitivePeerDependencies:
@ -14584,7 +14576,7 @@ snapshots:
eslint: 8.56.0 eslint: 8.56.0
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0) eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
eslint-plugin-jsx-a11y: 6.9.0(eslint@8.56.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.56.0)
eslint-plugin-react: 7.34.4(eslint@8.56.0) eslint-plugin-react: 7.34.4(eslint@8.56.0)
eslint-plugin-react-hooks: 4.6.2(eslint@8.56.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.56.0)
@ -14608,7 +14600,7 @@ snapshots:
enhanced-resolve: 5.17.0 enhanced-resolve: 5.17.0
eslint: 8.56.0 eslint: 8.56.0
eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0) eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
fast-glob: 3.3.2 fast-glob: 3.3.2
get-tsconfig: 4.7.5 get-tsconfig: 4.7.5
is-core-module: 2.14.0 is-core-module: 2.14.0
@ -14630,7 +14622,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0): eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0):
dependencies: dependencies:
array-includes: 3.1.8 array-includes: 3.1.8
array.prototype.findlastindex: 1.2.5 array.prototype.findlastindex: 1.2.5
@ -14901,7 +14893,7 @@ snapshots:
fast-deep-equal@3.1.3: {} fast-deep-equal@3.1.3: {}
fast-equals@5.2.0: {} fast-equals@5.2.2: {}
fast-fifo@1.3.2: {} fast-fifo@1.3.2: {}
@ -15078,8 +15070,6 @@ snapshots:
follow-redirects@1.15.6: {} follow-redirects@1.15.6: {}
follow-redirects@1.15.9: {}
follow-redirects@1.15.9(debug@4.3.4): follow-redirects@1.15.9(debug@4.3.4):
optionalDependencies: optionalDependencies:
debug: 4.3.4 debug: 4.3.4
@ -17427,18 +17417,6 @@ snapshots:
react: 18.3.1 react: 18.3.1
react-i18next: 14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-i18next: 14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-i18next@15.3.0(i18next@23.11.5)(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
dependencies:
'@babel/runtime': 7.24.8
'@types/hoist-non-react-statics': 3.3.5
core-js: 3.37.1
hoist-non-react-statics: 3.3.2
i18next: 23.11.5
i18next-fs-backend: 2.3.1
next: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)
react: 18.3.1
react-i18next: 14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next@14.2.5(@babel/core@7.24.9)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8): next@14.2.5(@babel/core@7.24.9)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8):
dependencies: dependencies:
'@next/env': 14.2.5 '@next/env': 14.2.5
@ -17465,36 +17443,10 @@ snapshots:
- '@babel/core' - '@babel/core'
- babel-plugin-macros - babel-plugin-macros
next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8):
dependencies:
'@next/env': 14.2.5
'@swc/helpers': 0.5.5
busboy: 1.6.0
caniuse-lite: 1.0.30001669
graceful-fs: 4.2.11
postcss: 8.4.31
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
styled-jsx: 5.1.1(react@18.3.1)
optionalDependencies:
'@next/swc-darwin-arm64': 14.2.5
'@next/swc-darwin-x64': 14.2.5
'@next/swc-linux-arm64-gnu': 14.2.5
'@next/swc-linux-arm64-musl': 14.2.5
'@next/swc-linux-x64-gnu': 14.2.5
'@next/swc-linux-x64-musl': 14.2.5
'@next/swc-win32-arm64-msvc': 14.2.5
'@next/swc-win32-ia32-msvc': 14.2.5
'@next/swc-win32-x64-msvc': 14.2.5
sass: 1.77.8
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
nextjs-cors@2.2.0(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)): nextjs-cors@2.2.0(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)):
dependencies: dependencies:
cors: 2.8.5 cors: 2.8.5
next: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) next: 14.2.5(@babel/core@7.24.9)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)
nextjs-node-loader@1.1.5(webpack@5.92.1): nextjs-node-loader@1.1.5(webpack@5.92.1):
dependencies: dependencies:
@ -18205,7 +18157,7 @@ snapshots:
react-smooth@4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): react-smooth@4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
fast-equals: 5.2.0 fast-equals: 5.2.2
prop-types: 15.8.1 prop-types: 15.8.1
react: 18.3.1 react: 18.3.1
react-dom: 18.3.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1)
@ -18931,11 +18883,6 @@ snapshots:
'@babel/core': 7.24.9 '@babel/core': 7.24.9
babel-plugin-macros: 3.1.0 babel-plugin-macros: 3.1.0
styled-jsx@5.1.1(react@18.3.1):
dependencies:
client-only: 0.0.1
react: 18.3.1
stylis@4.2.0: {} stylis@4.2.0: {}
stylis@4.3.2: {} stylis@4.3.2: {}
@ -19137,25 +19084,6 @@ snapshots:
ts-dedent@2.2.0: {} ts-dedent@2.2.0: {}
ts-jest@29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.11)(typescript@5.5.3)))(typescript@5.5.3):
dependencies:
bs-logger: 0.2.6
ejs: 3.1.10
fast-json-stable-stringify: 2.1.0
jest: 29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.11)(typescript@5.5.3))
jest-util: 29.7.0
json5: 2.2.3
lodash.memoize: 4.1.2
make-error: 1.3.6
semver: 7.6.3
typescript: 5.5.3
yargs-parser: 21.1.1
optionalDependencies:
'@babel/core': 7.24.9
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
babel-jest: 29.7.0(@babel/core@7.24.9)
ts-jest@29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.11)(typescript@5.5.3)))(typescript@5.5.3): ts-jest@29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.11)(typescript@5.5.3)))(typescript@5.5.3):
dependencies: dependencies:
bs-logger: 0.2.6 bs-logger: 0.2.6

View File

@ -2,7 +2,7 @@ import React, { useMemo } from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import Icon from '@fastgpt/web/components/common/Icon'; import Icon from '@fastgpt/web/components/common/Icon';
import { useCopyData } from '@/web/common/hooks/useCopyData'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
export const codeLight: { [key: string]: React.CSSProperties } = { export const codeLight: { [key: string]: React.CSSProperties } = {

View File

@ -12,7 +12,7 @@ import {
ModalCloseButton ModalCloseButton
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import Icon from '@fastgpt/web/components/common/Icon'; import Icon from '@fastgpt/web/components/common/Icon';
import { useCopyData } from '@/web/common/hooks/useCopyData'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useMarkdownWidth } from '../hooks'; import { useMarkdownWidth } from '../hooks';
import type { IconNameType } from '@fastgpt/web/components/common/Icon/type.d'; import type { IconNameType } from '@fastgpt/web/components/common/Icon/type.d';

View File

@ -26,6 +26,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import Avatar from '@fastgpt/web/components/common/Avatar'; import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTag from '@fastgpt/web/components/common/Tag/index'; import MyTag from '@fastgpt/web/components/common/Tag/index';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import CopyBox from '@fastgpt/web/components/common/String/CopyBox';
const MyModal = dynamic(() => import('@fastgpt/web/components/common/MyModal')); const MyModal = dynamic(() => import('@fastgpt/web/components/common/MyModal'));
@ -252,7 +253,9 @@ const ModelTable = () => {
<Td fontSize={'sm'}> <Td fontSize={'sm'}>
<HStack> <HStack>
<Avatar src={item.avatar} w={'1.2rem'} /> <Avatar src={item.avatar} w={'1.2rem'} />
<Box color={'myGray.900'}>{item.name}</Box> <CopyBox value={item.name} color={'myGray.900'}>
{item.name}
</CopyBox>
</HStack> </HStack>
</Td> </Td>
<Td> <Td>

View File

@ -15,7 +15,7 @@ import ChatFunctionTip from './Tip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import MyImage from '@fastgpt/web/components/common/Image/MyImage'; import MyImage from '@fastgpt/web/components/common/Image/MyImage';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context'; import { AppContext } from '@/pageComponents/app/detail/context';
const TTSSelect = ({ const TTSSelect = ({
value = defaultTTSConfig, value = defaultTTSConfig,

View File

@ -30,7 +30,7 @@ import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/workflow/ut
import ChatFunctionTip from './Tip'; import ChatFunctionTip from './Tip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import InputTypeConfig from '@/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig'; import InputTypeConfig from '@/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig';
import MyIconButton from '@fastgpt/web/components/common/Icon/button'; import MyIconButton from '@fastgpt/web/components/common/Icon/button';
export const defaultVariable: VariableItemType = { export const defaultVariable: VariableItemType = {

View File

@ -1,4 +1,4 @@
import { useCopyData } from '@/web/common/hooks/useCopyData'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { Flex, FlexProps, css, useTheme } from '@chakra-ui/react'; import { Flex, FlexProps, css, useTheme } from '@chakra-ui/react';
import { ChatSiteItemType } from '@fastgpt/global/core/chat/type'; import { ChatSiteItemType } from '@fastgpt/global/core/chat/type';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip';

View File

@ -15,7 +15,7 @@ import FilesBlock from './FilesBox';
import { ChatBoxContext } from '../Provider'; import { ChatBoxContext } from '../Provider';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import AIResponseBox from '../../../components/AIResponseBox'; import AIResponseBox from '../../../components/AIResponseBox';
import { useCopyData } from '@/web/common/hooks/useCopyData'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';

View File

@ -10,7 +10,7 @@ import { AdminFbkType } from '@fastgpt/global/core/chat/type.d';
import SelectCollections from '@/web/core/dataset/components/SelectCollections'; import SelectCollections from '@/web/core/dataset/components/SelectCollections';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal')); const InputDataModal = dynamic(() => import('@/pageComponents/dataset/detail/InputDataModal'));
export type AdminMarkType = { export type AdminMarkType = {
feedbackDataId?: string; feedbackDataId?: string;

View File

@ -12,7 +12,7 @@ import { SearchScoreTypeEnum, SearchScoreTypeMap } from '@fastgpt/global/core/da
import type { readCollectionSourceBody } from '@/pages/api/core/dataset/collection/read'; import type { readCollectionSourceBody } from '@/pages/api/core/dataset/collection/read';
import Markdown from '@/components/Markdown'; import Markdown from '@/components/Markdown';
const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal')); const InputDataModal = dynamic(() => import('@/pageComponents/dataset/detail/InputDataModal'));
type ScoreItemType = SearchDataResponseItemType['score'][0]; type ScoreItemType = SearchDataResponseItemType['score'][0];
const scoreTheme: Record< const scoreTheme: Record<

View File

@ -26,7 +26,7 @@ import {
import type { EditApiKeyProps } from '@/global/support/openapi/api.d'; import type { EditApiKeyProps } from '@/global/support/openapi/api.d';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { AddIcon } from '@chakra-ui/icons'; import { AddIcon } from '@chakra-ui/icons';
import { useCopyData } from '@/web/common/hooks/useCopyData'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
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';

View File

@ -18,7 +18,7 @@ import type { TeamTagItemType } from '@fastgpt/global/support/user/team/type';
import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { RepeatIcon } from '@chakra-ui/icons'; import { RepeatIcon } from '@chakra-ui/icons';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useCopyData } from '@/web/common/hooks/useCopyData'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { getTeamsTags, loadTeamTagsByDomain } from '@/web/support/user/team/api'; import { getTeamsTags, loadTeamTagsByDomain } from '@/web/support/user/team/api';

View File

@ -3,8 +3,8 @@ import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { Box, Button, Flex, ModalBody, ModalFooter, useDisclosure } from '@chakra-ui/react'; import { Box, Button, Flex, ModalBody, ModalFooter, useDisclosure } from '@chakra-ui/react';
import { NotSufficientModalType, useSystemStore } from '@/web/common/system/useSystemStore'; import { NotSufficientModalType, useSystemStore } from '@/web/common/system/useSystemStore';
import ExtraPlan from '@/pages/price/components/ExtraPlan'; import ExtraPlan from '@/pageComponents/price/ExtraPlan';
import StandardPlan from '@/pages/price/components/Standard'; import StandardPlan from '@/pageComponents/price/Standard';
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs'; import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';

View File

@ -27,7 +27,7 @@ import dayjs from 'dayjs';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import Divider from '@/pages/app/detail/components/WorkflowComponents/Flow/components/Divider'; import Divider from '@/pageComponents/app/detail/WorkflowComponents/Flow/components/Divider';
import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type'; import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';
import { InvoiceHeaderSingleForm } from './InvoiceHeaderForm'; import { InvoiceHeaderSingleForm } from './InvoiceHeaderForm';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';

View File

@ -1,4 +1,4 @@
import Divider from '@/pages/app/detail/components/WorkflowComponents/Flow/components/Divider'; import Divider from '@/pageComponents/app/detail/WorkflowComponents/Flow/components/Divider';
import { getTeamInvoiceHeader, updateTeamInvoiceHeader } from '@/web/support/user/team/api'; import { getTeamInvoiceHeader, updateTeamInvoiceHeader } from '@/web/support/user/team/api';
import { Box, Button, Flex, HStack, Input, InputProps, Radio, RadioGroup } from '@chakra-ui/react'; import { Box, Button, Flex, HStack, Input, InputProps, Radio, RadioGroup } from '@chakra-ui/react';
import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type'; import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';

View File

@ -51,6 +51,7 @@ import MyMenu from '@fastgpt/web/components/common/MyMenu';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { putUpdateWithJson } from '@/web/core/ai/config'; import { putUpdateWithJson } from '@/web/core/ai/config';
import CopyBox from '@fastgpt/web/components/common/String/CopyBox';
const MyModal = dynamic(() => import('@fastgpt/web/components/common/MyModal')); const MyModal = dynamic(() => import('@fastgpt/web/components/common/MyModal'));
@ -396,7 +397,9 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
<Td fontSize={'sm'}> <Td fontSize={'sm'}>
<HStack> <HStack>
<Avatar src={item.avatar} w={'1.2rem'} /> <Avatar src={item.avatar} w={'1.2rem'} />
<Box color={'myGray.900'}>{item.name}</Box> <CopyBox value={item.name} color={'myGray.900'}>
{item.name}
</CopyBox>
</HStack> </HStack>
</Td> </Td>
<Td> <Td>

View File

@ -1,7 +1,7 @@
import { Box, Button, Flex, Input, ModalBody, ModalFooter } from '@chakra-ui/react'; import { Box, Button, Flex, Input, ModalBody, ModalFooter } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal'; import MyModal from '@fastgpt/web/components/common/MyModal';
import React from 'react'; import React from 'react';
import { ThirdPartyAccountType } from '../index'; import { ThirdPartyAccountType } from '../../../pages/account/thirdParty/index';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';

View File

@ -1,13 +1,11 @@
import { getTotalPoints } from '@/web/support/wallet/usage/api'; import { getDashboardData } from '@/web/support/wallet/usage/api';
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { formatNumber } from '@fastgpt/global/common/math/tools'; import { formatNumber } from '@fastgpt/global/common/math/tools';
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
import { DateRangeType } from '@fastgpt/web/components/common/DateRangePicker';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { addDays } from 'date-fns'; import { addDays } from 'date-fns';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import React, { useEffect, useMemo } from 'react'; import React, { useMemo } from 'react';
import { import {
ResponsiveContainer, ResponsiveContainer,
LineChart, LineChart,
@ -19,7 +17,8 @@ import {
TooltipProps TooltipProps
} from 'recharts'; } from 'recharts';
import { NameType, ValueType } from 'recharts/types/component/DefaultTooltipContent'; import { NameType, ValueType } from 'recharts/types/component/DefaultTooltipContent';
import { UnitType } from '../index'; import { UnitType, UsageFilterParams } from './type';
import dayjs from 'dayjs';
export type usageFormType = { export type usageFormType = {
date: string; date: string;
@ -53,60 +52,57 @@ const CustomTooltip = ({ active, payload }: TooltipProps<ValueType, NameType>) =
return null; return null;
}; };
const UsageForm = ({ const UsageDashboard = ({
dateRange, filterParams,
selectTmbIds,
usageSources,
unit,
Tabs, Tabs,
Selectors Selectors
}: { }: {
dateRange: DateRangeType; filterParams: UsageFilterParams;
selectTmbIds: string[];
usageSources: UsageSourceEnum[];
unit: UnitType;
Tabs: React.ReactNode; Tabs: React.ReactNode;
Selectors: React.ReactNode; Selectors: React.ReactNode;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { const { dateRange, selectTmbIds, usageSources, unit, isSelectAllSource, isSelectAllTmb } =
run: getTotalPointsData, filterParams;
data: totalPoints,
loading: totalPointsLoading const { data: totalPoints = [], loading: totalPointsLoading } = useRequest2(
} = useRequest2(
() => () =>
getTotalPoints({ getDashboardData({
dateStart: dateRange.from || new Date(), dateStart: dateRange.from
dateEnd: addDays(dateRange.to || new Date(), 1), ? new Date(dateRange.from.setHours(0, 0, 0, 0))
teamMemberIds: selectTmbIds, : new Date(new Date().setHours(0, 0, 0, 0)),
sources: usageSources, dateEnd: dateRange.to
? new Date(addDays(dateRange.to, 1).setHours(0, 0, 0, 0))
: new Date(addDays(new Date(), 1).setHours(0, 0, 0, 0)),
sources: isSelectAllSource ? undefined : usageSources,
teamMemberIds: isSelectAllTmb ? undefined : selectTmbIds,
unit unit
}), }).then((res) =>
res.map((item) => ({
...item,
date: dayjs(item.date).format('YYYY-MM-DD')
}))
),
{ {
manual: true manual: false,
refreshDeps: [filterParams]
} }
); );
const totalUsage = useMemo(() => { const totalUsage = useMemo(() => {
return totalPoints?.reduce((acc, curr) => acc + curr.totalPoints, 0); return totalPoints.reduce((acc, curr) => acc + curr.totalPoints, 0);
}, [totalPoints]); }, [totalPoints]);
useEffect(() => {
if (selectTmbIds.length === 0 || usageSources.length === 0) return;
getTotalPointsData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [usageSources, selectTmbIds.length, dateRange, unit]);
return ( return (
<> <>
<Box>{Tabs}</Box> <Box>{Tabs}</Box>
<Box>{Selectors}</Box> <Box mt={4}>{Selectors}</Box>
<MyBox isLoading={totalPointsLoading}> <MyBox overflowY={'auto'} isLoading={totalPointsLoading}>
<Flex fontSize={'20px'} fontWeight={'medium'} my={6}> <Flex fontSize={'20px'} fontWeight={'medium'} my={6}>
<Box color={'black'}>{`${t('account_usage:total_usage')}:`}</Box> <Box color={'black'}>{`${t('account_usage:total_usage')}:`}</Box>
<Box color={'primary.600'} ml={2}> <Box color={'primary.600'} ml={2}>
{`${formatNumber(totalUsage || 0)} ${t('account_usage:points')}`} {`${formatNumber(totalUsage)} ${t('account_usage:points')}`}
</Box> </Box>
</Flex> </Flex>
<Flex mb={4} fontSize={'mini'} color={'myGray.500'} fontWeight={'medium'}> <Flex mb={4} fontSize={'mini'} color={'myGray.500'} fontWeight={'medium'}>
@ -127,31 +123,13 @@ const UsageForm = ({
tickMargin={12} tickMargin={12}
tick={{ fontSize: '12px', color: '#667085', fontWeight: '500' }} tick={{ fontSize: '12px', color: '#667085', fontWeight: '500' }}
/> />
<CartesianGrid <CartesianGrid strokeDasharray="3 3" horizontal={true} vertical={false} />
strokeDasharray="3 3"
verticalCoordinatesGenerator={(props) => {
const { width } = props;
if (width < 500) {
return [width * 0.2, width * 0.4, width * 0.6, width * 0.8];
} else {
return [
width * 0.125,
width * 0.25,
width * 0.375,
width * 0.5,
width * 0.625,
width * 0.75,
width * 0.875
];
}
}}
/>
<Tooltip content={<CustomTooltip />} /> <Tooltip content={<CustomTooltip />} />
<Line <Line
type="monotone" type="monotone"
dataKey="totalPoints" dataKey="totalPoints"
stroke="#5E8FFF" stroke="#5E8FFF"
strokeWidth={1.5} strokeWidth={2.5}
dot={false} dot={false}
/> />
</LineChart> </LineChart>
@ -161,4 +139,4 @@ const UsageForm = ({
); );
}; };
export default React.memo(UsageForm); export default React.memo(UsageDashboard);

View File

@ -11,115 +11,95 @@ import {
Tr Tr
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { formatNumber } from '@fastgpt/global/common/math/tools'; import { formatNumber } from '@fastgpt/global/common/math/tools';
import { UsageSourceEnum, UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants'; import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
import { UsageItemType } from '@fastgpt/global/support/wallet/usage/type'; import { UsageItemType } from '@fastgpt/global/support/wallet/usage/type';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useEffect, useState } from 'react'; import React, { useMemo, useState } from 'react';
import Avatar from '@fastgpt/web/components/common/Avatar'; import Avatar from '@fastgpt/web/components/common/Avatar';
import { usePagination } from '@fastgpt/web/hooks/usePagination'; import { usePagination } from '@fastgpt/web/hooks/usePagination';
import { getUserUsages } from '@/web/support/wallet/usage/api'; import { getUserUsages } from '@/web/support/wallet/usage/api';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { DateRangeType } from '@fastgpt/web/components/common/DateRangePicker';
import { addDays } from 'date-fns'; import { addDays } from 'date-fns';
import { ExportModalParams } from './ExportModal';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; import { UsageFilterParams } from './type';
import { useToast } from '@fastgpt/web/hooks/useToast'; import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { downloadFetch } from '@/web/common/system/utils';
const UsageDetail = dynamic(() => import('./UsageDetail')); const UsageDetail = dynamic(() => import('./UsageDetail'));
const ExportModal = dynamic(() => import('./ExportModal'));
const UsageTableList = ({ const UsageTableList = ({
dateRange, filterParams,
selectTmbIds,
usageSources,
projectName,
members,
memberTotal,
isSelectAllTmb,
Tabs, Tabs,
Selectors Selectors
}: { }: {
dateRange: DateRangeType;
selectTmbIds: string[];
usageSources: UsageSourceEnum[];
projectName: string;
members: TeamMemberItemType[];
memberTotal: number;
isSelectAllTmb: boolean;
Tabs: React.ReactNode; Tabs: React.ReactNode;
Selectors: React.ReactNode; Selectors: React.ReactNode;
filterParams: UsageFilterParams;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isPc } = useSystem();
const { toast } = useToast(); const { dateRange, selectTmbIds, isSelectAllTmb, usageSources, isSelectAllSource, projectName } =
filterParams;
const requestParans = useMemo(
() => ({
dateStart: dateRange.from || new Date(),
dateEnd: addDays(dateRange.to || new Date(), 1),
sources: isSelectAllSource ? undefined : usageSources,
teamMemberIds: isSelectAllTmb ? undefined : selectTmbIds,
projectName
}),
[
dateRange.from,
dateRange.to,
isSelectAllSource,
isSelectAllTmb,
projectName,
selectTmbIds,
usageSources
]
);
const { const {
data: usages, data: usages,
isLoading, isLoading,
Pagination, Pagination,
getData,
total total
} = usePagination(getUserUsages, { } = usePagination(getUserUsages, {
pageSize: isPc ? 20 : 10, pageSize: 20,
params: { params: requestParans,
dateStart: dateRange.from || new Date(), refreshDeps: [requestParans]
dateEnd: addDays(dateRange.to || new Date(), 1),
sources: usageSources,
teamMemberIds: selectTmbIds,
isSelectAllTmb,
projectName
},
defaultRequest: false
}); });
const [usageDetail, setUsageDetail] = useState<UsageItemType>(); const [usageDetail, setUsageDetail] = useState<UsageItemType>();
const [currentParams, setCurrentParams] = useState<ExportModalParams | null>(null);
useEffect(() => { const { runAsync: exportUsage } = useRequest2(
if ((!isSelectAllTmb && selectTmbIds.length === 0) || usageSources.length === 0) return; async () => {
getData(1); await downloadFetch({
// eslint-disable-next-line react-hooks/exhaustive-deps url: `/api/proApi/support/wallet/usage/exportUsage`,
}, [usageSources, selectTmbIds.length, projectName, dateRange, isSelectAllTmb]); filename: `usage.csv`,
body: requestParans
});
},
{
refreshDeps: [requestParans]
}
);
return ( return (
<> <>
<Box>{Tabs}</Box> <Box>{Tabs}</Box>
<Flex flexDir={['column', 'row']} w={'100%'} alignItems={['flex-end', 'center']}> <Flex mt={4} w={'100%'}>
<Box>{Selectors}</Box> <Box>{Selectors}</Box>
<Box flex={'1'} /> <Box flex={'1'} />
<Button <PopoverConfirm
size={'md'} Trigger={<Button size={'md'}>{t('common:Export')}</Button>}
onClick={() => { showCancel
if ((selectTmbIds.length === 0 && !isSelectAllTmb) || usageSources.length === 0) { content={t('account_usage:export_confirm_tip', { total })}
return toast({ onConfirm={exportUsage}
status: 'warning', />
title: t('account_usage:select_member_and_source_first')
});
}
setCurrentParams({
dateStart: dateRange.from || new Date(),
dateEnd: addDays(dateRange.to || new Date(), 1),
sources: usageSources,
teamMemberIds: selectTmbIds,
teamMemberNames: members
.filter((item) =>
isSelectAllTmb
? !selectTmbIds.includes(item.tmbId)
: selectTmbIds.includes(item.tmbId)
)
.map((item) => item.memberName),
isSelectAllTmb,
projectName
});
}}
>
{t('common:Export')}
</Button>
</Flex> </Flex>
<MyBox position={'relative'} overflowY={'auto'} mt={3} flex={1} isLoading={isLoading}> <MyBox position={'relative'} overflowY={'auto'} mt={3} flex={1} isLoading={isLoading}>
<TableContainer> <TableContainer>
@ -172,17 +152,8 @@ const UsageTableList = ({
{!!usageDetail && ( {!!usageDetail && (
<UsageDetail usage={usageDetail} onClose={() => setUsageDetail(undefined)} /> <UsageDetail usage={usageDetail} onClose={() => setUsageDetail(undefined)} />
)} )}
{!!currentParams && (
<ExportModal
onClose={() => setCurrentParams(null)}
params={currentParams}
memberTotal={isSelectAllTmb ? memberTotal - selectTmbIds.length : selectTmbIds.length}
total={total}
/>
)}
</> </>
); );
}; };
export default UsageTableList; export default React.memo(UsageTableList);

View File

@ -0,0 +1,13 @@
import { DateRangeType } from '@fastgpt/web/components/common/DateRangePicker';
export type UnitType = 'day' | 'month';
export type UsageFilterParams = {
dateRange: DateRangeType;
selectTmbIds: string[];
isSelectAllTmb: boolean;
usageSources: UsageSourceEnum[];
isSelectAllSource: boolean;
projectName: string;
unit: UnitType;
};

View File

@ -3,7 +3,7 @@ import { Box, Flex } from '@chakra-ui/react';
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';
import { filterSensitiveNodesData } from '@/web/core/workflow/utils'; import { filterSensitiveNodesData } from '@/web/core/workflow/utils';
import { useCopyData } from '@/web/common/hooks/useCopyData'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import MyPopover from '@fastgpt/web/components/common/MyPopover'; import MyPopover from '@fastgpt/web/components/common/MyPopover';
import { fileDownload } from '@/web/common/file/utils'; import { fileDownload } from '@/web/common/file/utils';
import { AppChatConfigType, AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; import { AppChatConfigType, AppSimpleEditFormType } from '@fastgpt/global/core/app/type';

View File

@ -1,6 +1,6 @@
import CollaboratorContextProvider from '@/components/support/permission/MemberManager/context'; import CollaboratorContextProvider from '@/components/support/permission/MemberManager/context';
import ResumeInherit from '@/components/support/permission/ResumeInheritText'; import ResumeInherit from '@/components/support/permission/ResumeInheritText';
import { AppContext } from '@/pages/app/detail/components/context'; import { AppContext } from './context';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { useI18n } from '@/web/context/I18n'; import { useI18n } from '@/web/context/I18n';
import { resumeInheritPer } from '@/web/core/app/api'; import { resumeInheritPer } from '@/web/core/app/api';

View File

@ -11,7 +11,7 @@ import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
import { PluginRunBoxTabEnum } from '@/components/core/chat/ChatContainer/PluginRunBox/constants'; import { PluginRunBoxTabEnum } from '@/components/core/chat/ChatContainer/PluginRunBox/constants';
import CloseIcon from '@fastgpt/web/components/common/Icon/close'; import CloseIcon from '@fastgpt/web/components/common/Icon/close';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { PcHeader } from '@/pages/chat/components/ChatHeader'; import { PcHeader } from '@/pageComponents/chat/ChatHeader';
import { GetChatTypeEnum } from '@/global/core/chat/constants'; import { GetChatTypeEnum } from '@/global/core/chat/constants';
import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import ChatRecordContextProvider, { import ChatRecordContextProvider, {

View File

@ -6,7 +6,7 @@ import { Box, Flex, FlexProps, Grid, ModalBody, Switch, useTheme } from '@chakra
import MyRadio from '@/components/common/MyRadio'; import MyRadio from '@/components/common/MyRadio';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useCopyData } from '@/web/common/hooks/useCopyData'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { fileToBase64 } from '@/web/common/file/utils'; import { fileToBase64 } from '@/web/common/file/utils';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';

View File

@ -28,7 +28,7 @@ import {
putShareChat putShareChat
} from '@/web/support/outLink/api'; } from '@/web/support/outLink/api';
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time'; import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
import { useCopyData } from '@/web/common/hooks/useCopyData'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { defaultOutLinkForm } from '@/web/core/app/constants'; import { defaultOutLinkForm } from '@/web/core/app/constants';
import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type.d'; import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type.d';

View File

@ -1,4 +1,4 @@
import { useCopyData } from '@/web/common/hooks/useCopyData'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { Box, Image, Flex, ModalBody } from '@chakra-ui/react'; import { Box, Image, Flex, ModalBody } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal'; import MyModal from '@fastgpt/web/components/common/MyModal';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';

View File

@ -16,7 +16,7 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import TagsEditModal from '../TagsEditModal'; import TagsEditModal from '../TagsEditModal';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { AppContext } from '@/pages/app/detail/components/context'; import { AppContext } from '@/pageComponents/app/detail/context';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import MyMenu from '@fastgpt/web/components/common/MyMenu'; import MyMenu from '@fastgpt/web/components/common/MyMenu';
import MyModal from '@fastgpt/web/components/common/MyModal'; import MyModal from '@fastgpt/web/components/common/MyModal';

View File

@ -27,7 +27,7 @@ import type { SettingAIDataType } from '@fastgpt/global/core/app/type.d';
import { TTSTypeEnum } from '@/web/core/app/constants'; import { TTSTypeEnum } from '@/web/core/app/constants';
import { workflowSystemVariables } from '@/web/core/app/utils'; import { workflowSystemVariables } from '@/web/core/app/utils';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context'; import { AppContext } from '@/pageComponents/app/detail/context';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip'; import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip';

View File

@ -23,7 +23,7 @@ import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { getTeamsTags } from '@/web/support/user/team/api'; import { getTeamsTags } from '@/web/support/user/team/api';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context'; import { AppContext } from './context';
const TagsEditModal = ({ onClose }: { onClose: () => void }) => { const TagsEditModal = ({ onClose }: { onClose: () => void }) => {
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -8,8 +8,8 @@ import { useTranslation } from 'next-i18next';
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge'; import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context'; import { AppContext } from '@/pageComponents/app/detail/context';
import { useChatTest } from '@/pages/app/detail/components/useChatTest'; import { useChatTest } from '../../useChatTest';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs'; import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
import { PluginRunBoxTabEnum } from '@/components/core/chat/ChatContainer/PluginRunBox/constants'; import { PluginRunBoxTabEnum } from '@/components/core/chat/ChatContainer/PluginRunBox/constants';

View File

@ -1,6 +1,6 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { getNanoid } from '@fastgpt/global/common/string/tools'; import { getNanoid } from '@fastgpt/global/common/string/tools';
import { useCopyData } from '@/web/common/hooks/useCopyData'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { Node, useKeyPress } from 'reactflow'; import { Node, useKeyPress } from 'reactflow';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';

Some files were not shown because too many files have changed in this diff Show More