mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 21:13:50 +00:00
I18n Translations (#2267)
* rebase * i18n-1 * add error info i18n * fix * fix * refactor: 删除error.json * delete useI18n
This commit is contained in:
@@ -22,7 +22,7 @@ const CommunityModal = ({ onClose }: { onClose: () => void }) => {
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} onClick={onClose}>
|
||||
关闭
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
|
@@ -3,7 +3,7 @@ import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { Box, Button, Flex, Grid, useTheme } from '@chakra-ui/react';
|
||||
import { PromptTemplateItem } from '@fastgpt/global/core/ai/type.d';
|
||||
import { ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
const PromptTemplate = ({
|
||||
title,
|
||||
templates,
|
||||
@@ -17,7 +17,7 @@ const PromptTemplate = ({
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [selectTemplateTitle, setSelectTemplateTitle] = useState<PromptTemplateItem>();
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<MyModal isOpen title={title} onClose={onClose} iconSrc="/imgs/modal/prompt.svg">
|
||||
<ModalBody h="100%" w={'600px'} maxW={'90vw'} overflowY={'auto'}>
|
||||
@@ -55,7 +55,7 @@ const PromptTemplate = ({
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
确认选择
|
||||
{t('common:confirm_choice')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
|
@@ -14,7 +14,6 @@ import CollaboratorContextProvider, {
|
||||
} from '../../support/permission/MemberManager/context';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import ResumeInherit from '@/components/support/permission/ResumeInheritText';
|
||||
|
||||
const FolderSlideCard = ({
|
||||
@@ -55,7 +54,6 @@ const FolderSlideCard = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const { commonT } = useI18n();
|
||||
const { toast } = useToast();
|
||||
|
||||
const { ConfirmModal, openConfirm } = useConfirm({
|
||||
@@ -78,7 +76,7 @@ const FolderSlideCard = ({
|
||||
/>
|
||||
</HStack>
|
||||
<Box mt={3} fontSize={'sm'} color={'myGray.500'} cursor={'pointer'} onClick={onEdit}>
|
||||
{intro || '暂无介绍'}
|
||||
{intro || t('common:not_yet_introduced')}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
|
@@ -135,7 +135,9 @@ const AIChatSettingsModal = ({
|
||||
<QuestionTip ml={1} label={t('common:core.module.template.AI support tool tip')} />
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
{selectedModel?.toolChoice || selectedModel?.functionCall ? '支持' : '不支持'}
|
||||
{selectedModel?.toolChoice || selectedModel?.functionCall
|
||||
? t('common:common.support')
|
||||
: t('common:common.not_support')}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={8}>
|
||||
|
@@ -20,11 +20,10 @@ import type { MultipleSelectProps } from '@fastgpt/web/components/common/MySelec
|
||||
import { cronParser2Fields } from '@fastgpt/global/common/string/time';
|
||||
import TimezoneSelect from '@fastgpt/web/components/common/MySelect/TimezoneSelect';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
|
||||
const MultipleRowSelect = dynamic(
|
||||
() => import('@fastgpt/web/components/common/MySelect/MultipleRowSelect')
|
||||
);
|
||||
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
// options type:
|
||||
enum CronJobTypeEnum {
|
||||
month = 'month',
|
||||
@@ -40,17 +39,32 @@ const get24HoursOptions = () => {
|
||||
value: i
|
||||
}));
|
||||
};
|
||||
|
||||
const getRoute = (i: number) => {
|
||||
switch (i) {
|
||||
case 0:
|
||||
return 'app:week.Sunday';
|
||||
case 1:
|
||||
return 'app:week.Monday';
|
||||
case 2:
|
||||
return 'app:week.Tuesday';
|
||||
case 3:
|
||||
return 'app:week.Wednesday';
|
||||
case 4:
|
||||
return 'app:week.Thursday';
|
||||
case 5:
|
||||
return 'app:week.Friday';
|
||||
case 6:
|
||||
return 'app:week.Saturday';
|
||||
default:
|
||||
return 'app:week.Sunday';
|
||||
}
|
||||
};
|
||||
|
||||
const getWeekOptions = () => {
|
||||
return Array.from({ length: 7 }, (_, i) => {
|
||||
if (i === 0) {
|
||||
return {
|
||||
label: '星期日',
|
||||
value: i,
|
||||
children: get24HoursOptions()
|
||||
};
|
||||
}
|
||||
return {
|
||||
label: `星期${i}`,
|
||||
label: i18nT(getRoute(i)),
|
||||
value: i,
|
||||
children: get24HoursOptions()
|
||||
};
|
||||
@@ -58,7 +72,7 @@ const getWeekOptions = () => {
|
||||
};
|
||||
const getMonthOptions = () => {
|
||||
return Array.from({ length: 28 }, (_, i) => ({
|
||||
label: `${i + 1}号`,
|
||||
label: `${i + 1}` + i18nT('app:month.unit'),
|
||||
value: i,
|
||||
children: get24HoursOptions()
|
||||
}));
|
||||
@@ -67,27 +81,27 @@ const getInterValOptions = () => {
|
||||
// 每n小时
|
||||
return [
|
||||
{
|
||||
label: `每小时`,
|
||||
label: i18nT('app:interval.per_hour'),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: `每2小时`,
|
||||
label: i18nT('app:interval.2_hours'),
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: `每3小时`,
|
||||
label: i18nT('app:interval.3_hours'),
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
label: `每4小时`,
|
||||
label: i18nT('app:interval.4_hours'),
|
||||
value: 4
|
||||
},
|
||||
{
|
||||
label: `每6小时`,
|
||||
label: i18nT('app:interval.6_hours'),
|
||||
value: 6
|
||||
},
|
||||
{
|
||||
label: `每12小时`,
|
||||
label: i18nT('app:interval.12_hours'),
|
||||
value: 12
|
||||
}
|
||||
];
|
||||
@@ -113,22 +127,22 @@ const ScheduledTriggerConfig = ({
|
||||
|
||||
const cronSelectList = useRef<MultipleSelectProps['list']>([
|
||||
{
|
||||
label: '每天执行',
|
||||
label: t('app:cron.every_day'),
|
||||
value: CronJobTypeEnum.day,
|
||||
children: get24HoursOptions()
|
||||
},
|
||||
{
|
||||
label: '每周执行',
|
||||
label: t('app:cron.every_week'),
|
||||
value: CronJobTypeEnum.week,
|
||||
children: getWeekOptions()
|
||||
},
|
||||
{
|
||||
label: '每月执行',
|
||||
label: t('app:cron.every_month'),
|
||||
value: CronJobTypeEnum.month,
|
||||
children: getMonthOptions()
|
||||
},
|
||||
{
|
||||
label: '间隔执行',
|
||||
label: t('app:cron.interval'),
|
||||
value: CronJobTypeEnum.interval,
|
||||
children: getInterValOptions()
|
||||
}
|
||||
@@ -224,7 +238,7 @@ const ScheduledTriggerConfig = ({
|
||||
}
|
||||
if (cronField[0] === 'week') {
|
||||
return t('core.app.schedule.Every week', {
|
||||
day: cronField[1] === 0 ? '日' : cronField[1],
|
||||
day: cronField[1] === 0 ? t('app:day') : cronField[1],
|
||||
hour: cronField[2]
|
||||
});
|
||||
}
|
||||
@@ -279,10 +293,7 @@ const ScheduledTriggerConfig = ({
|
||||
>
|
||||
<ModalBody>
|
||||
<Flex justifyContent={'space-between'} alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 80px'}>
|
||||
{' '}
|
||||
{t('common:core.app.schedule.Open schedule')}
|
||||
</FormLabel>
|
||||
<FormLabel flex={'0 0 80px'}>{t('common:core.app.schedule.Open schedule')}</FormLabel>
|
||||
<Switch
|
||||
isChecked={isOpenSchedule}
|
||||
onChange={(e) => {
|
||||
@@ -297,7 +308,7 @@ const ScheduledTriggerConfig = ({
|
||||
{isOpenSchedule && (
|
||||
<>
|
||||
<Flex alignItems={'center'} mt={5}>
|
||||
<FormLabel flex={'0 0 80px'}>执行时间</FormLabel>
|
||||
<FormLabel flex={'0 0 80px'}>{t('app:execute_time')}</FormLabel>
|
||||
<Box flex={'1 0 0'}>
|
||||
<MultipleRowSelect
|
||||
label={formatLabel}
|
||||
@@ -310,7 +321,7 @@ const ScheduledTriggerConfig = ({
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={5}>
|
||||
<FormLabel flex={'0 0 80px'}>时区</FormLabel>
|
||||
<FormLabel flex={'0 0 80px'}>{t('app:time_zone')}</FormLabel>
|
||||
<Box flex={'1 0 0'}>
|
||||
<TimezoneSelect
|
||||
value={timezone}
|
||||
|
@@ -6,12 +6,13 @@ import { ChatBoxContext } from '../Provider';
|
||||
import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
const isLLMNode = (item: ChatHistoryItemResType) =>
|
||||
item.moduleType === FlowNodeTypeEnum.chatNode || item.moduleType === FlowNodeTypeEnum.tools;
|
||||
|
||||
const ContextModal = ({ onClose, dataId }: { onClose: () => void; dataId: string }) => {
|
||||
const { getHistoryResponseData } = useContextSelector(ChatBoxContext, (v) => v);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { loading: isLoading, data: contextModalData } = useRequest2(
|
||||
() =>
|
||||
getHistoryResponseData({ dataId }).then((res) => {
|
||||
@@ -34,7 +35,7 @@ const ContextModal = ({ onClose, dataId }: { onClose: () => void; dataId: string
|
||||
onClose={onClose}
|
||||
isLoading={isLoading}
|
||||
iconSrc="/imgs/modal/chatHistory.svg"
|
||||
title={`上下文预览(${contextModalData?.length || 0}条)`}
|
||||
title={t('chat:contextual_preview', { num: contextModalData?.length || 0 })}
|
||||
h={['90vh', '80vh']}
|
||||
minW={['90vw', '600px']}
|
||||
isCentered
|
||||
|
@@ -175,28 +175,28 @@ const ResponseTags = ({
|
||||
{showDetail && (
|
||||
<Flex alignItems={'center'} mt={3} flexWrap={'wrap'} gap={2}>
|
||||
{quoteList.length > 0 && (
|
||||
<MyTooltip label="查看引用">
|
||||
<MyTooltip label={t('chat:view_citations')}>
|
||||
<MyTag
|
||||
colorSchema="blue"
|
||||
type="borderSolid"
|
||||
cursor={'pointer'}
|
||||
onClick={() => setQuoteModalData({ rawSearch: quoteList })}
|
||||
>
|
||||
{quoteList.length}条引用
|
||||
{t('chat:citations', { num: quoteList.length })}
|
||||
</MyTag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{llmModuleAccount === 1 && (
|
||||
<>
|
||||
{historyPreviewLength > 0 && (
|
||||
<MyTooltip label={'点击查看上下文预览'}>
|
||||
<MyTooltip label={t('chat:click_contextual_preview')}>
|
||||
<MyTag
|
||||
colorSchema="green"
|
||||
cursor={'pointer'}
|
||||
type="borderSolid"
|
||||
onClick={onOpenContextModal}
|
||||
>
|
||||
{historyPreviewLength}条上下文
|
||||
{t('chat:contextual', { num: historyPreviewLength })}
|
||||
</MyTag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
@@ -204,12 +204,12 @@ const ResponseTags = ({
|
||||
)}
|
||||
{llmModuleAccount > 1 && (
|
||||
<MyTag type="borderSolid" colorSchema="blue">
|
||||
多组 AI 对话
|
||||
{t('chat:multiple_AI_conversations')}
|
||||
</MyTag>
|
||||
)}
|
||||
|
||||
{isPc && runningTime > 0 && (
|
||||
<MyTooltip label={'模块运行时间和'}>
|
||||
<MyTooltip label={t('chat:module_runtime_and')}>
|
||||
<MyTag colorSchema="purple" type="borderSolid" cursor={'default'}>
|
||||
{runningTime}s
|
||||
</MyTag>
|
||||
|
@@ -90,7 +90,7 @@ const SelectMarkCollection = ({
|
||||
})()
|
||||
)}
|
||||
</Grid>
|
||||
{datasets.length === 0 && <EmptyTip text={'这个目录已经没东西可选了~'}></EmptyTip>}
|
||||
{datasets.length === 0 && <EmptyTip text={t('chat:empty_directory')}></EmptyTip>}
|
||||
</ModalBody>
|
||||
</DatasetSelectModal>
|
||||
)}
|
||||
|
@@ -4,8 +4,9 @@ import { useCallback } from 'react';
|
||||
import { htmlTemplate } from '@/web/core/chat/constants';
|
||||
import { fileDownload } from '@/web/common/file/utils';
|
||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
export const useChatBox = () => {
|
||||
const { t } = useTranslation();
|
||||
const onExportChat = useCallback(
|
||||
({ type, history }: { type: ExportChatType; history: ChatItemType[] }) => {
|
||||
const getHistoryHtml = () => {
|
||||
@@ -74,7 +75,7 @@ ${JSON.stringify(item.tools, null, 2)}
|
||||
fileDownload({
|
||||
text: html,
|
||||
type: 'text/html',
|
||||
filename: '聊天记录.html'
|
||||
filename: `${t('chat:chat_history')}.html`
|
||||
});
|
||||
},
|
||||
pdf: () => {
|
||||
@@ -84,7 +85,7 @@ ${JSON.stringify(item.tools, null, 2)}
|
||||
// @ts-ignore
|
||||
html2pdf(html, {
|
||||
margin: 0,
|
||||
filename: `聊天记录.pdf`
|
||||
filename: `${t('chat:chat_history')}.pdf`
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@@ -372,7 +372,7 @@ const ChatBox = (
|
||||
if (!onStartChat) return;
|
||||
if (isChatting) {
|
||||
toast({
|
||||
title: '正在聊天中...请等待结束',
|
||||
title: t('chat:is_chatting'),
|
||||
status: 'warning'
|
||||
});
|
||||
return;
|
||||
@@ -384,7 +384,7 @@ const ChatBox = (
|
||||
|
||||
if (!text && files.length === 0) {
|
||||
toast({
|
||||
title: '内容为空',
|
||||
title: t('chat:content_empty'),
|
||||
status: 'warning'
|
||||
});
|
||||
return;
|
||||
|
@@ -5,10 +5,10 @@ import { PluginRunContext } from '../context';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import AIResponseBox from '../../../components/AIResponseBox';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
const RenderOutput = () => {
|
||||
const { histories, isChatting } = useContextSelector(PluginRunContext, (v) => v);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const pluginOutputs = useMemo(() => {
|
||||
const pluginOutputs = histories?.[1]?.responseData?.find(
|
||||
(item) => item.moduleType === FlowNodeTypeEnum.pluginOutput
|
||||
@@ -22,7 +22,7 @@ const RenderOutput = () => {
|
||||
<Box border={'base'} rounded={'md'} bg={'myGray.25'}>
|
||||
<Box p={4} color={'myGray.900'}>
|
||||
<Box color={'myGray.900'} fontWeight={'bold'}>
|
||||
流输出
|
||||
{t('chat:stream_output')}
|
||||
</Box>
|
||||
{histories.length > 0 && histories[1]?.value.length > 0 ? (
|
||||
<Box mt={2}>
|
||||
@@ -46,7 +46,7 @@ const RenderOutput = () => {
|
||||
</Box>
|
||||
<Box border={'base'} mt={4} rounded={'md'} bg={'myGray.25'}>
|
||||
<Box p={4} color={'myGray.900'} fontWeight={'bold'}>
|
||||
<Box>插件输出</Box>
|
||||
<Box>{t('chat:plugins_output')}</Box>
|
||||
{histories.length > 0 && histories[1].responseData ? (
|
||||
<Markdown source={`~~~json\n${pluginOutputs}`} />
|
||||
) : null}
|
||||
|
@@ -3,14 +3,14 @@ import React from 'react';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { PluginRunContext } from '../context';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
const RenderResponseDetail = () => {
|
||||
const { histories, isChatting } = useContextSelector(PluginRunContext, (v) => v);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const responseData = histories?.[1]?.responseData || [];
|
||||
|
||||
return isChatting ? (
|
||||
<>{'进行中'}</>
|
||||
<>{t('chat:in_progress')}</>
|
||||
) : (
|
||||
<Box flex={'1 0 0'} h={'100%'} overflow={'auto'}>
|
||||
<ResponseBox useMobile={true} response={responseData} showDetail={true} />
|
||||
|
@@ -11,7 +11,7 @@ import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/c
|
||||
import { generatingMessageProps } from '../type';
|
||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { getPluginRunContent } from '@fastgpt/global/core/app/plugin/utils';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
type PluginRunContextType = PluginRunBoxProps & {
|
||||
isChatting: boolean;
|
||||
onSubmit: (e: FieldValues) => Promise<any>;
|
||||
@@ -44,7 +44,7 @@ const PluginRunContextProvider = ({
|
||||
|
||||
const { toast } = useToast();
|
||||
const chatController = useRef(new AbortController());
|
||||
|
||||
const { t } = useTranslation();
|
||||
/* Abort chat completions, questionGuide */
|
||||
const abortRequest = useCallback(() => {
|
||||
chatController.current?.abort('stop');
|
||||
@@ -148,7 +148,7 @@ const PluginRunContextProvider = ({
|
||||
if (!onStartChat) return;
|
||||
if (isChatting) {
|
||||
toast({
|
||||
title: '正在聊天中...请等待结束',
|
||||
title: t('chat:is_chatting'),
|
||||
status: 'warning'
|
||||
});
|
||||
return;
|
||||
|
@@ -119,7 +119,7 @@ const WholeResponseModal = ({
|
||||
title={
|
||||
<Flex alignItems={'center'}>
|
||||
{t('common:core.chat.response.Complete Response')}
|
||||
<QuestionTip ml={2} label={'从上到下,为各个模块的响应顺序'}></QuestionTip>
|
||||
<QuestionTip ml={2} label={t('chat:question_tip')}></QuestionTip>
|
||||
</Flex>
|
||||
}
|
||||
>
|
||||
|
@@ -62,7 +62,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
|
||||
const [apiKey, setApiKey] = useState('');
|
||||
const { ConfirmModal, openConfirm } = useConfirm({
|
||||
type: 'delete',
|
||||
content: '确认删除该API密钥?删除后该密钥立即失效,对应的对话日志不会删除,请确认!'
|
||||
content: t('workflow:delete_api')
|
||||
});
|
||||
|
||||
const { mutate: onclickRemove, isLoading: isDeleting } = useMutation({
|
||||
@@ -318,7 +318,7 @@ function EditKeyModal({
|
||||
|
||||
const { mutate: onclickCreate, isLoading: creating } = useRequest({
|
||||
mutationFn: async (e: EditProps) => createAOpenApiKey(e),
|
||||
errorToast: '创建链接异常',
|
||||
errorToast: t('workflow:create_link_error'),
|
||||
onSuccess: onCreate
|
||||
});
|
||||
const { mutate: onclickUpdate, isLoading: updating } = useRequest({
|
||||
@@ -326,7 +326,7 @@ function EditKeyModal({
|
||||
//@ts-ignore
|
||||
return putOpenApiKey(e);
|
||||
},
|
||||
errorToast: '更新链接异常',
|
||||
errorToast: t('workflow:update_link_error'),
|
||||
onSuccess: onEdit
|
||||
});
|
||||
|
||||
|
@@ -73,7 +73,7 @@ const LafAccountModal = ({
|
||||
onError: (err) => {
|
||||
onResetForm();
|
||||
toast({
|
||||
title: getErrText(err, '获取应用列表失败'),
|
||||
title: getErrText(err, t('common:get_app_failed')),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
@@ -132,7 +132,7 @@ const LafAccountModal = ({
|
||||
}}
|
||||
isLoading={isPatLoading}
|
||||
>
|
||||
验证
|
||||
{t('common:verification')}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
@@ -145,7 +145,7 @@ const LafAccountModal = ({
|
||||
});
|
||||
}}
|
||||
>
|
||||
已验证,点击取消绑定
|
||||
{t('common:has_verification')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
@@ -5,7 +5,7 @@ import type { PermissionValueType } from '@fastgpt/global/support/permission/typ
|
||||
import { ReadPermissionVal, WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export enum defaultPermissionEnum {
|
||||
private = 'private',
|
||||
@@ -34,12 +34,11 @@ const DefaultPermissionList = ({
|
||||
...styles
|
||||
}: Props) => {
|
||||
const { ConfirmModal, openConfirm } = useConfirm({});
|
||||
const { commonT } = useI18n();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const defaultPermissionSelectList = [
|
||||
{ label: '仅协作者访问', value: defaultPer },
|
||||
{ label: '团队可访问', value: readPer },
|
||||
{ label: '团队可编辑', value: writePer }
|
||||
{ label: t('user:permission.only_collaborators'), value: defaultPer },
|
||||
{ label: t('user:permission.team_read'), value: readPer },
|
||||
{ label: t('user:permission.team_write'), value: writePer }
|
||||
];
|
||||
|
||||
const { runAsync: onRequestChange, loading } = useRequest2((v: PermissionValueType) =>
|
||||
@@ -58,7 +57,7 @@ const DefaultPermissionList = ({
|
||||
openConfirm(
|
||||
() => onRequestChange(per),
|
||||
undefined,
|
||||
commonT('permission.Remove InheritPermission Confirm')
|
||||
t('common:permission.Remove InheritPermission Confirm')
|
||||
)();
|
||||
} else {
|
||||
return onRequestChange(per);
|
||||
|
@@ -79,7 +79,13 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
|
||||
});
|
||||
|
||||
return (
|
||||
<MyModal isOpen onClose={onClose} iconSrc="modal/AddClb" title="添加协作者" minW="800px">
|
||||
<MyModal
|
||||
isOpen
|
||||
onClose={onClose}
|
||||
iconSrc="modal/AddClb"
|
||||
title={t('user:team.add_collaborator')}
|
||||
minW="800px"
|
||||
>
|
||||
<ModalBody>
|
||||
<MyBox
|
||||
isLoading={loadingMembers}
|
||||
@@ -103,7 +109,7 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
|
||||
<MyIcon name="common/searchLight" w="16px" color={'myGray.500'} />
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
placeholder="搜索用户名"
|
||||
placeholder={t('user:search_user')}
|
||||
bgColor="myGray.50"
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
/>
|
||||
@@ -156,7 +162,9 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex p="4" flexDirection="column">
|
||||
<Box>已选: {selectedMemberIdList.length}</Box>
|
||||
<Box>
|
||||
{t('user:has_chosen') + ': '}+ {selectedMemberIdList.length}
|
||||
</Box>
|
||||
<Flex flexDirection="column" mt="2">
|
||||
{selectedMemberIdList.map((tmbId) => {
|
||||
const member = filterMembers.find((v) => v.tmbId === tmbId);
|
||||
@@ -211,7 +219,7 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
|
||||
onChange={(v) => setSelectedPermission(v)}
|
||||
/>
|
||||
<Button isLoading={isUpdating} ml="4" h={'32px'} onClick={onConfirm}>
|
||||
确认
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
|
@@ -23,12 +23,13 @@ import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
import Loading from '@fastgpt/web/components/common/MyLoading';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
export type ManageModalProps = {
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
function ManageModal({ onClose }: ManageModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const { userInfo } = useUserStore();
|
||||
const { permission, collaboratorList, onUpdateCollaborators, onDelOneCollaborator } =
|
||||
useContextSelector(CollaboratorContext, (v) => v);
|
||||
@@ -44,23 +45,29 @@ function ManageModal({ onClose }: ManageModalProps) {
|
||||
permission: per
|
||||
});
|
||||
},
|
||||
successToast: '更新成功',
|
||||
successToast: t('common.Update Success'),
|
||||
errorToast: 'Error'
|
||||
});
|
||||
|
||||
const loading = isDeleting || isUpdating;
|
||||
|
||||
return (
|
||||
<MyModal isOpen onClose={onClose} minW="600px" title="管理协作者" iconSrc="common/settingLight">
|
||||
<MyModal
|
||||
isOpen
|
||||
onClose={onClose}
|
||||
minW="600px"
|
||||
title={t('user:team.manage_collaborators')}
|
||||
iconSrc="common/settingLight"
|
||||
>
|
||||
<ModalBody>
|
||||
<TableContainer borderRadius="md" minH="400px">
|
||||
<Table>
|
||||
<Thead bg="myGray.100">
|
||||
<Tr>
|
||||
<Th border="none">名称</Th>
|
||||
<Th border="none">权限</Th>
|
||||
<Th border="none">{t('user:name')}</Th>
|
||||
<Th border="none">{t('user:permissions')}</Th>
|
||||
<Th border="none" w={'40px'}>
|
||||
操作
|
||||
{t('user:operations')}
|
||||
</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
@@ -109,7 +116,7 @@ function ManageModal({ onClose }: ManageModalProps) {
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
{collaboratorList?.length === 0 && <EmptyTip text={'暂无协作者'} />}
|
||||
{collaboratorList?.length === 0 && <EmptyTip text={t('user:team.no_collaborators')} />}
|
||||
</TableContainer>
|
||||
{loading && <Loading fixed={false} />}
|
||||
</ModalBody>
|
||||
|
@@ -47,8 +47,8 @@ function AddManagerModal({ onClose, onSuccess }: { onClose: () => void; onSucces
|
||||
refetchMembers();
|
||||
onSuccess();
|
||||
},
|
||||
successToast: '成功',
|
||||
errorToast: '失败'
|
||||
successToast: t('common:common.Success'),
|
||||
errorToast: t('common:common.failed')
|
||||
});
|
||||
|
||||
const filterMembers = useMemo(() => {
|
||||
@@ -83,7 +83,7 @@ function AddManagerModal({ onClose, onSuccess }: { onClose: () => void; onSucces
|
||||
<MyIcon name="common/searchLight" w="16px" color={'myGray.500'} />
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
placeholder="搜索用户名"
|
||||
placeholder={t('user:search_user')}
|
||||
fontSize="sm"
|
||||
bg={'myGray.50'}
|
||||
onChange={(e) => {
|
||||
@@ -120,7 +120,7 @@ function AddManagerModal({ onClose, onSuccess }: { onClose: () => void; onSucces
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex borderLeft="1px" borderColor="myGray.200" flexDirection="column" p="4">
|
||||
<Box mt={3}>已选: {selected.length} 个</Box>
|
||||
<Box mt={3}>{t('common:chosen') + ': ' + selected.length} </Box>
|
||||
<Box mt={5}>
|
||||
{selected.map((member) => {
|
||||
return (
|
||||
|
@@ -31,8 +31,8 @@ function PermissionManage() {
|
||||
mutationFn: async (memberId: string) => {
|
||||
return delMemberPermission(memberId);
|
||||
},
|
||||
successToast: '删除管理员成功',
|
||||
errorToast: '删除管理员异常',
|
||||
successToast: t('user:delete.admin_success'),
|
||||
errorToast: t('user:delete.admin_failed'),
|
||||
onSuccess: () => {
|
||||
refetchMembers();
|
||||
}
|
||||
@@ -75,7 +75,7 @@ function PermissionManage() {
|
||||
onOpenAddManager();
|
||||
}}
|
||||
>
|
||||
添加管理员
|
||||
{t('user:team.Add manager')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
@@ -95,7 +95,7 @@ const TeamTagsAsync = ({ onClose }: { onClose: () => void }) => {
|
||||
<Box>
|
||||
<Box>{teamInfo?.teamName}</Box>
|
||||
<Box color={'myGray.500'} fontSize={'xs'} fontWeight={'normal'}>
|
||||
{'填写标签同步链接,点击同步按钮即可同步'}
|
||||
{t('user:synchronization.title')}
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
@@ -110,7 +110,7 @@ const TeamTagsAsync = ({ onClose }: { onClose: () => void }) => {
|
||||
ml={4}
|
||||
autoFocus
|
||||
bg={'myWhite.600'}
|
||||
placeholder="请输入同步标签"
|
||||
placeholder={t('user:synchronization.placeholder')}
|
||||
{...register('teamDomain', {
|
||||
required: true
|
||||
})}
|
||||
@@ -181,7 +181,7 @@ const TeamTagsAsync = ({ onClose }: { onClose: () => void }) => {
|
||||
leftIcon={<RepeatIcon />}
|
||||
onClick={handleSubmit((data) => onclickTagAsync(data))}
|
||||
>
|
||||
立即同步
|
||||
{t('user:synchronization.button')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
|
@@ -73,7 +73,7 @@ const QRCodePayModal = ({
|
||||
return (
|
||||
<MyModal isOpen title={t('common:user.Pay')} iconSrc="/imgs/modal/pay.svg">
|
||||
<ModalBody textAlign={'center'}>
|
||||
<Box mb={3}>请微信扫码支付: {readPrice}元,请勿关闭页面</Box>
|
||||
<Box mb={3}>{t('common:pay.wechat', { price: readPrice })}</Box>
|
||||
<Box id={'payQRCode'} display={'inline-block'} h={'128px'}></Box>
|
||||
</ModalBody>
|
||||
<ModalFooter />
|
||||
|
@@ -112,13 +112,13 @@ const StandardPlanContentList = ({
|
||||
{!!planContent.permissionReRank && (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'price/right'} w={'16px'} mr={3} />
|
||||
<Box color={'myGray.600'}>检索结果重排</Box>
|
||||
<Box color={'myGray.600'}>{t('chat:rearrangement')}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
{!!planContent.permissionWebsiteSync && (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'price/right'} w={'16px'} mr={3} />
|
||||
<Box color={'myGray.600'}>Web站点同步</Box>
|
||||
<Box color={'myGray.600'}>{t('chat:web_site_sync')}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</Grid>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { InitChatResponse } from './api';
|
||||
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
export const defaultChatData: InitChatResponse = {
|
||||
chatId: '',
|
||||
appId: '',
|
||||
@@ -12,7 +12,7 @@ export const defaultChatData: InitChatResponse = {
|
||||
type: AppTypeEnum.simple,
|
||||
pluginInputs: []
|
||||
},
|
||||
title: '新对话',
|
||||
title: i18nT('chat:new_chat'),
|
||||
variables: {},
|
||||
history: []
|
||||
};
|
||||
|
@@ -8,13 +8,13 @@ import QueryClientContext from '@/web/context/QueryClient';
|
||||
import ChakraUIContext from '@/web/context/ChakraUI';
|
||||
import I18nContextProvider from '@/web/context/I18n';
|
||||
import { useInitApp } from '@/web/context/useInitApp';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import '@/web/styles/reset.scss';
|
||||
import NextHead from '@/components/common/NextHead';
|
||||
|
||||
function App({ Component, pageProps }: AppProps) {
|
||||
const { feConfigs, scripts, title } = useInitApp();
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<NextHead
|
||||
@@ -22,7 +22,7 @@ function App({ Component, pageProps }: AppProps) {
|
||||
desc={
|
||||
feConfigs?.systemDescription ||
|
||||
process.env.SYSTEM_DESCRIPTION ||
|
||||
`${title} 是一个大模型应用编排系统,提供开箱即用的数据处理、模型调用等能力,可以快速的构建知识库并通过 Flow 可视化进行工作流编排,实现复杂的知识库场景!`
|
||||
`${title}${t('app:intro')}`
|
||||
}
|
||||
icon={feConfigs?.favicon || process.env.SYSTEM_FAVICON}
|
||||
/>
|
||||
|
@@ -33,9 +33,10 @@ import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { usePagination } from '@fastgpt/web/hooks/usePagination';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
const BillTable = () => {
|
||||
const { t } = useTranslation();
|
||||
const { commonT } = useI18n();
|
||||
const { toast } = useToast();
|
||||
const [billType, setBillType] = useState<BillTypeEnum | ''>('');
|
||||
const [billDetail, setBillDetail] = useState<BillSchemaType>();
|
||||
@@ -134,7 +135,7 @@ const BillTable = () => {
|
||||
<Td>
|
||||
{item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'}
|
||||
</Td>
|
||||
<Td>{formatStorePrice2Read(item.price)}元</Td>
|
||||
<Td>{commonT('common:pay.yuan', { amount: formatStorePrice2Read(item.price) })}</Td>
|
||||
<Td>{t(billStatusMap[item.status]?.label as any)}</Td>
|
||||
<Td>
|
||||
{item.status === 'NOTPAY' && (
|
||||
@@ -181,7 +182,7 @@ export default BillTable;
|
||||
|
||||
function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () => void }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { commonT } = useI18n();
|
||||
return (
|
||||
<MyModal
|
||||
isOpen={true}
|
||||
@@ -211,7 +212,7 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
|
||||
)}
|
||||
<Flex alignItems={'center'} pb={4}>
|
||||
<FormLabel flex={'0 0 120px'}>{t('common:support.wallet.Amount')}:</FormLabel>
|
||||
<Box>{formatStorePrice2Read(bill.price)}元</Box>
|
||||
<Box>{commonT('common:pay.yuan', { amount: formatStorePrice2Read(bill.price) })}</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} pb={4}>
|
||||
<FormLabel flex={'0 0 120px'}>{t('common:support.wallet.bill.Type')}:</FormLabel>
|
||||
|
@@ -435,7 +435,7 @@ const PlanUsage = () => {
|
||||
<Box ml={2}>{formatTime2YMD(standardPlan?.expiredTime)}</Box>
|
||||
</Flex>
|
||||
<Box mt="2" color={'#485264'} fontSize="sm">
|
||||
免费版用户30天无任何使用记录时,系统会自动清理账号知识库。
|
||||
{t('common:info.free_plan')}
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
@@ -470,9 +470,9 @@ const PlanUsage = () => {
|
||||
>
|
||||
<Flex>
|
||||
<Flex flex={'1 0 0'} alignItems={'flex-end'}>
|
||||
<Box fontSize={'md'}>资源用量</Box>
|
||||
<Box fontSize={'md'}>{t('common:info.resource')}</Box>
|
||||
<Box fontSize={'xs'} color={'myGray.500'}>
|
||||
(包含标准套餐与额外资源包)
|
||||
{t('common:info.include')}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Link
|
||||
@@ -484,7 +484,7 @@ const PlanUsage = () => {
|
||||
cursor={'pointer'}
|
||||
fontSize={'sm'}
|
||||
>
|
||||
购买额外套餐
|
||||
{t('common:info.buy_extra')}
|
||||
<MyIcon ml={1} name={'common/rightArrowLight'} w={'12px'} />
|
||||
</Link>
|
||||
</Flex>
|
||||
@@ -567,7 +567,7 @@ const Other = () => {
|
||||
});
|
||||
reset(data);
|
||||
toast({
|
||||
title: '更新数据成功',
|
||||
title: t('common:dataset.data.Update Success Tip'),
|
||||
status: 'success'
|
||||
});
|
||||
},
|
||||
@@ -637,7 +637,7 @@ const Other = () => {
|
||||
>
|
||||
<Image src="/imgs/workflow/laf.png" w={'18px'} alt="laf" />
|
||||
<Box ml={2} flex={1}>
|
||||
laf 账号
|
||||
{'laf' + t('common:navbar.Account')}
|
||||
</Box>
|
||||
<Box
|
||||
w={'9px'}
|
||||
@@ -664,7 +664,7 @@ const Other = () => {
|
||||
>
|
||||
<MyIcon name={'common/openai'} w={'18px'} color={'myGray.600'} />
|
||||
<Box ml={2} flex={1}>
|
||||
OpenAI/OneAPI 账号
|
||||
{'OpenAI / OneAPI' + t('common:navbar.Account')}
|
||||
</Box>
|
||||
<Box
|
||||
w={'9px'}
|
||||
|
@@ -81,7 +81,9 @@ const InformTable = () => {
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
{!isLoading && informs.length === 0 && <EmptyTip text={'暂无通知~'}></EmptyTip>}
|
||||
{!isLoading && informs.length === 0 && (
|
||||
<EmptyTip text={t('common:user.no_notice')}></EmptyTip>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{total > pageSize && (
|
||||
|
@@ -37,9 +37,7 @@ const OpenAIAccountModal = ({
|
||||
>
|
||||
<ModalBody>
|
||||
<Box fontSize={'sm'} color={'myGray.500'}>
|
||||
可以填写 OpenAI/OneAPI
|
||||
的相关秘钥。如果你填写了该内容,在线上平台使用【AI对话】、【问题分类】和【内容提取】将会走你填写的Key,不会计费。请注意你的
|
||||
Key 是否有访问对应模型的权限。GPT模型可以选择 FastAI。
|
||||
{t('common:info.open_api_notice')}
|
||||
</Box>
|
||||
<Flex alignItems={'center'} mt={5}>
|
||||
<Box flex={'0 0 65px'}>API Key:</Box>
|
||||
@@ -50,16 +48,16 @@ const OpenAIAccountModal = ({
|
||||
<Input
|
||||
flex={1}
|
||||
{...register('baseUrl')}
|
||||
placeholder={'请求地址,默认为 openai 官方。可填中转地址,未自动补全 "v1"'}
|
||||
placeholder={t('common:info.open_api_placeholder')}
|
||||
></Input>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
|
||||
取消
|
||||
{t('common:common.Cancel')}
|
||||
</Button>
|
||||
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>
|
||||
确认
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
|
@@ -7,7 +7,6 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
|
||||
import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { EXTRA_PLAN_CARD_ROUTE } from '@/web/support/wallet/sub/constants';
|
||||
@@ -27,7 +26,6 @@ const PayModal = ({
|
||||
const [inputVal, setInputVal] = useState<number | undefined>(defaultValue);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [qrPayData, setQRPayData] = useState<QRPayProps>();
|
||||
|
||||
const handleClickPay = useCallback(async () => {
|
||||
if (!inputVal || inputVal <= 0 || isNaN(+inputVal)) return;
|
||||
setLoading(true);
|
||||
@@ -79,7 +77,7 @@ const PayModal = ({
|
||||
variant={item === inputVal ? 'solid' : 'outline'}
|
||||
onClick={() => setInputVal(item)}
|
||||
>
|
||||
{item}元
|
||||
{t('common:pay.yuan', { amount: item })}
|
||||
</Button>
|
||||
))}
|
||||
</Grid>
|
||||
@@ -88,7 +86,7 @@ const PayModal = ({
|
||||
value={inputVal}
|
||||
type={'number'}
|
||||
step={1}
|
||||
placeholder={'其他金额,请取整数'}
|
||||
placeholder={t('common:pay.other')}
|
||||
onChange={(e) => {
|
||||
setInputVal(Math.floor(+e.target.value));
|
||||
}}
|
||||
@@ -106,7 +104,7 @@ const PayModal = ({
|
||||
isDisabled={!inputVal || inputVal === 0}
|
||||
onClick={handleClickPay}
|
||||
>
|
||||
获取充值二维码
|
||||
{t('common:pay.get_pay_QR')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
|
@@ -105,9 +105,9 @@ const Promotion = () => {
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>时间</Th>
|
||||
<Th>类型</Th>
|
||||
<Th>金额(¥)</Th>
|
||||
<Th>{t('common:user.Time')}</Th>
|
||||
<Th>{t('common:user.type')}</Th>
|
||||
<Th>{t('common:pay.amount')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody fontSize={'sm'}>
|
||||
@@ -124,7 +124,9 @@ const Promotion = () => {
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
{!isLoading && promotionRecords.length === 0 && <EmptyTip text="无邀请记录~"></EmptyTip>}
|
||||
{!isLoading && promotionRecords.length === 0 && (
|
||||
<EmptyTip text={t('common:user.no_invite_records')}></EmptyTip>
|
||||
)}
|
||||
{total > pageSize && (
|
||||
<Flex mt={4} justifyContent={'flex-end'}>
|
||||
<Pagination />
|
||||
|
@@ -70,16 +70,16 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => {
|
||||
flex={1}
|
||||
bg={'myGray.50'}
|
||||
{...register('account', { required: true })}
|
||||
placeholder={t('common:support.user.Email Or Phone')}
|
||||
placeholder={t('user:password.email_phone')}
|
||||
></Input>
|
||||
</Flex>
|
||||
<Flex mt="6" alignItems="center" position={'relative'}>
|
||||
<Box flex={'0 0 70px'}>{t('common:support.user.Verify Code')}</Box>
|
||||
<Box flex={'0 0 70px'}>{t('user:password.verification_code')}</Box>
|
||||
<Input
|
||||
flex={1}
|
||||
bg={'myGray.50'}
|
||||
{...register('verifyCode', { required: true })}
|
||||
placeholder={t('common:support.user.Verify Code')}
|
||||
placeholder={t('user:password.code_required')}
|
||||
></Input>
|
||||
<Box
|
||||
position={'absolute'}
|
||||
|
@@ -45,11 +45,11 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
||||
>
|
||||
<ModalBody>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box flex={'0 0 70px'}>旧密码:</Box>
|
||||
<Box flex={'0 0 70px'}>{t('common:user.old_password') + ':'}</Box>
|
||||
<Input flex={1} type={'password'} {...register('oldPsw', { required: true })}></Input>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={5}>
|
||||
<Box flex={'0 0 70px'}>新密码:</Box>
|
||||
<Box flex={'0 0 70px'}>{t('common:user.new_password') + ':'}</Box>
|
||||
<Input
|
||||
flex={1}
|
||||
type={'password'}
|
||||
@@ -57,13 +57,13 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
||||
required: true,
|
||||
maxLength: {
|
||||
value: 60,
|
||||
message: '密码最少 4 位最多 60 位'
|
||||
message: t('common:user.password_message')
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={5}>
|
||||
<Box flex={'0 0 70px'}>确认密码:</Box>
|
||||
<Box flex={'0 0 70px'}>{t('common:user.confirm_password') + ':'}</Box>
|
||||
<Input
|
||||
flex={1}
|
||||
type={'password'}
|
||||
@@ -71,7 +71,7 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
||||
required: true,
|
||||
maxLength: {
|
||||
value: 60,
|
||||
message: '密码最少 4 位最多 60 位'
|
||||
message: t('common:user.password_message')
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
@@ -79,10 +79,10 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
|
||||
取消
|
||||
{t('common:common.Cancel')}
|
||||
</Button>
|
||||
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>
|
||||
确认
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
|
@@ -174,7 +174,7 @@ const UsageTable = () => {
|
||||
<Td>{formatNumber(item.totalPoints) || 0}</Td>
|
||||
<Td>
|
||||
<Button size={'sm'} variant={'whitePrimary'} onClick={() => setUsageDetail(item)}>
|
||||
详情
|
||||
{t('common:common.Detail')}
|
||||
</Button>
|
||||
</Td>
|
||||
</Tr>
|
||||
@@ -183,7 +183,9 @@ const UsageTable = () => {
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
{!isLoading && usages.length === 0 && <EmptyTip text="无使用记录~"></EmptyTip>}
|
||||
{!isLoading && usages.length === 0 && (
|
||||
<EmptyTip text={t('common:user.no_usage_records')}></EmptyTip>
|
||||
)}
|
||||
|
||||
<Loading loading={isLoading} fixed={false} />
|
||||
{!!usageDetail && (
|
||||
|
@@ -77,10 +77,12 @@ const StandDetailModal = ({ onClose }: { onClose: () => void }) => {
|
||||
{currentSubLevel &&
|
||||
`(${t(standardSubLevelMap[currentSubLevel]?.label as any)})`}
|
||||
</Td>
|
||||
<Td>{datasetSize ? `${datasetSize}组` : '-'}</Td>
|
||||
<Td>
|
||||
{datasetSize ? `${datasetSize + t('common:core.dataset.data.group')}` : '-'}
|
||||
</Td>
|
||||
<Td>
|
||||
{totalPoints
|
||||
? `${Math.round(totalPoints - surplusPoints)} / ${totalPoints} 积分`
|
||||
? `${Math.round(totalPoints - surplusPoints)} / ${totalPoints} ${t('common:support.wallet.subscription.point')}`
|
||||
: '-'}
|
||||
</Td>
|
||||
<Td>{formatTime2YMDHM(startTime)}</Td>
|
||||
|
@@ -104,7 +104,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
|
||||
];
|
||||
|
||||
const { openConfirm, ConfirmModal } = useConfirm({
|
||||
content: '确认退出登录?'
|
||||
content: t('common:support.user.logout.confirm')
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
@@ -14,6 +14,7 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { transformPreviewHistories } from '@/global/core/chat/utils';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
@@ -61,7 +62,7 @@ async function handler(
|
||||
return {
|
||||
chatId,
|
||||
appId,
|
||||
title: chat?.title || '新对话',
|
||||
title: chat?.title || i18nT('chat:new_chat'),
|
||||
userAvatar: undefined,
|
||||
variables: chat?.variables || {},
|
||||
history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories),
|
||||
|
@@ -18,6 +18,7 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { transformPreviewHistories } from '@/global/core/chat/utils';
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
@@ -69,7 +70,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
data: {
|
||||
chatId,
|
||||
appId: app._id,
|
||||
title: chat?.title || '新对话',
|
||||
title: chat?.title || i18nT('chat:new_chat'),
|
||||
//@ts-ignore
|
||||
userAvatar: tmb?.userId?.avatar,
|
||||
variables: chat?.variables || {},
|
||||
|
@@ -18,6 +18,7 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { transformPreviewHistories } from '@/global/core/chat/utils';
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
@@ -72,7 +73,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
data: {
|
||||
chatId,
|
||||
appId,
|
||||
title: chat?.title || '新对话',
|
||||
title: chat?.title || i18nT('chat:new_chat'),
|
||||
userAvatar: team?.avatar,
|
||||
variables: chat?.variables || {},
|
||||
history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories),
|
||||
|
@@ -146,7 +146,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
||||
() => resumeInheritPer(appDetail._id),
|
||||
// () => putAppById(appDetail._id, { inheritPermission: true }),
|
||||
{
|
||||
errorToast: '恢复失败',
|
||||
errorToast: t('common:resume_failed'),
|
||||
onSuccess: () => {
|
||||
reloadApp();
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@ import CloseIcon from '@fastgpt/web/components/common/Icon/close';
|
||||
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
|
||||
|
||||
const DetailLogsModal = ({
|
||||
@@ -32,7 +31,6 @@ const DetailLogsModal = ({
|
||||
const { t } = useTranslation();
|
||||
const { isPc } = useSystem();
|
||||
const theme = useTheme();
|
||||
|
||||
const {
|
||||
ChatBoxRef,
|
||||
chatRecords,
|
||||
@@ -101,7 +99,7 @@ const DetailLogsModal = ({
|
||||
...(chatRecords.length > 0
|
||||
? [
|
||||
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output },
|
||||
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
|
||||
{ label: t('common:common.all_result'), value: PluginRunBoxTabEnum.detail }
|
||||
]
|
||||
: [])
|
||||
]}
|
||||
@@ -133,7 +131,9 @@ const DetailLogsModal = ({
|
||||
<>
|
||||
<MyTag colorSchema="blue">
|
||||
<MyIcon name={'history'} w={'14px'} />
|
||||
<Box ml={1}>{`${chatRecords.length}条记录`}</Box>
|
||||
<Box ml={1}>
|
||||
{t('common:core.chat.History Amount', { amount: chatRecords.length })}
|
||||
</Box>
|
||||
</MyTag>
|
||||
{!!chatModels && (
|
||||
<MyTag ml={2} colorSchema={'green'}>
|
||||
|
@@ -122,7 +122,7 @@ const Logs = () => {
|
||||
key={item._id}
|
||||
_hover={{ bg: 'myWhite.600' }}
|
||||
cursor={'pointer'}
|
||||
title={'点击查看对话详情'}
|
||||
title={t('common:core.view_chat_detail')}
|
||||
onClick={() => setDetailLogsId(item.id)}
|
||||
>
|
||||
<Td>
|
||||
|
@@ -13,6 +13,7 @@ import dynamic from 'next/dynamic';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
import Flow from '../WorkflowComponents/Flow';
|
||||
import { t } from 'i18next';
|
||||
const Logs = dynamic(() => import('../Logs/index'));
|
||||
const PublishChannel = dynamic(() => import('../Publish'));
|
||||
|
||||
@@ -22,8 +23,7 @@ const WorkflowEdit = () => {
|
||||
|
||||
const { openConfirm, ConfirmModal } = useConfirm({
|
||||
showCancel: false,
|
||||
content:
|
||||
'检测到您的高级编排为旧版,系统将为您自动格式化成新版工作流。\n\n由于版本差异较大,会导致一些工作流无法正常排布,请重新手动连接工作流。如仍异常,可尝试删除对应节点后重新添加。\n\n你可以直接点击调试进行工作流测试,调试完毕后点击发布。直到你点击发布,新工作流才会真正保存生效。\n\n在你发布新工作流前,自动保存不会生效。'
|
||||
content: t('common:info.old_version_attention')
|
||||
});
|
||||
|
||||
const initData = useContextSelector(WorkflowContext, (v) => v.initData);
|
||||
|
@@ -123,8 +123,7 @@ const FeiShuEditModal = ({
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={4}>
|
||||
<Flex flex={'0 0 90px'} alignItems={'center'}>
|
||||
默认回复
|
||||
{/* TODO: i18n */}
|
||||
{t('common:default_reply')}
|
||||
</Flex>
|
||||
<Input
|
||||
placeholder={publishT('default_response') || 'link_name'}
|
||||
@@ -136,8 +135,7 @@ const FeiShuEditModal = ({
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={4}>
|
||||
<Flex flex={'0 0 90px'} alignItems={'center'}>
|
||||
立即回复
|
||||
{/* TODO: i18n */}
|
||||
{t('common:reply_now')}
|
||||
</Flex>
|
||||
<Input
|
||||
placeholder={publishT('default_response') || 'link_name'}
|
||||
|
@@ -13,6 +13,7 @@ import dynamic from 'next/dynamic';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
import Flow from '../WorkflowComponents/Flow';
|
||||
import { t } from 'i18next';
|
||||
const Logs = dynamic(() => import('../Logs/index'));
|
||||
const PublishChannel = dynamic(() => import('../Publish'));
|
||||
|
||||
@@ -22,8 +23,7 @@ const WorkflowEdit = () => {
|
||||
|
||||
const { openConfirm, ConfirmModal } = useConfirm({
|
||||
showCancel: false,
|
||||
content:
|
||||
'检测到您的高级编排为旧版,系统将为您自动格式化成新版工作流。\n\n由于版本差异较大,会导致一些工作流无法正常排布,请重新手动连接工作流。如仍异常,可尝试删除对应节点后重新添加。\n\n你可以直接点击调试进行工作流测试,调试完毕后点击发布。直到你点击发布,新工作流才会真正保存生效。\n\n在你发布新工作流前,自动保存不会生效。'
|
||||
content: t('common:info.old_version_attention')
|
||||
});
|
||||
|
||||
const initData = useContextSelector(WorkflowContext, (v) => v.initData);
|
||||
|
@@ -77,7 +77,7 @@ const ChatTest = ({
|
||||
...(chatRecords.length > 0
|
||||
? [
|
||||
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output },
|
||||
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
|
||||
{ label: t('common:common.all_result'), value: PluginRunBoxTabEnum.detail }
|
||||
]
|
||||
: [])
|
||||
]}
|
||||
|
@@ -5,7 +5,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../context';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
type Props = {
|
||||
onClose: () => void;
|
||||
};
|
||||
@@ -15,7 +15,7 @@ const ImportSettings = ({ onClose }: Props) => {
|
||||
const { toast } = useToast();
|
||||
const initData = useContextSelector(WorkflowContext, (v) => v.initData);
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<MyModal
|
||||
isOpen
|
||||
@@ -50,7 +50,7 @@ const ImportSettings = ({ onClose }: Props) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
确认
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
|
@@ -263,7 +263,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
|
||||
onClick={() => router.push('/app/list')}
|
||||
gap={1}
|
||||
>
|
||||
<Box>去创建</Box>
|
||||
<Box>{t('common:create')}</Box>
|
||||
<MyIcon name={'common/rightArrowLight'} w={'0.8rem'} />
|
||||
</Flex>
|
||||
)}
|
||||
@@ -279,7 +279,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
|
||||
onClick={() => window.open(feConfigs.systemPluginCourseUrl)}
|
||||
gap={1}
|
||||
>
|
||||
<Box>贡献插件</Box>
|
||||
<Box>{t('common:plugin.contribute')}</Box>
|
||||
<MyIcon name={'common/rightArrowLight'} w={'0.8rem'} />
|
||||
</Flex>
|
||||
)}
|
||||
|
@@ -47,7 +47,7 @@ const SelectAppModal = ({
|
||||
return (
|
||||
<MyModal
|
||||
isOpen
|
||||
title={`选择应用`}
|
||||
title={t('common:core.module.Select app')}
|
||||
iconSrc="/imgs/workflow/ai.svg"
|
||||
onClose={onClose}
|
||||
position={'relative'}
|
||||
|
@@ -261,7 +261,7 @@ export const useDebug = () => {
|
||||
})}
|
||||
</Box>
|
||||
<Flex py={2} justifyContent={'flex-end'} px={6}>
|
||||
<Button onClick={handleSubmit(onClickRun)}>运行</Button>
|
||||
<Button onClick={handleSubmit(onClickRun)}>{t('common:common.Run')}</Button>
|
||||
</Flex>
|
||||
</MyRightDrawer>
|
||||
);
|
||||
|
@@ -25,6 +25,7 @@ import { connectionLineStyle, defaultEdgeOptions } from '../constants';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../context';
|
||||
import { useWorkflow } from './hooks/useWorkflow';
|
||||
import { t } from 'i18next';
|
||||
|
||||
const NodeSimple = dynamic(() => import('./nodes/NodeSimple'));
|
||||
const nodeTypes: Record<FlowNodeTypeEnum, any> = {
|
||||
@@ -171,7 +172,7 @@ const FlowController = React.memo(function FlowController() {
|
||||
showInteractive={false}
|
||||
showFitView={false}
|
||||
>
|
||||
<MyTooltip label={'页面居中'}>
|
||||
<MyTooltip label={t('common:common.page_center')}>
|
||||
<ControlButton className="custom-workflow-fix_view" onClick={() => fitView()}>
|
||||
<MyIcon name={'core/modules/fixview'} w={'14px'} />
|
||||
</ControlButton>
|
||||
|
@@ -64,7 +64,7 @@ const NodeCQNode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
/>
|
||||
</MyTooltip>
|
||||
<Box flex={1} color={'myGray.600'} fontWeight={'medium'}>
|
||||
分类{i + 1}
|
||||
{t('common:classification') + (i + 1)}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box position={'relative'}>
|
||||
|
@@ -203,7 +203,10 @@ function Reference({
|
||||
/>
|
||||
</Flex>
|
||||
<ReferSelector
|
||||
placeholder={t((inputChildren.referencePlaceholder as any) || '选择知识库引用')}
|
||||
placeholder={t(
|
||||
(inputChildren.referencePlaceholder as any) ||
|
||||
t('common:core.module.Dataset quote.select')
|
||||
)}
|
||||
list={referenceList}
|
||||
value={formatValue}
|
||||
onSelect={onSelect}
|
||||
|
@@ -79,10 +79,10 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th bg={'myGray.50'} borderRadius={'none !important'}>
|
||||
字段名
|
||||
{t('common:item_name')}
|
||||
</Th>
|
||||
<Th bg={'myGray.50'}>字段描述</Th>
|
||||
<Th bg={'myGray.50'}>必须</Th>
|
||||
<Th bg={'myGray.50'}>{t('common:item_description')}</Th>
|
||||
<Th bg={'myGray.50'}>{t('common:required')}</Th>
|
||||
<Th bg={'myGray.50'} borderRadius={'none !important'}></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
@@ -197,7 +197,7 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
|
||||
const newOutput: FlowNodeOutputItemType = {
|
||||
id: getNanoid(),
|
||||
key: data.key,
|
||||
label: `提取结果-${data.desc}`,
|
||||
label: `${t('common:extraction_results')}-${data.desc}`,
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
type: FlowNodeOutputTypeEnum.static
|
||||
};
|
||||
@@ -215,7 +215,7 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
|
||||
key: data.key,
|
||||
value: {
|
||||
...output,
|
||||
label: `提取结果-${data.desc}`
|
||||
label: `${t('common:extraction_results')}-${data.desc}`
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@@ -392,7 +392,7 @@ const ConditionSelect = ({
|
||||
list={filterQuiredConditionList}
|
||||
value={condition}
|
||||
onchange={onSelect}
|
||||
placeholder="选择条件"
|
||||
placeholder={t('common:chose_condition')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@@ -92,7 +92,7 @@ const NodeLaf = (props: NodeProps<FlowNodeItemType>) => {
|
||||
onError(err) {
|
||||
toast({
|
||||
status: 'error',
|
||||
title: getErrText(err, '获取Laf函数列表失败')
|
||||
title: getErrText(err, t('common:get_laf_failed'))
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -298,7 +298,7 @@ const ConfigLaf = () => {
|
||||
)}
|
||||
</Center>
|
||||
) : (
|
||||
<Box>系统未配置Laf环境</Box>
|
||||
<Box>{t('common:no_laf_env')}</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -443,7 +443,7 @@ const NodeIntro = React.memo(function NodeIntro({
|
||||
// edit intro
|
||||
const { onOpenModal: onOpenIntroModal, EditModal: EditIntroModal } = useEditTextarea({
|
||||
title: t('common:core.module.Edit intro'),
|
||||
tip: '调整该模块会对工具调用时机有影响。\n你可以通过精确的描述该模块功能,引导模型进行工具调用。',
|
||||
tip: t('common:info.node_info'),
|
||||
canEmpty: false
|
||||
});
|
||||
|
||||
|
@@ -85,7 +85,7 @@ const EditFieldModal = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<MyModal isOpen iconSrc="modal/edit" title={'工具字段参数配置'} onClose={onClose}>
|
||||
<MyModal isOpen iconSrc="modal/edit" title={t('common:tool_field')} onClose={onClose}>
|
||||
<ModalBody>
|
||||
<Flex alignItems={'center'} mb={5}>
|
||||
<Box flex={'0 0 80px'}>{t('common:common.Require Input')}</Box>
|
||||
@@ -111,7 +111,7 @@ const EditFieldModal = ({
|
||||
required: true,
|
||||
pattern: {
|
||||
value: /^[a-zA-Z]+[0-9]*$/,
|
||||
message: '字段key必须是纯英文字母或数字,并且不能以数字开头。'
|
||||
message: t('common:info.felid_message')
|
||||
}
|
||||
})}
|
||||
/>
|
||||
|
@@ -64,9 +64,9 @@ const RenderToolInput = ({
|
||||
<Table bg={'white'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>字段名</Th>
|
||||
<Th>字段描述</Th>
|
||||
<Th>必须</Th>
|
||||
<Th>{t('common:item_name')}</Th>
|
||||
<Th>{t('common:item_description')}</Th>
|
||||
<Th>{t('common:required')}</Th>
|
||||
{dynamicInput && <Th></Th>}
|
||||
</Tr>
|
||||
</Thead>
|
||||
|
@@ -387,7 +387,7 @@ const WorkflowContextProvider = ({
|
||||
if (input) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: 'key 重复'
|
||||
title: t('common:key_repetition')
|
||||
});
|
||||
} else {
|
||||
updateObj.inputs.push(props.value);
|
||||
@@ -408,7 +408,7 @@ const WorkflowContextProvider = ({
|
||||
if (output) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: 'key 重复'
|
||||
title: t('common:key_repetition')
|
||||
});
|
||||
updateObj.outputs = node.data.outputs;
|
||||
} else {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { BoxProps, FlexProps } from '@chakra-ui/react';
|
||||
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
export const cardStyles: BoxProps = {
|
||||
borderRadius: 'lg',
|
||||
// overflow: 'hidden',
|
||||
@@ -20,11 +20,11 @@ export const workflowBoxStyles: FlexProps = {
|
||||
export const publishStatusStyle = {
|
||||
unPublish: {
|
||||
colorSchema: 'adora' as any,
|
||||
text: '未发布'
|
||||
text: i18nT('common:core.app.have_publish')
|
||||
},
|
||||
published: {
|
||||
colorSchema: 'green' as any,
|
||||
text: '已发布'
|
||||
text: i18nT('common:core.app.not_published')
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -215,7 +215,7 @@ const ListItem = () => {
|
||||
fontSize={'xs'}
|
||||
color={'myGray.500'}
|
||||
>
|
||||
<Box className={'textEllipsis2'}>{app.intro || '还没写介绍~'}</Box>
|
||||
<Box className={'textEllipsis2'}>{app.intro || t('common:common.no_intro')}</Box>
|
||||
</Box>
|
||||
<Flex
|
||||
h={'24px'}
|
||||
@@ -295,7 +295,7 @@ const ListItem = () => {
|
||||
children: [
|
||||
{
|
||||
icon: 'edit',
|
||||
label: '编辑信息',
|
||||
label: t('common:dataset.Edit Info'),
|
||||
onClick: () => {
|
||||
if (app.type === AppTypeEnum.httpPlugin) {
|
||||
setEditHttpPlugin({
|
||||
@@ -383,14 +383,14 @@ const ListItem = () => {
|
||||
})}
|
||||
</Grid>
|
||||
|
||||
{myApps.length === 0 && <EmptyTip text={'还没有应用,快去创建一个吧!'} pt={'30vh'} />}
|
||||
{myApps.length === 0 && <EmptyTip text={t('common:core.app.no_app')} pt={'30vh'} />}
|
||||
|
||||
<DelConfirmModal />
|
||||
<ConfirmCopyModal />
|
||||
{!!editedApp && (
|
||||
<EditResourceModal
|
||||
{...editedApp}
|
||||
title="应用信息编辑"
|
||||
title={t('common:core.app.edit_content')}
|
||||
onClose={() => {
|
||||
setEditedApp(undefined);
|
||||
}}
|
||||
|
@@ -31,7 +31,7 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => {
|
||||
<LightRowTabs<PluginRunBoxTabEnum>
|
||||
list={[
|
||||
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output },
|
||||
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
|
||||
{ label: t('common:common.all_result'), value: PluginRunBoxTabEnum.detail }
|
||||
]}
|
||||
value={tab}
|
||||
onChange={setTab}
|
||||
@@ -52,7 +52,7 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => {
|
||||
list={[
|
||||
{ label: t('common:common.Input'), value: PluginRunBoxTabEnum.input },
|
||||
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output },
|
||||
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
|
||||
{ label: t('common:common.all_result'), value: PluginRunBoxTabEnum.detail }
|
||||
]}
|
||||
value={tab}
|
||||
onChange={setTab}
|
||||
|
@@ -287,7 +287,7 @@ const FileSelector = ({
|
||||
{isMaxSelected ? (
|
||||
<>
|
||||
<Box color={'myGray.500'} fontSize={'xs'}>
|
||||
已达到最大文件数量
|
||||
{t('file:reached_max_file_count')}
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
|
@@ -243,7 +243,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box mt={3} fontSize={'sm'}>
|
||||
读取 CSV 文件第一列进行批量测试,单次最多支持 100 组数据。
|
||||
{t('common:info.csv_message')}
|
||||
<Box
|
||||
as={'span'}
|
||||
color={'primary.600'}
|
||||
@@ -256,7 +256,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
||||
});
|
||||
}}
|
||||
>
|
||||
点击下载批量测试模板
|
||||
{t('common:info.csv_download')}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
@@ -154,7 +154,9 @@ function List() {
|
||||
label={
|
||||
<Flex flexDirection={'column'} alignItems={'center'}>
|
||||
<Box fontSize={'xs'} color={'myGray.500'}>
|
||||
{dataset.type === DatasetTypeEnum.folder ? '打开文件夹' : '打开知识库'}
|
||||
{dataset.type === DatasetTypeEnum.folder
|
||||
? t('common.folder.Open folder')
|
||||
: t('common.folder.open_dataset')}
|
||||
</Box>
|
||||
</Flex>
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ import { useSendCode } from '@/web/support/user/hooks/useSendCode';
|
||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
interface Props {
|
||||
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
|
||||
loginSuccess: (e: ResLogin) => void;
|
||||
@@ -22,6 +22,7 @@ interface RegisterType {
|
||||
|
||||
const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
const { toast } = useToast();
|
||||
const { t } = useTranslation();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const {
|
||||
register,
|
||||
@@ -58,12 +59,12 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
})
|
||||
);
|
||||
toast({
|
||||
title: `密码已找回`,
|
||||
title: t('user:password.retrieved'),
|
||||
status: 'success'
|
||||
});
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: error.message || '修改密码异常',
|
||||
title: error.message || t('user:password.change_error'),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
@@ -75,7 +76,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}>
|
||||
找回 {feConfigs?.systemTitle} 账号
|
||||
{t('user:password.retrieved_account', { account: feConfigs?.systemTitle })}
|
||||
</Box>
|
||||
<Box
|
||||
mt={'42px'}
|
||||
@@ -88,13 +89,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
<FormControl isInvalid={!!errors.username}>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
placeholder="邮箱/手机号"
|
||||
placeholder={t('user:password.email_phone')}
|
||||
{...register('username', {
|
||||
required: '邮箱/手机号不能为空',
|
||||
required: t('user:password.email_phone_void'),
|
||||
pattern: {
|
||||
value:
|
||||
/(^1[3456789]\d{9}$)|(^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$)/,
|
||||
message: '邮箱/手机号格式错误'
|
||||
message: t('user:password.email_phone_error')
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
@@ -110,9 +111,9 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
bg={'myGray.50'}
|
||||
flex={1}
|
||||
maxLength={8}
|
||||
placeholder="验证码"
|
||||
placeholder={t('user:password.verification_code')}
|
||||
{...register('code', {
|
||||
required: '验证码不能为空'
|
||||
required: t('user:password.code_required')
|
||||
})}
|
||||
></Input>
|
||||
<Box
|
||||
@@ -137,16 +138,16 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
type={'password'}
|
||||
placeholder="新密码(4~20位)"
|
||||
placeholder={t('user:password.new_password')}
|
||||
{...register('password', {
|
||||
required: '密码不能为空',
|
||||
required: t('user:password.password_required'),
|
||||
minLength: {
|
||||
value: 4,
|
||||
message: '密码最少 4 位最多 20 位'
|
||||
message: t('user:password.password_condition')
|
||||
},
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: '密码最少 4 位最多 20 位'
|
||||
message: t('user:password.password_condition')
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
@@ -155,9 +156,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
type={'password'}
|
||||
placeholder="确认密码"
|
||||
placeholder={t('user:password.confirm')}
|
||||
{...register('password2', {
|
||||
validate: (val) => (getValues('password') === val ? true : '两次密码不一致')
|
||||
validate: (val) =>
|
||||
getValues('password') === val ? true : t('user:password.not_match')
|
||||
})}
|
||||
></Input>
|
||||
</FormControl>
|
||||
@@ -171,7 +173,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
isLoading={requesting}
|
||||
onClick={handleSubmit(onclickFindPassword)}
|
||||
>
|
||||
找回密码
|
||||
{t('user:password.retrieve')}
|
||||
</Button>
|
||||
<Box
|
||||
float={'right'}
|
||||
@@ -183,7 +185,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
_hover={{ textDecoration: 'underline' }}
|
||||
onClick={() => setPageType(LoginPageTypeEnum.passwordLogin)}
|
||||
>
|
||||
去登录
|
||||
{t('user:password.to_login')}
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
|
@@ -43,12 +43,12 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
})
|
||||
);
|
||||
toast({
|
||||
title: '登录成功',
|
||||
title: t('user:login.success'),
|
||||
status: 'success'
|
||||
});
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: error.message || '登录异常',
|
||||
title: error.message || t('user:login.error'),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
@@ -101,7 +101,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
required: true,
|
||||
maxLength: {
|
||||
value: 60,
|
||||
message: '密码最多 60 位'
|
||||
message: t('user:login.password_condition')
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
|
@@ -24,7 +24,7 @@ const WechatForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
onError(err) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(err, '获取二维码失败')
|
||||
title: getErrText(err, t('common:get_QR_failed'))
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@@ -10,7 +10,6 @@ import { postCreateApp } from '@/web/core/app/api';
|
||||
import { defaultAppTemplates } from '@/web/core/app/templates';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
interface Props {
|
||||
loginSuccess: (e: ResLogin) => void;
|
||||
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
|
||||
@@ -26,6 +25,7 @@ interface RegisterType {
|
||||
const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
const { toast } = useToast();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { feConfigs } = useSystemStore();
|
||||
const {
|
||||
register,
|
||||
@@ -63,7 +63,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
})
|
||||
);
|
||||
toast({
|
||||
title: `注册成功`,
|
||||
title: t('user:register.success'),
|
||||
status: 'success'
|
||||
});
|
||||
// auto register template app
|
||||
@@ -80,7 +80,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
}, 100);
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: error.message || '注册异常',
|
||||
title: error.message || t('user:register.error'),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
@@ -92,7 +92,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}>
|
||||
注册 {feConfigs?.systemTitle} 账号
|
||||
{t('user:register.register_account', { account: feConfigs?.systemTitle })}
|
||||
</Box>
|
||||
<Box
|
||||
mt={'42px'}
|
||||
@@ -105,13 +105,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
<FormControl isInvalid={!!errors.username}>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
placeholder="邮箱/手机号"
|
||||
placeholder={t('user:password.email_phone')}
|
||||
{...register('username', {
|
||||
required: '邮箱/手机号不能为空',
|
||||
required: t('user:password.email_phone_void'),
|
||||
pattern: {
|
||||
value:
|
||||
/(^1[3456789]\d{9}$)|(^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$)/,
|
||||
message: '邮箱/手机号格式错误'
|
||||
message: t('user:password.email_phone_error')
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
@@ -127,9 +127,9 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
bg={'myGray.50'}
|
||||
flex={1}
|
||||
maxLength={8}
|
||||
placeholder="验证码"
|
||||
placeholder={t('user:password.verification_code')}
|
||||
{...register('code', {
|
||||
required: '验证码不能为空'
|
||||
required: t('user:password.code_required')
|
||||
})}
|
||||
></Input>
|
||||
<Box
|
||||
@@ -154,16 +154,16 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
type={'password'}
|
||||
placeholder="密码(4~20位)"
|
||||
placeholder={t('user:password.new_password')}
|
||||
{...register('password', {
|
||||
required: '密码不能为空',
|
||||
required: t('user:password.password_required'),
|
||||
minLength: {
|
||||
value: 4,
|
||||
message: '密码最少 4 位最多 20 位'
|
||||
message: t('user:password.password_condition')
|
||||
},
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: '密码最少 4 位最多 20 位'
|
||||
message: t('user:password.password_condition')
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
@@ -172,9 +172,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
type={'password'}
|
||||
placeholder="确认密码"
|
||||
placeholder={t('user:password.confirm')}
|
||||
{...register('password2', {
|
||||
validate: (val) => (getValues('password') === val ? true : '两次密码不一致')
|
||||
validate: (val) =>
|
||||
getValues('password') === val ? true : t('user:password.not_match')
|
||||
})}
|
||||
></Input>
|
||||
</FormControl>
|
||||
@@ -187,7 +188,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
isLoading={requesting}
|
||||
onClick={handleSubmit(onclickRegister)}
|
||||
>
|
||||
确认注册
|
||||
{t('user:register.confirm')}
|
||||
</Button>
|
||||
<Box
|
||||
float={'right'}
|
||||
@@ -199,7 +200,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
_hover={{ textDecoration: 'underline' }}
|
||||
onClick={() => setPageType(LoginPageTypeEnum.passwordLogin)}
|
||||
>
|
||||
已有账号,去登录
|
||||
{t('user:register.to_login')}
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
|
@@ -9,7 +9,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import Loading from '@fastgpt/web/components/common/MyLoading';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
const FastLogin = ({
|
||||
code,
|
||||
token,
|
||||
@@ -23,7 +23,7 @@ const FastLogin = ({
|
||||
const { setUserInfo } = useUserStore();
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const loginSuccess = useCallback(
|
||||
(res: ResLogin) => {
|
||||
setToken(res.token);
|
||||
@@ -50,7 +50,7 @@ const FastLogin = ({
|
||||
if (!res) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: '登录异常'
|
||||
title: t('common:support.user.login.error')
|
||||
});
|
||||
return setTimeout(() => {
|
||||
router.replace('/login');
|
||||
@@ -60,7 +60,7 @@ const FastLogin = ({
|
||||
} catch (error) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(error, '登录异常')
|
||||
title: getErrText(error, t('common:support.user.login.error'))
|
||||
});
|
||||
setTimeout(() => {
|
||||
router.replace('/login');
|
||||
|
@@ -13,6 +13,7 @@ import { clearToken, setToken } from '@/web/support/user/auth';
|
||||
import Script from 'next/script';
|
||||
import Loading from '@fastgpt/web/components/common/MyLoading';
|
||||
import { useMount } from 'ahooks';
|
||||
import { t } from 'i18next';
|
||||
|
||||
const RegisterForm = dynamic(() => import('./components/RegisterForm'));
|
||||
const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm'));
|
||||
@@ -115,7 +116,7 @@ const Login = () => {
|
||||
textAlign={'center'}
|
||||
onClick={onOpen}
|
||||
>
|
||||
无法登录,点击联系
|
||||
{t('common:support.user.login.can_not_login')}
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
|
@@ -57,7 +57,7 @@ const provider = () => {
|
||||
if (!res) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: '登录异常'
|
||||
title: t('common:support.user.login.error')
|
||||
});
|
||||
return setTimeout(() => {
|
||||
router.replace('/login');
|
||||
@@ -67,7 +67,7 @@ const provider = () => {
|
||||
} catch (error) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(error, '登录异常')
|
||||
title: getErrText(error, t('common:support.user.login.error'))
|
||||
});
|
||||
setTimeout(() => {
|
||||
router.replace('/login');
|
||||
@@ -95,7 +95,7 @@ const provider = () => {
|
||||
if (state !== loginStore?.state) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: '安全校验失败'
|
||||
title: t('common:support.user.login.security_failed')
|
||||
});
|
||||
setTimeout(() => {
|
||||
router.replace('/login');
|
||||
|
@@ -46,7 +46,7 @@ const ExtraPlan = () => {
|
||||
if (datasetSizePayAmount === 0) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: '购买数量不能为0'
|
||||
title: t('common:support.wallet.amount_0')
|
||||
});
|
||||
}
|
||||
setLoading(true);
|
||||
@@ -91,7 +91,7 @@ const ExtraPlan = () => {
|
||||
if (payAmount === 0) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: '购买数量不能为0'
|
||||
title: t('common:support.wallet.amount_0')
|
||||
});
|
||||
}
|
||||
setLoading(true);
|
||||
@@ -147,7 +147,7 @@ const ExtraPlan = () => {
|
||||
{t('common:support.wallet.subscription.Extra dataset size')}
|
||||
</Box>
|
||||
<Box mt={3} fontSize={['28px', '32px']} fontWeight={'bold'}>
|
||||
¥{extraDatasetPrice}/1000组{' '}
|
||||
{`¥${extraDatasetPrice}/1000` + t('common:core.dataset.data.group')}
|
||||
<Box ml={1} as={'span'} fontSize={'md'} color={'myGray.600'} fontWeight={'normal'}>
|
||||
/{t('common:common.month')}
|
||||
</Box>
|
||||
@@ -164,7 +164,7 @@ const ExtraPlan = () => {
|
||||
<Box h={'120px'} w={'100%'}>
|
||||
<Flex mt={4}>
|
||||
<MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} />
|
||||
购买资源包
|
||||
{t('common:support.wallet.buy_resource')}
|
||||
</Flex>
|
||||
<Flex mt={4} alignItems={'center'}>
|
||||
<Box flex={['0 0 100px', '1 0 0']}>
|
||||
@@ -252,7 +252,7 @@ const ExtraPlan = () => {
|
||||
{t('common:support.wallet.subscription.Extra ai points')}
|
||||
</Box>
|
||||
<Box mt={3} fontSize={['28px', '32px']} fontWeight={'bold'}>
|
||||
¥{extraPointsPrice}/1000积分{' '}
|
||||
{`¥${extraDatasetPrice}/1000` + t('common:support.wallet.subscription.point')}
|
||||
<Box ml={1} as={'span'} fontSize={'md'} color={'myGray.600'} fontWeight={'normal'}>
|
||||
/{t('common:common.month')}
|
||||
</Box>
|
||||
@@ -269,7 +269,7 @@ const ExtraPlan = () => {
|
||||
<Box h={'120px'} w={'100%'}>
|
||||
<Flex mt={4}>
|
||||
<MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} />
|
||||
购买资源包
|
||||
{t('common:support.wallet.buy_resource')}
|
||||
</Flex>
|
||||
{/* <Flex mt={4} alignItems={'center'}>
|
||||
<Box flex={['0 0 100px', '1 0 0']}>
|
||||
@@ -325,7 +325,7 @@ const ExtraPlan = () => {
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
<Box position={'absolute'} right={'20px'} color={'myGray.500'} fontSize={'xs'}>
|
||||
000积分
|
||||
{'000' + t('common:support.wallet.subscription.point')}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
@@ -6,36 +6,36 @@ const FAQ = () => {
|
||||
const { t } = useTranslation();
|
||||
const faqs = [
|
||||
{
|
||||
title: '订阅套餐会自动续费么?',
|
||||
desc: '当前套餐过期后,系统会自动根据“未来套餐”进行续费,系统会尝试从账户余额进行扣费,如果您需要自动续费,请在账户余额中预留额度。'
|
||||
title: t('common:FAQ.auto_renew_q'),
|
||||
desc: t('common:FAQ.auto_renew_a')
|
||||
},
|
||||
{
|
||||
title: '能否切换订阅套餐?',
|
||||
desc: '当前套餐价格大于新套餐时,无法立即切换,将会在当前套餐过期后以“续费”形式进行切换。\n当前套餐价格小于新套餐时,系统会自动计算当前套餐剩余余额,您可支付差价进行套餐切换。'
|
||||
title: t('common:FAQ.change_package_q'),
|
||||
desc: t('common:FAQ.change_package_a')
|
||||
},
|
||||
{
|
||||
title: '什么是AI积分?',
|
||||
desc: '每次调用AI模型时,都会消耗一定的AI积分。具体的计算标准可参考上方的“AI 积分计算标准”。\nToken计算采用GPT3.5相同公式,1Token≈0.7中文字符≈0.9英文单词,连续出现的字符可能被认为是1个Tokens。'
|
||||
title: t('common:FAQ.ai_point_q'),
|
||||
desc: t('common:FAQ.ai_point_a')
|
||||
},
|
||||
{
|
||||
title: 'AI积分会过期么?',
|
||||
desc: '会过期。当前套餐过期后,AI积分将会清空,并更新为新套餐的AI积分。年度套餐的AI积分时长为1年,而不是每个月。'
|
||||
title: t('common:FAQ.ai_point_expire_q'),
|
||||
desc: t('common:FAQ.ai_point_expire_a')
|
||||
},
|
||||
{
|
||||
title: '知识库存储怎么计算?',
|
||||
desc: '1条知识库存储等于1条知识库索引。一条知识库数据可以包含1条或多条知识库索引。增强训练中,1条数据会生成5条索引。'
|
||||
title: t('common:FAQ.dataset_compute_q'),
|
||||
desc: t('common:FAQ.dataset_compute_a')
|
||||
},
|
||||
{
|
||||
title: '知识库索引超出会删除么?',
|
||||
desc: '不会。但知识库索引超出时,无法插入和更新知识库内容。'
|
||||
title: t('common:FAQ.dataset_index_q'),
|
||||
desc: t('common:FAQ.dataset_index_a')
|
||||
},
|
||||
{
|
||||
title: '额外资源包可以叠加么?',
|
||||
desc: '可以的。每次购买的资源包都是独立的,在其有效期内将会叠加使用。AI积分会优先扣除最先过期的资源包。'
|
||||
title: t('common:FAQ.package_overlay_q'),
|
||||
desc: t('common:FAQ.package_overlay_a')
|
||||
},
|
||||
{
|
||||
title: '免费版数据会清除么?',
|
||||
desc: '免费版用户(免费版且未购买额外套餐)30天无使用记录后,系统会自动清除账号下所有知识库内容。'
|
||||
title: t('common:FAQ.free_user_clean_q'),
|
||||
desc: t('common:FAQ.free_user_clean_a')
|
||||
}
|
||||
];
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Box, Flex, Grid, Link } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
|
||||
const Points = () => {
|
||||
@@ -19,7 +18,7 @@ const Points = () => {
|
||||
{t('common:support.wallet.subscription.Ai points')}
|
||||
</Box>
|
||||
<Link href="https://tiktokenizer.vercel.app/" target="_blank">
|
||||
点击查看在线 Tokens 计算器
|
||||
{t('common:support.wallet.subscription.token_compute')}
|
||||
</Link>
|
||||
<Grid gap={6} mt={['30px', '40px']} w={'100%'}>
|
||||
<Box
|
||||
@@ -39,13 +38,17 @@ const Points = () => {
|
||||
fontSize={'md'}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
AI语言模型
|
||||
{t('common:support.wallet.subscription.ai_model')}
|
||||
</Box>
|
||||
<Box flex={4} textAlign={'center'}>
|
||||
{llmModelList?.map((item, i) => (
|
||||
<Flex key={item.model} py={4} bg={i % 2 !== 0 ? 'myGray.50' : ''}>
|
||||
<Box flex={'1 0 0'}>{item.name}</Box>
|
||||
<Box flex={'1 0 0'}>{item.charsPointsPrice}积分 / 1000 Tokens</Box>
|
||||
<Box flex={'1 0 0'}>
|
||||
{item.charsPointsPrice +
|
||||
t('common:support.wallet.subscription.point') +
|
||||
' / 1000 Tokens'}
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
@@ -60,17 +63,21 @@ const Points = () => {
|
||||
>
|
||||
<Box flex={1} borderRightWidth={'1px'} borderRightColor={'myGray.150'} py={4} px={6}>
|
||||
<Box fontSize={'md'} fontWeight={'bold'}>
|
||||
索引模型
|
||||
{t('common:core.ai.model.Vector Model')}
|
||||
</Box>
|
||||
<Box fontSize={'sm'} mt={1} color={'myGray.500'}>
|
||||
文档索引 & 对话索引
|
||||
{t('common:core.ai.model.doc_index_and_dialog')}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box flex={4} textAlign={'center'}>
|
||||
{vectorModelList?.map((item, i) => (
|
||||
<Flex key={item.model} py={4} bg={i % 2 !== 0 ? 'myGray.50' : ''}>
|
||||
<Box flex={'1 0 0'}>{item.name}</Box>
|
||||
<Box flex={'1 0 0'}>{item.charsPointsPrice}积分 / 1000 Tokens</Box>
|
||||
<Box flex={'1 0 0'}>
|
||||
{item.charsPointsPrice +
|
||||
t('common:support.wallet.subscription.point') +
|
||||
' / 1000 Tokens'}
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
@@ -85,14 +92,19 @@ const Points = () => {
|
||||
>
|
||||
<Box flex={1} borderRightWidth={'1px'} borderRightColor={'myGray.150'} py={4} px={6}>
|
||||
<Box fontSize={'md'} fontWeight={'bold'}>
|
||||
语音播放
|
||||
{t('common:core.app.TTS')}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box flex={4} textAlign={'center'}>
|
||||
{audioSpeechModelList?.map((item, i) => (
|
||||
<Flex key={item.model} py={4} bg={i % 2 !== 0 ? 'myGray.50' : ''}>
|
||||
<Box flex={'1 0 0'}>{item.name}</Box>
|
||||
<Box flex={'1 0 0'}>{item.charsPointsPrice}积分 / 1000字符</Box>
|
||||
<Box flex={'1 0 0'}>
|
||||
{item.charsPointsPrice +
|
||||
t('common:support.wallet.subscription.point') +
|
||||
' / 1000' +
|
||||
t('common:unit.character')}
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
@@ -107,13 +119,18 @@ const Points = () => {
|
||||
>
|
||||
<Box flex={1} borderRightWidth={'1px'} borderRightColor={'myGray.150'} py={4} px={6}>
|
||||
<Box fontSize={'md'} fontWeight={'bold'}>
|
||||
语音输入
|
||||
{t('common:core.app.Whisper')}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box flex={4} textAlign={'center'} h={'100%'}>
|
||||
<Flex py={4}>
|
||||
<Box flex={'1 0 0'}>{whisperModel?.name}</Box>
|
||||
<Box flex={'1 0 0'}>{whisperModel?.charsPointsPrice}积分 / 分钟</Box>
|
||||
<Box flex={'1 0 0'}>
|
||||
{whisperModel?.charsPointsPrice +
|
||||
t('common:support.wallet.subscription.point') +
|
||||
' / 1000' +
|
||||
t('common:unit.minute')}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Box>
|
||||
|
@@ -309,6 +309,7 @@ const ConfirmPayModal = ({
|
||||
onConfirmPay
|
||||
}: ConfirmPayModalProps & { onClose: () => void; onConfirmPay: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [qrPayData, setQRPayData] = useState<QRPayProps>();
|
||||
|
||||
const formatPayPrice = Math.ceil(formatStorePrice2Read(payPrice));
|
||||
@@ -340,16 +341,24 @@ const ConfirmPayModal = ({
|
||||
>
|
||||
<ModalBody py={5} px={9}>
|
||||
<Flex>
|
||||
<Box flex={'0 0 100px'}>新套餐价格</Box>
|
||||
<Box>{formatStorePrice2Read(totalPrice)}元</Box>
|
||||
<Box flex={'0 0 100px'}>{t('common:pay.new_package_price')}</Box>
|
||||
<Box>{t('common:pay.yuan', { amount: formatStorePrice2Read(totalPrice) })}</Box>
|
||||
</Flex>
|
||||
<Flex mt={6}>
|
||||
<Box flex={'0 0 100px'}>旧套餐余额</Box>
|
||||
<Box>{Math.floor(formatStorePrice2Read(totalPrice - payPrice))}元</Box>
|
||||
<Box flex={'0 0 100px'}>{t('common:pay.old_package_price')}</Box>
|
||||
<Box>
|
||||
{t('common:pay.yuan', {
|
||||
amount: Math.floor(formatStorePrice2Read(totalPrice - payPrice))
|
||||
})}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={6}>
|
||||
<Box flex={'0 0 100px'}>需支付</Box>
|
||||
<Box>{formatPayPrice}元</Box>
|
||||
<Box flex={'0 0 100px'}>{t('common:pay.balance_notice')}</Box>
|
||||
<Box>
|
||||
{t('common:pay.yuan', {
|
||||
amount: formatPayPrice
|
||||
})}
|
||||
</Box>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
<ModalFooter
|
||||
@@ -359,13 +368,15 @@ const ConfirmPayModal = ({
|
||||
justifyContent={'flex-start'}
|
||||
px={0}
|
||||
>
|
||||
<Box>账号余额: </Box>
|
||||
<Box>{t('common:pay.balance') + ': '}</Box>
|
||||
<Box ml={2} flex={1}>
|
||||
{formatTeamBalance}元
|
||||
{t('common:pay.yuan', {
|
||||
amount: formatTeamBalance
|
||||
})}
|
||||
</Box>
|
||||
{teamBalance >= payPrice ? (
|
||||
<Button size={'sm'} onClick={onConfirmPay}>
|
||||
确认支付
|
||||
{t('common:pay.confirm_pay')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
@@ -375,7 +386,7 @@ const ConfirmPayModal = ({
|
||||
handleClickPay(Math.ceil(formatStorePrice2Read(payPrice - teamBalance)));
|
||||
}}
|
||||
>
|
||||
余额不足,去充值
|
||||
{t('common:pay.to_recharge')}
|
||||
</Button>
|
||||
)}
|
||||
</ModalFooter>
|
||||
|
@@ -15,14 +15,14 @@ const Tools = () => {
|
||||
const list = [
|
||||
{
|
||||
icon: 'core/dataset/datasetLight',
|
||||
label: '我的知识库',
|
||||
label: t('common:core.dataset.My Dataset'),
|
||||
link: '/dataset/list'
|
||||
},
|
||||
...(feConfigs?.show_git
|
||||
? [
|
||||
{
|
||||
icon: 'common/gitLight',
|
||||
label: 'GitHub 地址',
|
||||
label: t('common:core.app.tool_label.github'),
|
||||
link: 'https://github.com/labring/FastGPT'
|
||||
}
|
||||
]
|
||||
@@ -31,7 +31,7 @@ const Tools = () => {
|
||||
? [
|
||||
{
|
||||
icon: 'common/courseLight',
|
||||
label: '使用文档',
|
||||
label: t('common:core.app.tool_label.doc'),
|
||||
link: getDocPath('/docs/intro')
|
||||
}
|
||||
]
|
||||
@@ -40,7 +40,7 @@ const Tools = () => {
|
||||
? [
|
||||
{
|
||||
icon: 'support/bill/priceLight',
|
||||
label: '计费说明',
|
||||
label: t('common:core.app.tool_label.price'),
|
||||
link: '/price'
|
||||
}
|
||||
]
|
||||
|
@@ -18,7 +18,7 @@ export const langMap = {
|
||||
};
|
||||
|
||||
export const serviceSideProps = (content: any, ns: I18nNsType = []) => {
|
||||
return serverSideTranslations(content.locale, ['common', ...ns], null, content.locales);
|
||||
return serverSideTranslations(content.locale, ['common', 'error', ...ns], null, content.locales);
|
||||
};
|
||||
|
||||
export const getLng = (lng: string) => {
|
||||
|
@@ -3,10 +3,10 @@ import { AppDetailType } from '@fastgpt/global/core/app/type.d';
|
||||
import type { FeishuType, OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
|
||||
import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
|
||||
import { NullPermission } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
export const defaultApp: AppDetailType = {
|
||||
_id: '',
|
||||
name: '应用加载中',
|
||||
name: i18nT('common:core.app.loading'),
|
||||
type: AppTypeEnum.simple,
|
||||
avatar: '/icon/logo.svg',
|
||||
intro: '',
|
||||
|
@@ -4,7 +4,7 @@ import { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
let timer: NodeJS.Timeout;
|
||||
|
||||
export const useSendCode = () => {
|
||||
@@ -29,8 +29,8 @@ export const useSendCode = () => {
|
||||
}, 1000);
|
||||
},
|
||||
{
|
||||
successToast: '验证码已发送',
|
||||
errorToast: '验证码发送异常',
|
||||
successToast: i18nT('user:password.code_sended'),
|
||||
errorToast: i18nT('user:password.code_send_error'),
|
||||
refreshDeps: [codeCountDown, feConfigs?.googleClientVerKey]
|
||||
}
|
||||
);
|
||||
@@ -38,12 +38,12 @@ export const useSendCode = () => {
|
||||
const sendCodeText = useMemo(() => {
|
||||
if (codeSending) return t('common:support.user.auth.Sending Code');
|
||||
if (codeCountDown >= 10) {
|
||||
return `${codeCountDown}s后重新获取`;
|
||||
return `${codeCountDown}${t('user:password.get_code_again')}`;
|
||||
}
|
||||
if (codeCountDown > 0) {
|
||||
return `0${codeCountDown}s后重新获取`;
|
||||
return `0${codeCountDown}${t('user:password.get_code_again')}`;
|
||||
}
|
||||
return '获取验证码';
|
||||
return t('user:password.get_code');
|
||||
}, [codeCountDown, codeSending, t]);
|
||||
|
||||
return {
|
||||
|
Reference in New Issue
Block a user