From 6fe7cf18bd3a42d1916933bddf9dd97e95c94003 Mon Sep 17 00:00:00 2001 From: ScarChin Date: Mon, 13 Jan 2025 18:33:55 +0800 Subject: [PATCH 1/2] fix: login page redirect multiple times (#166) * fix: System Settings - General After changing the interface language, the internal pages do not automatically follow the language switch * fix: login page language error --- .../components/aoplatform/LanguageSetting.tsx | 84 +++++++++---------- .../src/contexts/GlobalStateContext.tsx | 4 +- frontend/packages/common/src/locales/index.ts | 32 +++++-- .../src/pages/aiSetting/AiSettingList.tsx | 3 +- .../core/src/pages/common/CommonPage.tsx | 57 ++++++------- 5 files changed, 97 insertions(+), 83 deletions(-) diff --git a/frontend/packages/common/src/components/aoplatform/LanguageSetting.tsx b/frontend/packages/common/src/components/aoplatform/LanguageSetting.tsx index 90a20efa..adc69330 100644 --- a/frontend/packages/common/src/components/aoplatform/LanguageSetting.tsx +++ b/frontend/packages/common/src/components/aoplatform/LanguageSetting.tsx @@ -4,48 +4,48 @@ import { Icon } from '@iconify/react/dist/iconify.js' import { Button, Dropdown } from 'antd' import { memo, useEffect, useMemo } from 'react' +const LanguageItems = [ + { + key: 'en-US', + label: ( + + ), + title: 'English' + }, + { + key: 'ja-JP', + label: ( + + ), + title: '日本語' + }, + { + key: 'zh-TW', + label: ( + + ), + title: '繁體中文' + }, + { + key: 'zh-CN', + label: ( + + ), + title: '简体中文' + } +] const LanguageSetting = ({ mode = 'light' }: { mode?: 'dark' | 'light' }) => { const { dispatch, state } = useGlobalContext() - const items = [ - { - key: 'en-US', - label: ( - - ), - title: 'English' - }, - { - key: 'ja-JP', - label: ( - - ), - title: '日本語' - }, - { - key: 'zh-TW', - label: ( - - ), - title: '繁體中文' - }, - { - key: 'zh-CN', - label: ( - - ), - title: '简体中文' - } - ] - const langLabel = useMemo(() => items.find((item) => item?.key === state.language)?.title, [state.language]) + const langLabel = useMemo(() => LanguageItems.find((item) => item?.key === state.language)?.title, [state.language]) useEffect(() => { const savedLang = i18n.language || sessionStorage.getItem('i18nextLng') @@ -53,17 +53,17 @@ const LanguageSetting = ({ mode = 'light' }: { mode?: 'dark' | 'light' }) => { dispatch({ type: 'UPDATE_LANGUAGE', language: savedLang }) } else if (!savedLang) { const browserLang = navigator.language - const supportedLang = items.find((item) => item.key === browserLang) ? browserLang : 'zh-CN' + const supportedLang = LanguageItems.find((item) => item.key === browserLang) ? browserLang : 'zh-CN' + if (state.language === supportedLang) return dispatch({ type: 'UPDATE_LANGUAGE', language: supportedLang }) i18n.changeLanguage(supportedLang) } }, []) - return ( { const { key } = e diff --git a/frontend/packages/common/src/contexts/GlobalStateContext.tsx b/frontend/packages/common/src/contexts/GlobalStateContext.tsx index da730fa6..31fbb1d4 100644 --- a/frontend/packages/common/src/contexts/GlobalStateContext.tsx +++ b/frontend/packages/common/src/contexts/GlobalStateContext.tsx @@ -342,7 +342,7 @@ export const GlobalProvider: FC<{ children: ReactNode }> = ({ children }) => { updateDate: '2024-07-01', powered: 'Powered by https://apipark.com', mainPage: '/guide/page', - language: 'en-US', + language: sessionStorage.getItem('i18nextLng') || 'en-US', pluginsLoaded: false }) const [accessData, setAccessData] = useState>(new Map()) @@ -510,7 +510,7 @@ export const useGlobalContext = () => { updateDate: '', powered: '', mainPage: '', - language: 'en-US', + language: sessionStorage.getItem('i18nextLng') || 'en-US', pluginsLoaded: false }, dispatch: () => {}, diff --git a/frontend/packages/common/src/locales/index.ts b/frontend/packages/common/src/locales/index.ts index 8a98b857..5da94e8a 100644 --- a/frontend/packages/common/src/locales/index.ts +++ b/frontend/packages/common/src/locales/index.ts @@ -1,5 +1,5 @@ import i18n from 'i18next' -import { initReactI18next } from 'react-i18next' +import { initReactI18next, useTranslation } from 'react-i18next' // i18next-browser-languagedetector插件 这是一个 i18next 语言检测插件,用于检测浏览器中的用户语言, import crc32 from 'crc/crc32' import LanguageDetector from 'i18next-browser-languagedetector' @@ -39,23 +39,22 @@ i18n .init({ // 初始化 resources, // 本地多语言数据 - // fallbackLng: config.lang, // 默认当前环境的语言 + supportedLngs: ['zh-CN', 'en-US', 'zh-TW', 'ja-JP'], detection: { caches: ['localStorage', 'sessionStorage', 'cookie'] } }) // --------这里是i18next-scanner新增的配置------------- +// 用于非 React 组件中的翻译 export const $t = (key: string, params?: any[]): string => { - const hashKey = `K${crc32(key).toString(16)}` // 将中文转换成crc32格式去匹配对应的json语言包 + // 将中文转换成crc32格式去匹配对应的json语言包 + const hashKey = `K${crc32(key).toString(16)}` let words = i18n.t(hashKey) - // const { t } = useTranslation(); // 通过hooks - // let words = t(hashKey); if (words === hashKey) { words = key } - // 配置传递参数的场景, 目前仅支持数组,可在此拓展 if (Array.isArray(params)) { const reg = /\((\d)\)/g words = words.replace(reg, (a: string, b: number) => { @@ -65,4 +64,25 @@ export const $t = (key: string, params?: any[]): string => { return words } +// 用于 React 组件中的翻译 +export const useI18n = () => { + const { t } = useTranslation() + + return (key: string, params?: any[]): string => { + const hashKey = `K${crc32(key).toString(16)}` + let words = t(hashKey) + if (words === hashKey) { + words = key + } + + if (Array.isArray(params)) { + const reg = /\((\d)\)/g + words = words.replace(reg, (a: string, b: number) => { + return params[b] + }) + } + return words + } +} + export default i18n diff --git a/frontend/packages/core/src/pages/aiSetting/AiSettingList.tsx b/frontend/packages/core/src/pages/aiSetting/AiSettingList.tsx index 30694d5a..aa7034d2 100644 --- a/frontend/packages/core/src/pages/aiSetting/AiSettingList.tsx +++ b/frontend/packages/core/src/pages/aiSetting/AiSettingList.tsx @@ -1,5 +1,5 @@ import InsidePage from '@common/components/aoplatform/InsidePage' -import { $t } from '@common/locales' +import { useI18n } from '@common/locales' import { Tabs } from 'antd' import { useEffect, useState } from 'react' import { useSearchParams } from 'react-router-dom' @@ -10,6 +10,7 @@ import { AiSettingProvider } from './contexts/AiSettingContext' const CONTENT_STYLE = { height: 'calc(-300px + 100vh)' } as const const AiSettingContent = () => { + const $t = useI18n() const [searchParams, setSearchParams] = useSearchParams() const [activeKey, setActiveKey] = useState(searchParams.get('status') === 'unconfigure' ? 'config' : 'flow') diff --git a/frontend/packages/core/src/pages/common/CommonPage.tsx b/frontend/packages/core/src/pages/common/CommonPage.tsx index e1e9ae86..7a2c62b4 100644 --- a/frontend/packages/core/src/pages/common/CommonPage.tsx +++ b/frontend/packages/core/src/pages/common/CommonPage.tsx @@ -1,33 +1,26 @@ -import InsidePage from "@common/components/aoplatform/InsidePage"; -import { $t } from "@common/locales"; -import ServiceCategory from "./ServiceCategory"; -import ApiRequestSetting from "./ApiRequestSetting"; -import { Row, Col } from "antd"; +import InsidePage from '@common/components/aoplatform/InsidePage' +import { useI18n } from '@common/locales' +import { Col, Row } from 'antd' +import ApiRequestSetting from './ApiRequestSetting' +import ServiceCategory from './ServiceCategory' -export default function CommonPage(){ - return ( - - - - - {$t('API 请求设置')} - - - - - - - - {$t('服务分类')} - - - - - - ) -} \ No newline at end of file +export default function CommonPage() { + const $t = useI18n() + + return ( + + + + {$t('API 请求设置')} + + + + + + {$t('服务分类')} + + + + + ) +} From c28e3ec5c2bc1d8bddf1ae61b1bf6dac61bac098 Mon Sep 17 00:00:00 2001 From: ScarChin Date: Tue, 14 Jan 2025 17:52:07 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=201.3-beta=E7=89=88=E6=9C=AC=EF=BC=8C?= =?UTF-8?q?=E8=B6=85=E7=BA=A7=E7=AE=A1=E7=90=86=E5=91=98(admin=EF=BC=89?= =?UTF-8?q?=E8=B4=A6=E6=88=B7=E6=97=A0=E6=B3=95=E4=BF=AE=E6=94=B9=E5=88=86?= =?UTF-8?q?=E7=B1=BB=E5=92=8C=E6=B7=BB=E5=8A=A0=E5=AD=90=E5=88=86=E7=B1=BB?= =?UTF-8?q?=EF=BC=8C=E9=A1=B5=E9=9D=A2=E6=98=BE=E7=A4=BA=E6=97=A0=E6=9D=83?= =?UTF-8?q?=E9=99=90=E6=93=8D=E4=BD=9C=20(#164)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/aoplatform/WithPermission.tsx | 4 +- .../packages/common/src/utils/permission.ts | 6 +- frontend/packages/core/src/App.tsx | 12 +- .../core/src/pages/common/ServiceCategory.tsx | 60 ++--- .../pages/common/ServiceHubCategoryConfig.tsx | 219 +++++++++--------- .../market/src/const/serviceHub/type.ts | 4 +- .../management/ServiceHubManagement.tsx | 24 +- 7 files changed, 144 insertions(+), 185 deletions(-) diff --git a/frontend/packages/common/src/components/aoplatform/WithPermission.tsx b/frontend/packages/common/src/components/aoplatform/WithPermission.tsx index 719cd5bd..9d1740af 100644 --- a/frontend/packages/common/src/components/aoplatform/WithPermission.tsx +++ b/frontend/packages/common/src/components/aoplatform/WithPermission.tsx @@ -1,8 +1,8 @@ +import { PERMISSION_DEFINITION } from '@common/const/permissions' +import { $t } from '@common/locales' import { Button, Tooltip, Upload } from 'antd' import { ReactElement, cloneElement, useEffect, useMemo, useState } from 'react' import { useGlobalContext } from '../../contexts/GlobalStateContext' -import { PERMISSION_DEFINITION } from '@common/const/permissions' -import { $t } from '@common/locales' type WithPermissionProps = { access?: string | string[] diff --git a/frontend/packages/common/src/utils/permission.ts b/frontend/packages/common/src/utils/permission.ts index f8317c28..d5517d44 100644 --- a/frontend/packages/common/src/utils/permission.ts +++ b/frontend/packages/common/src/utils/permission.ts @@ -18,7 +18,11 @@ export const checkAccess: (access: AccessDataType, accessData: Map 0 ? hasIntersection(neededBackendAccessArr, accessSet) : false + if (!accessSet!.size) { + return false + } + const hasAccess = hasIntersection(neededBackendAccessArr, accessSet) + return hasAccess } const hasIntersection = (arr1: string[], set1: Set) => { diff --git a/frontend/packages/core/src/App.tsx b/frontend/packages/core/src/App.tsx index aa5b4c40..cb98a151 100644 --- a/frontend/packages/core/src/App.tsx +++ b/frontend/packages/core/src/App.tsx @@ -151,15 +151,15 @@ function App() { form={{ validateMessages }} > - - - + + + - - - + + + diff --git a/frontend/packages/core/src/pages/common/ServiceCategory.tsx b/frontend/packages/core/src/pages/common/ServiceCategory.tsx index 7ee2abcc..e3bf18bc 100644 --- a/frontend/packages/core/src/pages/common/ServiceCategory.tsx +++ b/frontend/packages/core/src/pages/common/ServiceCategory.tsx @@ -28,7 +28,7 @@ export default function ServiceCategory() { const { accessData } = useGlobalContext() const [loading, setLoading] = useState(false) - const onDrop: TreeProps['onDrop'] = info => { + const onDrop: TreeProps['onDrop'] = (info) => { const dropKey = info.node.key const dragKey = info.dragNode.key const dropPos = info.node.pos.split('-') @@ -59,7 +59,7 @@ export default function ServiceCategory() { if (!info.dropToGap) { // Drop on the content - loop(data, dropKey, item => { + loop(data, dropKey, (item) => { item.children = item.children || [] // where to insert. New item was inserted to the start of the array in this example, but can be anywhere item.children.unshift(dragObj) @@ -129,9 +129,9 @@ export default function ServiceCategory() { const treeData = useMemo(() => { setExpandedKeys([]) const loop = (data: CategorizesType[]): DataNode[] => - data?.map(item => { + data?.map((item) => { if (item.children) { - setExpandedKeys(prev => [...prev, item.id]) + setExpandedKeys((prev) => [...prev, item.id]) return { title: ( @@ -169,40 +169,22 @@ export default function ServiceCategory() { return !checkAccess(permission, accessData) } - const openModal = ( - type: 'addCate' | 'addChildCate' | 'renameCate' | 'delete', - entity?: CategorizesType - ) => { + const openModal = (type: 'addCate' | 'addChildCate' | 'renameCate' | 'delete', entity?: CategorizesType) => { let title: string = '' let content: string | React.ReactNode = '' switch (type) { - case 'addCate': + case 'addCate': { title = $t('添加分类') - content = ( - - ) + content = break + } case 'addChildCate': title = $t('添加子分类') - content = ( - - ) + content = break case 'renameCate': title = $t('重命名分类') - content = ( - - ) + content = break case 'delete': title = $t('删除') @@ -215,19 +197,19 @@ export default function ServiceCategory() { onOk: () => { switch (type) { case 'addCate': - return addRef.current?.save().then(res => { + return addRef.current?.save().then((res) => { if (res === true) getCategoryList() }) case 'addChildCate': - return addChildRef.current?.save().then(res => { + return addChildRef.current?.save().then((res) => { if (res === true) getCategoryList() }) case 'renameCate': - return renameRef.current?.save().then(res => { + return renameRef.current?.save().then((res) => { if (res === true) getCategoryList() }) case 'delete': - return deleteCate(entity!).then(res => { + return deleteCate(entity!).then((res) => { if (res === true) getCategoryList() }) } @@ -249,7 +231,7 @@ export default function ServiceCategory() { method: 'DELETE', eoParams: { catalogue: entity.id } }) - .then(response => { + .then((response) => { const { code, msg } = response if (code === STATUS_CODE.SUCCESS) { message.success(msg || $t(RESPONSE_TIPS.success)) @@ -259,14 +241,14 @@ export default function ServiceCategory() { reject(msg || $t(RESPONSE_TIPS.error)) } }) - .catch(errorInfo => reject(errorInfo)) + .catch((errorInfo) => reject(errorInfo)) }) } const sortCategories = (newData: CategorizesType[]) => { setLoading(true) fetchData>('catalogue/sort', { method: 'PUT', eoBody: newData }) - .then(response => { + .then((response) => { const { code, msg } = response if (code === STATUS_CODE.SUCCESS) { getCategoryList() @@ -288,7 +270,7 @@ export default function ServiceCategory() { fetchData>('catalogues', { method: 'GET' }) - .then(response => { + .then((response) => { const { code, data, msg } = response if (code === STATUS_CODE.SUCCESS) { setGData(data.catalogues) @@ -308,11 +290,7 @@ export default function ServiceCategory() { return (
- } - spinning={loading} - className="" - > + } spinning={loading} className=""> ((props, ref) => { - const { message } = App.useApp() - const [form] = Form.useForm() - const { type, entity } = props - const { fetchData } = useFetch() +export const ServiceHubCategoryConfig = forwardRef( + (props, ref) => { + const { message } = App.useApp() + const [form] = Form.useForm() + const { type, entity } = props + const { fetchData } = useFetch() - const save: () => Promise = () => { - const url: string = 'catalogue' - let method: string - switch (type) { - case 'addCate': - case 'addChildCate': - method = 'POST' - break - case 'renameCate': - method = 'PUT' - break - } - return new Promise((resolve, reject) => { - if (!url || !method) { - reject($t(RESPONSE_TIPS.error)) - return + const save: () => Promise = () => { + const url: string = 'catalogue' + let method: string + switch (type) { + case 'addCate': + case 'addChildCate': + method = 'POST' + break + case 'renameCate': + method = 'PUT' + break } - form - .validateFields() - .then(value => { - fetchData>(url, { - method, - eoBody: value, - eoParams: { ...(type === 'renameCate' ? { catalogue: value.id } : undefined) } - }) - .then(response => { - const { code, msg } = response - if (code === STATUS_CODE.SUCCESS) { - message.success(msg || $t(RESPONSE_TIPS.success)) - resolve(true) - } else { - message.error(msg || $t(RESPONSE_TIPS.error)) - reject(msg || $t(RESPONSE_TIPS.error)) - } + return new Promise((resolve, reject) => { + if (!url || !method) { + reject($t(RESPONSE_TIPS.error)) + return + } + form + .validateFields() + .then((value) => { + fetchData>(url, { + method, + eoBody: value, + eoParams: { ...(type === 'renameCate' ? { catalogue: value.id } : undefined) } }) - .catch(errorInfo => reject(errorInfo)) - }) - .catch(errorInfo => reject(errorInfo)) - }) - } - - useImperativeHandle(ref, () => ({ - save - })) - - useEffect(() => { - switch (type) { - case 'addCate': - form.setFieldsValue({}) - break - case 'addChildCate': - form.setFieldsValue({ parent: entity!.id }) - break - case 'renameCate': - form.setFieldsValue(entity) - break + .then((response) => { + const { code, msg } = response + if (code === STATUS_CODE.SUCCESS) { + message.success(msg || $t(RESPONSE_TIPS.success)) + resolve(true) + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + reject(msg || $t(RESPONSE_TIPS.error)) + } + }) + .catch((errorInfo) => reject(errorInfo)) + }) + .catch((errorInfo) => reject(errorInfo)) + }) } - }, []) - return ( - ({ + save + })) + + useEffect(() => { + switch (type) { + case 'addCate': + form.setFieldsValue({}) + break + case 'addChildCate': + form.setFieldsValue({ parent: entity!.id }) + break + case 'renameCate': + form.setFieldsValue(entity) + break } - > -
- {type === 'renameCate' && ( - - label={$t('ID')} - name="id" - hidden - rules={[{ required: true, whitespace: true }]} - > - - - )} - {(type === 'addCate' || type === 'renameCate') && ( - - label={$t('分类名称')} - name="name" - rules={[{ required: true, whitespace: true }]} - > - - - )} + }, []) - {type === 'addChildCate' && ( - <> + return ( + + + {type === 'renameCate' && ( - label={$t('父分类 ID')} - name="parent" + label={$t('ID')} + name="id" hidden rules={[{ required: true, whitespace: true }]} > - + )} + {(type === 'addCate' || type === 'renameCate') && ( - label={$t('子分类名称')} + label={$t('分类名称')} name="name" rules={[{ required: true, whitespace: true }]} > - - )} - - - ) -}) + )} + + {type === 'addChildCate' && ( + <> + + label={$t('父分类 ID')} + name="parent" + hidden + rules={[{ required: true, whitespace: true }]} + > + + + + + label={$t('子分类名称')} + name="name" + rules={[{ required: true, whitespace: true }]} + > + + + + )} + +
+ ) + } +) diff --git a/frontend/packages/market/src/const/serviceHub/type.ts b/frontend/packages/market/src/const/serviceHub/type.ts index e973d88d..6e02a06b 100644 --- a/frontend/packages/market/src/const/serviceHub/type.ts +++ b/frontend/packages/market/src/const/serviceHub/type.ts @@ -1,7 +1,6 @@ -import { DefaultOptionType } from 'antd/es/select' import { EntityItem } from '@common/const/type' import { SubscribeEnum, SubscribeFromEnum } from '@core/const/system/const' -import WithPermission from '@common/components/aoplatform/WithPermission' +import { DefaultOptionType } from 'antd/es/select' export type ServiceBasicInfoType = { app: EntityItem @@ -37,7 +36,6 @@ export type ServiceHubCategoryConfigFieldType = { export type ServiceHubCategoryConfigProps = { type: 'addCate' | 'addChildCate' | 'renameCate' entity?: { [k: string]: unknown } - WithPermission: typeof WithPermission } export type ServiceHubCategoryConfigHandle = { diff --git a/frontend/packages/market/src/pages/serviceHub/management/ServiceHubManagement.tsx b/frontend/packages/market/src/pages/serviceHub/management/ServiceHubManagement.tsx index ef5f5ea2..8f0fe92d 100644 --- a/frontend/packages/market/src/pages/serviceHub/management/ServiceHubManagement.tsx +++ b/frontend/packages/market/src/pages/serviceHub/management/ServiceHubManagement.tsx @@ -5,7 +5,7 @@ import WithPermission from '@common/components/aoplatform/WithPermission' import { BasicResponse, DATA_SHOW_TYPE_OPTIONS, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' import { SimpleTeamItem } from '@common/const/type' import { useBreadcrumb } from '@common/contexts/BreadcrumbContext' -import { GlobalProvider, useGlobalContext } from '@common/contexts/GlobalStateContext' +import { useGlobalContext } from '@common/contexts/GlobalStateContext' import { useFetch } from '@common/hooks/http' import { $t } from '@common/locales' import { RouterParams } from '@core/components/aoplatform/RenderRoutes' @@ -158,28 +158,8 @@ export default function ServiceHubManagement() { switch (type) { case 'add': title = $t('添加消费者') - content = ( - - - - ) + content = break - // case 'edit':{ - // title='配置 Open Api' - // message.loading('正在加载数据') - // const {code,data,msg} = await fetchData>('external-app',{method:'GET',eoParams:{id:entity!.id}}) - // message.destroy() - // if(code === STATUS_CODE.SUCCESS){ - // content= - // }else{ - // message.error(msg || $t(RESPONSE_TIPS.error)) - // return - // } - // break;} - // case 'delete': - // title='删除' - // content='该数据删除后将无法找回,请确认是否删除?' - // break; } modal.confirm({