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:
@@ -2,12 +2,22 @@ import { ErrType } from '../errorCode';
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
/* team: 503000 */
|
||||
export enum UserErrEnum {
|
||||
notUser = 'notUser',
|
||||
userExist = 'userExist',
|
||||
unAuthRole = 'unAuthRole',
|
||||
account_psw_error = 'account_psw_error',
|
||||
balanceNotEnough = 'balanceNotEnough',
|
||||
unAuthSso = 'unAuthSso'
|
||||
}
|
||||
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,
|
||||
message: i18nT('common:code_error.account_error')
|
||||
|
||||
17
packages/global/support/wallet/usage/api.d.ts
vendored
17
packages/global/support/wallet/usage/api.d.ts
vendored
@@ -6,21 +6,20 @@ export type CreateTrainingUsageProps = {
|
||||
datasetId: string;
|
||||
};
|
||||
|
||||
export type GetTotalPointsProps = {
|
||||
dateStart: Date;
|
||||
dateEnd: Date;
|
||||
teamMemberIds: string[];
|
||||
sources: UsageSourceEnum[];
|
||||
unit: 'day' | 'week' | 'month';
|
||||
};
|
||||
|
||||
export type GetUsageProps = {
|
||||
dateStart: Date;
|
||||
dateEnd: Date;
|
||||
sources?: UsageSourceEnum[];
|
||||
teamMemberIds?: string[];
|
||||
projectName?: string;
|
||||
isSelectAllTmb?: boolean;
|
||||
};
|
||||
|
||||
export type GetUsageDashboardProps = GetUsageProps & {
|
||||
unit: 'day' | 'month';
|
||||
};
|
||||
export type GetUsageDashboardResponseItem = {
|
||||
date: Date;
|
||||
totalPoints: number;
|
||||
};
|
||||
|
||||
export type ConcatUsageProps = UsageListItemCountType & {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { jsonRes } from '../response';
|
||||
// unit: times/s
|
||||
// how to use?
|
||||
// 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) => {
|
||||
const ip = requestIp.getClientIp(req);
|
||||
if (!ip || (process.env.USE_IP_LIMIT !== 'true' && !force)) {
|
||||
|
||||
@@ -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 = (
|
||||
await Promise.all(
|
||||
datasetIds.map(async (id) => {
|
||||
@@ -637,7 +524,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
||||
filterCollectionIdList
|
||||
}),
|
||||
// FullText tmp
|
||||
fullTextRecall2({
|
||||
fullTextRecall({
|
||||
query,
|
||||
limit: fullTextLimit,
|
||||
filterCollectionIdList,
|
||||
|
||||
@@ -61,7 +61,8 @@ const UsageSchema = new Schema({
|
||||
});
|
||||
|
||||
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
|
||||
// UsageSchema.index({ teamId: 1, time: -1 });
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ const DateRangePicker = ({
|
||||
date.to = date.from;
|
||||
}
|
||||
setRange(date);
|
||||
onChange && onChange(date);
|
||||
onChange?.(date);
|
||||
}}
|
||||
footer={
|
||||
<Flex justifyContent={'flex-end'}>
|
||||
@@ -116,7 +116,7 @@ const DateRangePicker = ({
|
||||
<Button
|
||||
size={'sm'}
|
||||
onClick={() => {
|
||||
onSuccess && onSuccess(range || defaultDate);
|
||||
onSuccess?.(range || defaultDate);
|
||||
setShowSelected(false);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -10,29 +10,30 @@ import {
|
||||
MenuList,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import MyTag from '../Tag/index';
|
||||
import MyIcon from '../Icon';
|
||||
import MyAvatar from '../Avatar';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useScrollPagination } from '../../../hooks/useScrollPagination';
|
||||
import MyDivider from '../MyDivider';
|
||||
|
||||
export type SelectProps<T = any> = {
|
||||
value: T[];
|
||||
placeholder?: string;
|
||||
list: {
|
||||
icon?: string;
|
||||
label: string | React.ReactNode;
|
||||
value: T;
|
||||
}[];
|
||||
value: T[];
|
||||
isSelectAll: boolean;
|
||||
setIsSelectAll: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
|
||||
placeholder?: string;
|
||||
maxH?: number;
|
||||
itemWrap?: boolean;
|
||||
onSelect: (val: T[]) => void;
|
||||
closeable?: boolean;
|
||||
showCheckedIcon?: boolean;
|
||||
ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
|
||||
isSelectAll?: boolean;
|
||||
setIsSelectAll?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
} & Omit<ButtonProps, 'onSelect'>;
|
||||
|
||||
const MultipleSelect = <T = any,>({
|
||||
@@ -42,7 +43,6 @@ const MultipleSelect = <T = any,>({
|
||||
maxH = 400,
|
||||
onSelect,
|
||||
closeable = false,
|
||||
showCheckedIcon = true,
|
||||
itemWrap = true,
|
||||
ScrollData,
|
||||
isSelectAll,
|
||||
@@ -65,79 +65,66 @@ const MultipleSelect = <T = any,>({
|
||||
}
|
||||
};
|
||||
|
||||
const onclickItem = (val: T) => {
|
||||
if (value.includes(val)) {
|
||||
onSelect(value.filter((i) => i !== val));
|
||||
} else {
|
||||
onSelect([...value, val]);
|
||||
}
|
||||
};
|
||||
const onclickItem = useCallback(
|
||||
(val: T) => {
|
||||
// 全选状态下,value 实际上上空。
|
||||
if (isSelectAll) {
|
||||
onSelect(list.map((item) => item.value).filter((i) => i !== val));
|
||||
setIsSelectAll(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const onSelectAll = () => {
|
||||
if (!setIsSelectAll) {
|
||||
onSelect(value.length === list.length ? [] : list.map((item) => item.value));
|
||||
return;
|
||||
}
|
||||
if (value.includes(val)) {
|
||||
onSelect(value.filter((i) => i !== val));
|
||||
} else {
|
||||
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);
|
||||
};
|
||||
}, [value, list, setIsSelectAll, onSelect]);
|
||||
|
||||
const ListRender = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
{list.map((item, i) => (
|
||||
<MenuItem
|
||||
key={i}
|
||||
{...menuItemStyles}
|
||||
{...((isSelectAll && !value.includes(item.value)) ||
|
||||
(!isSelectAll && value.includes(item.value))
|
||||
? {
|
||||
color: 'primary.600'
|
||||
}
|
||||
: {
|
||||
color: 'myGray.900'
|
||||
})}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
onclickItem(item.value);
|
||||
}}
|
||||
whiteSpace={'pre-wrap'}
|
||||
fontSize={'sm'}
|
||||
gap={2}
|
||||
>
|
||||
{!showCheckedIcon && (
|
||||
<Checkbox
|
||||
isChecked={
|
||||
(isSelectAll && !value.includes(item.value)) ||
|
||||
(!isSelectAll && value.includes(item.value))
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{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>
|
||||
))}
|
||||
{list.map((item, i) => {
|
||||
const isSelected = isSelectAll || value.includes(item.value);
|
||||
return (
|
||||
<MenuItem
|
||||
key={i}
|
||||
{...menuItemStyles}
|
||||
{...(isSelected
|
||||
? {
|
||||
color: 'primary.600'
|
||||
}
|
||||
: {
|
||||
color: 'myGray.900'
|
||||
})}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
onclickItem(item.value);
|
||||
}}
|
||||
whiteSpace={'pre-wrap'}
|
||||
fontSize={'sm'}
|
||||
gap={2}
|
||||
>
|
||||
<Checkbox isChecked={isSelected} />
|
||||
{item.icon && <MyAvatar src={item.icon} w={'1rem'} borderRadius={'0'} />}
|
||||
<Box flex={'1 0 0'}>{item.label}</Box>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}, [value, list, isSelectAll]);
|
||||
|
||||
const isAllSelected = useMemo(
|
||||
() => (isSelectAll && value.length === 0) || (!isSelectAll && value.length === list.length),
|
||||
[isSelectAll, value, list]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Menu
|
||||
@@ -186,44 +173,43 @@ const MultipleSelect = <T = any,>({
|
||||
overflow={'hidden'}
|
||||
flex={1}
|
||||
>
|
||||
{isAllSelected ? (
|
||||
{isSelectAll ? (
|
||||
<Box fontSize={'mini'} color={'myGray.900'}>
|
||||
{t('common:common.All')}
|
||||
</Box>
|
||||
) : (
|
||||
(isSelectAll
|
||||
? list.filter((item) => !value.includes(item.value))
|
||||
: list.filter((item) => value.includes(item.value))
|
||||
).map((item, i) => (
|
||||
<MyTag
|
||||
className="tag-icon"
|
||||
key={i}
|
||||
bg={'primary.100'}
|
||||
color={'primary.700'}
|
||||
type={'fill'}
|
||||
borderRadius={'full'}
|
||||
px={2}
|
||||
py={0.5}
|
||||
flexShrink={0}
|
||||
>
|
||||
{item.label}
|
||||
{closeable && (
|
||||
<MyIcon
|
||||
name={'common/closeLight'}
|
||||
ml={1}
|
||||
w="0.8rem"
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
color: 'red.500'
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onclickItem(item.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</MyTag>
|
||||
))
|
||||
list
|
||||
.filter((item) => value.includes(item.value))
|
||||
.map((item, i) => (
|
||||
<MyTag
|
||||
className="tag-icon"
|
||||
key={i}
|
||||
bg={'primary.100'}
|
||||
color={'primary.700'}
|
||||
type={'fill'}
|
||||
borderRadius={'full'}
|
||||
px={2}
|
||||
py={0.5}
|
||||
flexShrink={0}
|
||||
>
|
||||
{item.label}
|
||||
{closeable && (
|
||||
<MyIcon
|
||||
name={'common/closeLight'}
|
||||
ml={1}
|
||||
w="0.8rem"
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
color: 'red.500'
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onclickItem(item.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</MyTag>
|
||||
))
|
||||
)}
|
||||
</Flex>
|
||||
<MyIcon name={'core/chat/chevronDown'} color={'myGray.600'} w={4} h={4} />
|
||||
@@ -245,7 +231,7 @@ const MultipleSelect = <T = any,>({
|
||||
>
|
||||
<MenuItem
|
||||
{...menuItemStyles}
|
||||
color={isAllSelected ? 'primary.600' : 'myGray.900'}
|
||||
color={isSelectAll ? 'primary.600' : 'myGray.900'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
@@ -256,15 +242,12 @@ const MultipleSelect = <T = any,>({
|
||||
gap={2}
|
||||
mb={1}
|
||||
>
|
||||
{!showCheckedIcon && <Checkbox isChecked={isAllSelected} />}
|
||||
<Checkbox isChecked={isSelectAll} />
|
||||
<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>
|
||||
|
||||
<MyDivider my={1} />
|
||||
|
||||
{ScrollData ? <ScrollData>{ListRender}</ScrollData> : ListRender}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
@@ -273,3 +256,9 @@ const MultipleSelect = <T = any,>({
|
||||
};
|
||||
|
||||
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 };
|
||||
};
|
||||
|
||||
24
packages/web/components/common/String/CopyBox.tsx
Normal file
24
packages/web/components/common/String/CopyBox.tsx
Normal 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;
|
||||
64
packages/web/hooks/useCopyData.tsx
Normal file
64
packages/web/hooks/useCopyData.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useToast } from './useToast';
|
||||
import { useCallback } from 'react';
|
||||
import { hasHttps } from '../common/system/utils';
|
||||
import { isProduction } from '@fastgpt/global/common/system/constants';
|
||||
|
||||
/**
|
||||
* copy text data
|
||||
*/
|
||||
export const useCopyData = () => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
|
||||
const copyData = useCallback(
|
||||
async (
|
||||
data: string,
|
||||
title: string | null = t('common:common.Copy Successful'),
|
||||
duration = 1000
|
||||
) => {
|
||||
data = data.trim();
|
||||
|
||||
try {
|
||||
if ((hasHttps() || !isProduction) && navigator.clipboard) {
|
||||
await navigator.clipboard.writeText(data);
|
||||
} else {
|
||||
throw new Error('');
|
||||
}
|
||||
} catch (error) {
|
||||
// console.log(error);
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = data;
|
||||
textarea.style.position = 'absolute';
|
||||
textarea.style.opacity = '0';
|
||||
document.body.appendChild(textarea);
|
||||
|
||||
textarea.select();
|
||||
const res = document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
|
||||
if (!res) {
|
||||
return toast({
|
||||
title: t('common:common.Copy_failed'),
|
||||
status: 'error',
|
||||
duration
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (title) {
|
||||
toast({
|
||||
title,
|
||||
status: 'success',
|
||||
duration
|
||||
});
|
||||
}
|
||||
},
|
||||
[t, toast]
|
||||
);
|
||||
|
||||
return {
|
||||
copyData
|
||||
};
|
||||
};
|
||||
@@ -9,7 +9,10 @@
|
||||
"details": "Details",
|
||||
"dingtalk": "DingTalk",
|
||||
"duration_seconds": "Duration (seconds)",
|
||||
"every_day": "Day",
|
||||
"every_month": "Moon",
|
||||
"export_confirm": "Export confirmation",
|
||||
"export_confirm_tip": "There are currently {{total}} usage records in total. Are you sure to export?",
|
||||
"feishu": "Feishu",
|
||||
"generation_time": "Generation time",
|
||||
"input_token_length": "input tokens",
|
||||
@@ -26,7 +29,6 @@
|
||||
"select_member_and_source_first": "Please select members and types first",
|
||||
"share": "Share Link",
|
||||
"source": "source",
|
||||
"start_export": "Export started",
|
||||
"text_length": "text length",
|
||||
"token_length": "token length",
|
||||
"total_points": "AI points consumption",
|
||||
|
||||
@@ -37,9 +37,12 @@
|
||||
"chose_condition": "Choose Condition",
|
||||
"chosen": "Chosen",
|
||||
"classification": "Classification",
|
||||
"click_to_copy": "Click to copy",
|
||||
"click_to_resume": "Click to Resume",
|
||||
"code_editor": "Code Editor",
|
||||
"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_owner": "Unauthorized Application Owner",
|
||||
"code_error.app_error.not_exist": "Application Does Not Exist",
|
||||
@@ -932,14 +935,14 @@
|
||||
"model_doubao": "Doubao",
|
||||
"model_ernie": "Ernie",
|
||||
"model_hunyuan": "Hunyuan",
|
||||
"model_intern": "Intern",
|
||||
"model_moka": "Moka-AI",
|
||||
"model_moonshot": "Moonshot",
|
||||
"model_other": "Other",
|
||||
"model_qwen": "Qwen",
|
||||
"model_sparkdesk": "SprkDesk",
|
||||
"model_stepfun": "StepFun",
|
||||
"model_yi": "Yi",
|
||||
"model_intern": "Intern",
|
||||
"model_moka": "Moka-AI",
|
||||
"move.confirm": "Confirm move",
|
||||
"navbar.Account": "Account",
|
||||
"navbar.Chat": "Chat",
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
"details": "详情",
|
||||
"dingtalk": "钉钉",
|
||||
"duration_seconds": "时长(秒)",
|
||||
"every_day": "每天",
|
||||
"every_month": "每月",
|
||||
"every_day": "天",
|
||||
"every_month": "月",
|
||||
"every_week": "每周",
|
||||
"export_confirm": "导出确认",
|
||||
"export_confirm_tip": "当前共 {{total}} 条使用记录,确认导出?",
|
||||
"export_success": "导出成功",
|
||||
"feishu": "飞书",
|
||||
"generation_time": "生成时间",
|
||||
@@ -30,7 +31,6 @@
|
||||
"select_member_and_source_first": "请先选中成员和类型",
|
||||
"share": "分享链接",
|
||||
"source": "来源",
|
||||
"start_export": "已开始导出",
|
||||
"text_length": "文本长度",
|
||||
"token_length": "token 长度",
|
||||
"total_points": "AI 积分消耗",
|
||||
|
||||
@@ -41,9 +41,12 @@
|
||||
"chose_condition": "选择条件",
|
||||
"chosen": "已选",
|
||||
"classification": "分类",
|
||||
"click_to_copy": "点击复制",
|
||||
"click_to_resume": "点击恢复",
|
||||
"code_editor": "代码编辑",
|
||||
"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_owner": "非法的应用所有者",
|
||||
"code_error.app_error.not_exist": "应用不存在",
|
||||
@@ -935,14 +938,14 @@
|
||||
"model_doubao": "豆包",
|
||||
"model_ernie": "文心一言",
|
||||
"model_hunyuan": "腾讯混元",
|
||||
"model_intern": "书生",
|
||||
"model_moka": "Moka-AI",
|
||||
"model_moonshot": "月之暗面",
|
||||
"model_other": "其他",
|
||||
"model_qwen": "阿里千问",
|
||||
"model_sparkdesk": "讯飞星火",
|
||||
"model_stepfun": "阶跃星辰",
|
||||
"model_yi": "零一万物",
|
||||
"model_intern": "书生",
|
||||
"model_moka": "Moka-AI",
|
||||
"move.confirm": "确认移动",
|
||||
"navbar.Account": "账号",
|
||||
"navbar.Chat": "聊天",
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
"details": "詳情",
|
||||
"dingtalk": "釘釘",
|
||||
"duration_seconds": "時長(秒)",
|
||||
"every_day": "天",
|
||||
"every_month": "月",
|
||||
"export_confirm": "導出確認",
|
||||
"export_confirm_tip": "當前共 {{total}} 筆使用記錄,確認導出?",
|
||||
"feishu": "飛書",
|
||||
"generation_time": "生成時間",
|
||||
"input_token_length": "輸入 tokens",
|
||||
@@ -26,7 +29,6 @@
|
||||
"select_member_and_source_first": "請先選取成員和類型",
|
||||
"share": "分享連結",
|
||||
"source": "來源",
|
||||
"start_export": "已開始匯出",
|
||||
"text_length": "文字長度",
|
||||
"token_length": "token 長度",
|
||||
"total_points": "AI 積分消耗",
|
||||
|
||||
@@ -37,9 +37,11 @@
|
||||
"chose_condition": "選擇條件",
|
||||
"chosen": "已選擇",
|
||||
"classification": "分類",
|
||||
"click_to_copy": "點選複製",
|
||||
"click_to_resume": "點選繼續",
|
||||
"code_editor": "程式碼編輯器",
|
||||
"code_error.account_error": "帳號名稱或密碼錯誤",
|
||||
"code_error.account_not_found": "用戶未註冊",
|
||||
"code_error.app_error.invalid_app_type": "無效的應用程式類型",
|
||||
"code_error.app_error.invalid_owner": "非法的應用程式擁有者",
|
||||
"code_error.app_error.not_exist": "應用程式不存在",
|
||||
@@ -932,14 +934,14 @@
|
||||
"model_doubao": "豆包",
|
||||
"model_ernie": "文心一言",
|
||||
"model_hunyuan": "騰訊混元",
|
||||
"model_intern": "書生",
|
||||
"model_moka": "Moka-AI",
|
||||
"model_moonshot": "月之暗面",
|
||||
"model_other": "其他",
|
||||
"model_qwen": "阿里千問",
|
||||
"model_sparkdesk": "訊飛星火",
|
||||
"model_stepfun": "階躍星辰",
|
||||
"model_yi": "零一萬物",
|
||||
"model_intern": "書生",
|
||||
"model_moka": "Moka-AI",
|
||||
"move.confirm": "確認移動",
|
||||
"navbar.Account": "帳戶",
|
||||
"navbar.Chat": "對話",
|
||||
|
||||
Reference in New Issue
Block a user