feat:多语言

This commit is contained in:
maggieyyy
2024-08-20 18:49:14 +08:00
parent 92a3abea89
commit 49efc0e4df
146 changed files with 4327 additions and 3798 deletions
+68
View File
@@ -0,0 +1,68 @@
const fs = require('fs');
const { crc32 } = require('crc');
const systemLanguage = {
zh_CN: 'zh-CN',
en_GB: 'en-GB'
};
// 读取已经存在在en.json文件的词条
let existData = {};
try {
console.log('Current working directory:', process.cwd());
const existJsonData = fs.readFileSync('packages/common/src/locales/scan/en-GB.json');
existData = JSON.parse(existJsonData);
} catch (error) {
if (error.code === 'ENOENT') {
console.warn('File not found: packages/common/src/locales/scan/en-GB.json. Creating an empty file.');
fs.writeFileSync('packages/common/src/locales/scan/en-GB.json', JSON.stringify({}));
} else {
throw error;
}
}
const keyList = Object.keys(existData);
module.exports = {
input: [
'packages/*/src/**/*.{js,jsx,tsx,ts}',
// 不需要扫描的文件加!
'!packages/*/src/locales/**',
'!**/node_modules/**'
],
output: 'packages/common/src/locales/scan', // 输出目录
options: {
debug: true,
func: false,
trans: false,
lngs: [systemLanguage.zh_CN, systemLanguage.en_GB],
defaultLng: systemLanguage.zh_CN,
resource: {
loadPath: './newJson/{{lng}}.json', // 输入路径 (手动新建目录)
savePath: './newJson/{{lng}}.json', // 输出路径 (输出会根据输入路径内容自增, 不会覆盖已有的key)
jsonIndent: 2,
lineEnding: '\n'
},
removeUnusedKeys: true,
nsSeparator: false, // namespace separator
keySeparator: false, // key separator
interpolation: {
prefix: '{{',
suffix: '}}'
}
},
// 这里我们要实现将中文转换成crc格式, 通过crc格式key作为索引, 最终实现语言包的切换.
transform: function (file, enc, done) {
// 自己通过该函数来加工key或value
const { parser } = this;
const content = fs.readFileSync(file.path, enc);
parser.parseFuncFromString(content, { list: ['t'] }, (key, options) => {
options.defaultValue = key;
const hashKey = `K${crc32(key).toString(16)}`; // crc32转换格式
// 如果词条不存在,则写入
if (!keyList.includes(hashKey)) {
parser.set(hashKey, options);
}
});
done();
}
};
+7 -2
View File
@@ -14,7 +14,8 @@
"serve:remotes": "lerna run serve --scope=remote --parallel",
"dev": "lerna run dev --scope=core --stream",
"dev:pro": "lerna run dev --scope=business-entry --stream",
"stop": "kill-port --port 5000,5001"
"stop": "kill-port --port 5000",
"scan":"i18next-scanner --config i18next-scanner.config.js"
},
"keywords": [],
"author": "",
@@ -28,16 +29,19 @@
"@types/uuid": "^9.0.7",
"@vitejs/plugin-react": "^4.2.0",
"autoprefixer": "^10.4.16",
"crc": "^4.3.2",
"dayjs": "^1.11.10",
"dompurify": "^3.1.6",
"i18next": "^23.12.2",
"i18next-browser-languagedetector": "^8.0.0",
"js-base64": "^3.7.5",
"moment": "^2.29.4",
"postcss": "^8.4.31",
"postcss-import": "^16.1.0",
"postcss-nesting": "^12.1.5",
"react": "^18.2.0",
"react-ace": "^10.1.0",
"react-dom": "^18.2.0",
"react-i18next": "^15.0.1",
"react-router-dom": "^6.20.0",
"tailwindcss": "^3.3.5",
"uuid": "^9.0.1",
@@ -64,6 +68,7 @@
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.4",
"file-saver": "^2.0.5",
"i18next-scanner": "^4.5.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-fetch-mock": "^3.0.3",
@@ -6,23 +6,26 @@ import {
Button} from 'antd';
import Logo from '@common/assets/layout-logo.png';
import AvatarPic from '@common/assets/default-avatar.png'
import { routerKeyMap, TOTAL_MENU_ITEMS } from "./Navigation";
import {Outlet, useLocation, useNavigate} from "react-router-dom";
import {useEffect, useMemo, useRef, useState} from "react";
import { useEffect, useMemo, useState} from "react";
import { useGlobalContext } from '@common/contexts/GlobalStateContext.tsx';
import { PERMISSION_DEFINITION } from '@common/const/permissions.ts';
import {
ProConfigProvider,
ProLayout,
} from '@ant-design/pro-components';
import { UserProfile } from './UserProfile.tsx';
import { ResetPsw, ResetPswHandle } from './ResetPsw.tsx';
import { BasicResponse, STATUS_CODE } from '@common/const/const.ts';
import { UserInfoType, UserProfileHandle } from '@common/const/type.ts';
import { BasicResponse, RESPONSE_TIPS, routerKeyMap, STATUS_CODE } from '@common/const/const.tsx';
import { UserInfoType } from '@common/const/type.ts';
import { useFetch } from '@common/hooks/http.ts';
import { QuestionCircleOutlined } from '@ant-design/icons';
import { Icon } from '@iconify/react/dist/iconify.js';
import { ProjectFilled } from '@ant-design/icons';
import { getNavItem } from '@common/utils/navigation';
import { Icon } from '@iconify/react';
import { $t } from '@common/locales';
import LanguageSetting from './LanguageSetting';
const APP_MODE = import.meta.env.VITE_APP_MODE;
export type MenuItem = Required<MenuProps>['items'][number];
const themeToken = {
bgLayout:'#17163E;',
header: {
@@ -38,10 +41,46 @@ const themeToken = {
const navigator = useNavigate()
const location = useLocation()
const currentUrl = location.pathname
const { accessData,checkPermission} = useGlobalContext()
const { state,accessData,checkPermission} = useGlobalContext()
const [pathname, setPathname] = useState(currentUrl);
const mainPage = project === 'core' ?'/service/list':'/serviceHub/list'
const TOTAL_MENU_ITEMS:MenuProps['items'] = useMemo(() => [
getNavItem($t('工作空间'), 'workspace','/tenantManagement',<Icon icon="ic:baseline-space-dashboard" width="18" height="18"/>, [
getNavItem($t('我的'), 'my','/tenantManagement',null,[
getNavItem(<a>{$t('应用')}</a>, 'tenantManagement','/tenantManagement',<Icon icon="ic:baseline-apps" width="18" height="18"/>,undefined,undefined,''),
getNavItem(<a>{$t('服务')}</a>, 'service','/service',<Icon icon="ic:baseline-blinds-closed" width="18" height="18"/>,undefined,undefined,''),
getNavItem(<a>{$t('团队')}</a>, 'team','/team',<Icon icon="ic:baseline-people-alt" width="18" height="18"/>,undefined,undefined,''),
],undefined,''),
getNavItem(<a>{$t('API 市场')}</a>, 'serviceHub','/serviceHub',<Icon icon="ic:baseline-hub" width="18" height="18"/>,undefined,undefined,'system.workspace.api_market.view'),
]),
APP_MODE === 'pro' ? getNavItem($t('仪表盘'), 'mainPage', '/dashboard',<Icon icon="ic:baseline-bar-chart" width="18" height="18"/>,[
getNavItem(<a >{$t('运行视图')}</a>, 'dashboard','/dashboard',<ProjectFilled />,undefined,undefined,''),
getNavItem(<a >{$t('系统拓扑图')}</a>, 'systemrunning','/systemrunning',<ProjectFilled />,undefined,undefined,''),
]):null,
getNavItem($t('系统设置'), 'operationCenter','/member',<Icon icon="ic:baseline-settings" width="18" height="18"/>, [
getNavItem($t('组织'), 'organization','/member',null,[
getNavItem(<a>{$t('成员')}</a>, 'member','/member',<Icon icon="ic:baseline-people-alt" width="18" height="18"/>,undefined,undefined,'system.organization.member.view'),
getNavItem(<a>{$t('角色')}</a>, 'role','/role',<Icon icon="ic:baseline-verified-user" width="18" height="18"/>,undefined,undefined,'system.organization.role.view'),
],undefined,''),
getNavItem($t('API 市场'), 'serviceHubSetting','/servicecategories',null,[
getNavItem(<a>{$t('服务分类管理')}</a>, 'servicecategories','/servicecategories',<Icon icon="ic:baseline-hub" width="18" height="18"/>,undefined,undefined,'system.api_market.service_classification.view'),
],undefined,'system.api_market.service_classification.view'),
getNavItem($t('运维与集成'), 'maintenanceCenter','/cluster', null, [
getNavItem(<a>{$t('集群')}</a>, 'cluster','/cluster',<Icon icon="ic:baseline-device-hub" width="18" height="18"/>,undefined,undefined,'system.devops.cluster.view'),
getNavItem(<a>{$t('证书')}</a>, 'cert','/cert',<Icon icon="ic:baseline-security" width="18" height="18"/>,undefined,undefined,'system.devops.ssl_certificate.view'),
getNavItem(<a>{$t('日志')}</a>, 'logsettings','/logsettings',<Icon icon="ic:baseline-sticky-note-2" width="18" height="18"/>,undefined,undefined,'system.devops.log_configuration.view'),
APP_MODE === 'pro' ? getNavItem(<a>{$t('资源')}</a>, 'resourcesettings','/resourcesettings',null,undefined,undefined,'system.partition.self.view'):null,
APP_MODE === 'pro' ? getNavItem(<a>{$t('Open API')}</a>, 'openapi','/openapi',null,undefined,undefined,'system.openapi.self.view'):null,
]),
]),
],[state.language])
useEffect(()=>{console.log(state.language, $t('工作空间'))},[state.language])
useEffect(() => {
if(currentUrl === '/'){
navigator(mainPage)
@@ -82,13 +121,13 @@ const themeToken = {
const res = [...TOTAL_MENU_ITEMS]!.filter(x => x).map((x: any) => (x.routes ? { ...x, routes: filterMenu(x.routes) } : x));
// 返回处理后的数据
return { path: '/', routes: res.map(x=> ({...x, routes: x.routes?.filter(x=> (x.access || x.routes?.length > 0))})).filter(x=> (x.access || x.routes?.length > 0)) };
}, [accessData]);
}, [accessData, state.language]);
const { modal,message } = App.useApp()
const { message } = App.useApp()
const { dispatch,resetAccess,getGlobalAccessData} = useGlobalContext()
const [userInfo,setUserInfo] = useState<UserInfoType>()
const resetPswRef = useRef<ResetPswHandle>(null)
const userProfileRef = useRef<UserProfileHandle>(null)
const {fetchData} = useFetch()
const navigate = useNavigate();
@@ -100,7 +139,7 @@ const themeToken = {
setUserInfo(data.profile)
dispatch({type:'UPDATE_USERDATA',userData:data.profile})
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
})
}
@@ -116,10 +155,10 @@ const themeToken = {
if(code === STATUS_CODE.SUCCESS){
dispatch({type:'LOGOUT'})
resetAccess()
message.success(msg || '退出成功,将跳转至登录页')
message.success(msg || RESPONSE_TIPS.logoutSuccess)
navigate('/login')
}else{
message.error(msg ||'操作失败')
message.error(msg ||RESPONSE_TIPS.error)
}
})
}
@@ -129,49 +168,19 @@ const themeToken = {
key: '2',
label: (
<Button key="changePsw" type="text" className="border-none p-0 flex items-center bg-transparent " onClick={()=>navigator('/userProfile/changepsw')}>
{$t('账号设置')}
</Button>)
},
{
key: '3',
label: (
<Button key="logout" type="text" className="border-none p-0 flex items-center bg-transparent " onClick={logOut}>
退
{$t('退出登录')}
</Button>)
},
];
const openModal = (type:'userSetting'|'resetPsw')=>{
let title:string = ''
let content:string|React.ReactNode = ''
switch (type){
case 'userSetting':
title='用户设置'
content=<UserProfile ref={userProfileRef} entity={userInfo}/>
break;
case 'resetPsw':
title='重置密码'
content=<ResetPsw ref={resetPswRef} entity={userInfo} />
break;
}
modal.confirm({
title,
content,
onOk:()=>{
switch (type){
case 'userSetting':
return userProfileRef.current?.save().then((res)=>{if(res === true) getUserInfo()})
case 'resetPsw':
return resetPswRef.current?.save().then((res)=>{if(res === true) logOut()})
}
},
width:600,
okText:'确认',
cancelText:'取消',
closable:true,
icon:<></>,
})
}
return(
<div
@@ -182,97 +191,98 @@ const themeToken = {
}}
>
<ProConfigProvider hashed={false}>
<ConfigProvider
getTargetContainer={() => {
return document.getElementById('test-pro-layout') || document.body;
}}
>
<ProLayout
prefixCls="apipark-layout"
location={{
pathname,
}}
siderWidth={220}
breakpoint={'lg'}
route={headerMenuData}
token={themeToken}
siderMenuType="group"
menu={{
type: 'group',
collapsedShowGroupTitle: true,
}}
disableMobile={true}
avatarProps={{
src: AvatarPic || userInfo?.avatar,
size: 'small',
title: userInfo?.username||'unknown',
render: (props, dom) => {
return (
<Dropdown
menu={{
items
}}
>
<div className='avatar-dom'>{dom}
</div>
</Dropdown>
);
},
}}
actionsRender={(props) => {
if (props.isMobile) return [];
if (typeof window === 'undefined') return [];
return [
<Button className=" text-[#ffffffb3] hover:text-[#fff] border-none" type="default" ghost onClick={()=>{window.open('https://docs.apipark.com','_blank')}}>
<span className='flex items-center gap-[8px]'> <Icon icon="ic:baseline-help" width="14" height="14"/></span>
</Button>
];
}}
headerTitleRender={() => (
<div className="w-[192px] flex items-center">
<img
className="h-[20px] cursor-pointer "
src={Logo}
onClick={()=> navigator(mainPage)}
/>
</div>
)}
logo={Logo}
pageTitleRender={()=>'APIPark - 企业API数据开放平台'}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
}}
menuItemRender={(item, dom) => (
<div
onClick={() => {
// 同级目录点击无效
if(item.key && routerKeyMap.get(item.key) && routerKeyMap.get(item.key).length > 0 && routerKeyMap.get(item.key)?.indexOf(pathname.split('/')[1]) !== -1){
return
}
if(item.key === pathname.split('/')[1]){
return
}
if(item.path){
navigator(item.path)
}
setPathname(item.path || '');
<ConfigProvider
getTargetContainer={() => {
return document.getElementById('test-pro-layout') || document.body;
}}
>
{dom}
</div>
)}
fixSiderbar={true}
layout='mix'
splitMenus={true}
collapsed={false}
collapsedButtonRender={false}
>
<div className={`w-full h-calc-100vh-minus-navbar pl-PAGE_INSIDE_X pt-PAGE_INSIDE_T ${currentUrl.startsWith('/role/list') ? 'overflow-auto' : 'overflow-hidden' }`}>
<Outlet />
</div>
</ProLayout>
</ConfigProvider>
<ProLayout
prefixCls="apipark-layout"
location={{
pathname,
}}
siderWidth={220}
breakpoint={'lg'}
route={headerMenuData}
token={themeToken}
siderMenuType="group"
menu={{
type: 'group',
collapsedShowGroupTitle: true,
}}
disableMobile={true}
avatarProps={{
src: AvatarPic || userInfo?.avatar,
size: 'small',
title: userInfo?.username||'unknown',
render: (props, dom) => {
return (
<Dropdown
menu={{
items
}}
>
<div className='avatar-dom'>{dom}
</div>
</Dropdown>
);
},
}}
actionsRender={(props) => {
if (props.isMobile) return [];
if (typeof window === 'undefined') return [];
return [
<LanguageSetting />,
<Button className=" text-[#ffffffb3] hover:text-[#fff] border-none" type="default" ghost onClick={()=>{window.open('https://docs.apipark.com','_blank')}}>
<span className='flex items-center gap-[8px]'> <Icon icon="ic:baseline-help" width="14" height="14"/>{$t('文档')}</span>
</Button>
];
}}
headerTitleRender={() => (
<div className="w-[192px] flex items-center">
<img
className="h-[20px] cursor-pointer "
src={Logo}
onClick={()=> navigator(mainPage)}
/>
</div>
)}
logo={Logo}
pageTitleRender={()=>$t('APIPark - 企业API数据开放平台')}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
}}
menuItemRender={(item, dom) => (
<div
onClick={() => {
// 同级目录点击无效
if(item.key && routerKeyMap.get(item.key) && routerKeyMap.get(item.key).length > 0 && routerKeyMap.get(item.key)?.indexOf(pathname.split('/')[1]) !== -1){
return
}
if(item.key === pathname.split('/')[1]){
return
}
if(item.path){
navigator(item.path)
}
setPathname(item.path || '');
}}
>
{dom}
</div>
)}
fixSiderbar={true}
layout='mix'
splitMenus={true}
collapsed={false}
collapsedButtonRender={false}
>
<div className={`w-full h-calc-100vh-minus-navbar pl-PAGE_INSIDE_X pt-PAGE_INSIDE_T ${currentUrl.startsWith('/role/list') ? 'overflow-auto' : 'overflow-hidden' }`}>
<Outlet />
</div>
</ProLayout>
</ConfigProvider>
</ProConfigProvider>
</div>
)
@@ -1,6 +1,7 @@
import { FC } from 'react';
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { $t } from '@common/locales';
interface DataType {
httpStatusCode: string;
@@ -11,17 +12,17 @@ interface DataType {
const columns: ColumnsType<DataType> = [
{
title: 'HTTP 状态码',
title:$t('HTTP 状态码'),
dataIndex: 'httpStatusCode',
key: 'httpStatusCode',
},
{
title: '系统状态码',
title:$t('系统状态码'),
dataIndex: 'systemStatusCode',
key: 'systemStatusCode',
},
{
title: '描述',
title: $t('描述'),
dataIndex: 'description',
key: 'description',
ellipsis:true
@@ -1,8 +0,0 @@
import { DatePicker } from 'antd';
import type { Moment } from 'moment';
import momentGenerateConfig from 'rc-picker/lib/generate/moment';
const MyDatePicker = DatePicker.generatePicker<Moment>(momentGenerateConfig);
export default MyDatePicker;
@@ -2,6 +2,7 @@
import { Button, Drawer, DrawerProps, Space } from "antd";
import WithPermission from "./WithPermission";
import { useEffect, useState } from "react";
import { $t } from '@common/locales';
export type DrawerWithFooterProps = DrawerProps & {
onSubmit?: () => Promise<boolean|string>|undefined
@@ -17,7 +18,7 @@ export type DrawerWithFooterProps = DrawerProps & {
cancelBtnTitle?:string
}
export function DrawerWithFooter(props:DrawerWithFooterProps){
const {children,title,placement='right',onClose,onSubmit,submitDisabled = false,okBtnTitle='提交',cancelBtnTitle,open,submitAccess,showLastStep,onLastStep,notAutoClose,showOkBtn=true,extraBtn} = props
const {children,title,placement='right',onClose,onSubmit,submitDisabled = false,okBtnTitle= $t('提交'),cancelBtnTitle,open,submitAccess,showLastStep,onLastStep,notAutoClose,showOkBtn=true,extraBtn} = props
const [submitLoading, setSubmitLoading] = useState<boolean>(false)
const handlerSubmit = ()=>{
setSubmitLoading(true)
@@ -41,12 +42,12 @@ export function DrawerWithFooter(props:DrawerWithFooterProps){
<Space className="flex flex-row-reverse" style={{}}>
{showOkBtn && <WithPermission access={submitAccess}>
<Button onClick={handlerSubmit} type="primary" loading={submitLoading} disabled={submitDisabled}>
{okBtnTitle}
{ okBtnTitle}
</Button>
</WithPermission>}
{ showLastStep && <Button onClick={onLastStep ?? onClose}></Button>}
{ showLastStep && <Button onClick={onLastStep ?? onClose}> { $t('上一步')}</Button>}
{ extraBtn }
<Button onClick={onClose}>{cancelBtnTitle ?? (showOkBtn ? '取消':'关闭')}</Button>
<Button onClick={onClose}>{cancelBtnTitle ?? (showOkBtn ? $t('取消'):$t('关闭'))}</Button>
</Space>
}
onClose={onClose}
@@ -1,11 +1,14 @@
import { EditableProTable, ProColumns } from "@ant-design/pro-components";
import { EditableProTable } from "@ant-design/pro-components";
import { Button } from "antd";
import { useState, useEffect } from "react";
import { v4 as uuidv4} from 'uuid';
import WithPermission from "./WithPermission";
import { PageProColumns } from "./PageList";
import { Icon } from "@iconify/react/dist/iconify.js";
import TableBtnWithPermission from "./TableBtnWithPermission";
interface EditableTableProps<T> {
configFields: ProColumns<T>[];
configFields: PageProColumns<T>[];
value?: T[]; // 外部传入的值
className?: string;
onChange?: (newConfigItems: T[]) => void; // 当配置项变化时,外部传入的回调函数
@@ -54,43 +57,36 @@ const EditableTable = <T extends { _id: string }>({
editableKeys:disabled ? [] : configurations?.map(x=>x._id),
actionRender: (row, config) => {
return [
<WithPermission access="" key="addPermission" ><Button type="text" className="h-[22px] border-none p-0 flex items-center bg-transparent "
key="add"
onClick={() => {
const newId = uuidv4();
setConfigurations((prev)=>{
const tmpPreData = [...prev];
const newId = uuidv4()
const lastRecord:{[k:string]:unknown} = tmpPreData[tmpPreData.length - 1];
const newRecord :{[k:string]:unknown, _id:string}= { _id: newId };
// 当extendsId的长度大于0时,根据extendsId指定的字段从最后一个record中复制值
if(extendsId && extendsId.length > 0) {
extendsId.forEach(field => {
newRecord[field] = lastRecord[field];
});
}
tmpPreData.splice(Number(config.index) + 1, 0,newRecord);
onChange?.(getNotEmptyValue(tmpPreData));
return tmpPreData});
setEditableRowKeys((prev)=>([...prev,newId]))
}}
>
</Button></WithPermission>,
(config.index !== configurations.length - 1 )&& <WithPermission access=""><Button type="text" className="h-[22px] border-none p-0 flex items-center bg-transparent "
key="edit"
onClick={() => {
setConfigurations((prev)=>{
const tmpPreData = [...prev];
tmpPreData.splice(Number(config.index), 1);
onChange?.(tmpPreData);
return tmpPreData});
setEditableRowKeys((prev)=>(prev.filter(x=>x !== config._id)))
}}
>
</Button></WithPermission>,
<TableBtnWithPermission key="add" btnType="add" onClick={() => {
const newId = uuidv4();
setConfigurations((prev)=>{
const tmpPreData = [...prev];
const newId = uuidv4()
const lastRecord:{[k:string]:unknown} = tmpPreData[tmpPreData.length - 1];
const newRecord :{[k:string]:unknown, _id:string}= { _id: newId };
// 当extendsId的长度大于0时,根据extendsId指定的字段从最后一个record中复制值
if(extendsId && extendsId.length > 0) {
extendsId.forEach(field => {
newRecord[field] = lastRecord[field];
});
}
tmpPreData.splice(Number(config.index) + 1, 0,newRecord);
onChange?.(getNotEmptyValue(tmpPreData));
return tmpPreData});
setEditableRowKeys((prev)=>([...prev,newId]))
}}
btnTitle="增加"/>,
(config.index !== configurations.length - 1 )&& <TableBtnWithPermission key="delete" btnType="delete" btnTitle="删除"
onClick={() => {
setConfigurations((prev)=>{
const tmpPreData = [...prev];
tmpPreData.splice(Number(config.index), 1);
onChange?.(tmpPreData);
return tmpPreData});
setEditableRowKeys((prev)=>(prev.filter(x=>x !== config._id)))
}}/>,,
];
},
onValuesChange: (record, recordList) => {
@@ -3,6 +3,8 @@ import { Button, Modal, Form, Table, FormInstance, TableProps, Divider } from 'a
import { v4 as uuidv4 } from 'uuid';
import { ColumnsType } from 'antd/es/table';
import WithPermission from './WithPermission';
import { $t } from '@common/locales';
import { COLUMNS_TITLE, VALIDATE_MESSAGE } from '@common/const/const';
export interface ConfigField<T> {
title: string;
@@ -90,9 +92,9 @@ const EditableTableWithModal = <T extends { _id?: string }>({
}));
!disabled && columns.push({
title: '操作',
title: COLUMNS_TITLE.operate,
key: 'action',
width:117,
btnNums:2,
render: (_: unknown, record: T) => (
<>
<div className="flex items-center">
@@ -109,7 +111,7 @@ const EditableTableWithModal = <T extends { _id?: string }>({
<Form.Item
label={title as string}
name={key as string}
rules={[{ required, message: `必填项`}]}
rules={[{ required, message: VALIDATE_MESSAGE.required}]}
>
{component}
</Form.Item>
@@ -119,12 +121,12 @@ const EditableTableWithModal = <T extends { _id?: string }>({
return (
<>
{!disabled && <Button className="" disabled={disabled} onClick={() => showModal()}></Button>}
{!disabled && <Button className="" disabled={disabled} onClick={() => showModal()}>{$t('添加配置')}</Button>}
{configurations.length > 0 &&
<Table
className={`mt-btnybase border-solid border-[1px] border-BORDER border-b-0 rounded ${className}`} {...tableProps} dataSource={configurations} size="small" columns={columns} rowKey="_id" pagination={false}/>}
<Modal
title={editingConfig ? '编辑配置' : '添加配置'}
title={editingConfig ? $t('编辑配置') : $t('添加配置')}
open={isModalVisible}
onOk={handleOk}
onCancel={handleCancel}
@@ -1,105 +0,0 @@
import DirectoryTree from "antd/es/tree/DirectoryTree";
import { DataNode, DirectoryTreeProps } from "antd/lib/tree";
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import TreeWithMore from "@common/components/aoplatform/TreeWithMore";
import { SearchOutlined } from "@ant-design/icons";
import { Input, Button, MenuProps } from "antd";
import { debounce } from "lodash-es";
import WithPermission from "@common/components/aoplatform/WithPermission";
import { v4 as uuidv4 } from 'uuid'
type T = unknown
export interface GroupTreeProps extends DirectoryTreeProps{
groupData?:(DataNode & T )[]
addBtnName?:React.ReactNode
addBtnAccess?:string
treeNameSuffixKey?:string
dropdownMenu?:(data:(DataNode & T )) => MenuProps['items']
withMore?:boolean
onEditGroup:(type:'rename'|'addChild'|'addPeer', entity:DataNode & T, val:string) => Promise<boolean>|undefined
placeholder?:string
}
export interface GroupTreeHandle {
startEdit:(id:string)=>void;
startAdd:(type:'peer',entity?:DataNode & T)=>void
}
const GroupTree = forwardRef<GroupTreeHandle,GroupTreeProps>((props, ref)=>{
const {groupData,selectedKeys,onSelect,addBtnName,addBtnAccess,treeNameSuffixKey,dropdownMenu,onEditGroup,placeholder="输入以搜索"} = props
const [treeData, setTreeData] = useState<DataNode[]>([])
const [searchWord, setSearchWord] = useState<string>('')
const [editingId, setEditingId] = useState<string>('')
const [addStatus, setAddStatus] = useState<boolean>(false)
useImperativeHandle(ref, ()=>({
startEdit:setEditingId,
startAdd:handlerAction
}))
const handlerAction = (type:'peer')=>{
if(type === 'peer'){
setAddStatus(true)
setEditingId(uuidv4())
}
}
const getTreeData = (rawData?:DataNode[])=>{
const loop = (data: DataNode[]): DataNode[] =>{
const newData = [...data,...(addStatus? [{title:'',key:editingId,id:editingId}]:[])]
return newData.map((item) => {
const strTitle = item.title as string;
const index = strTitle.indexOf(searchWord);
const beforeStr = strTitle.substring(0, index);
const afterStr = strTitle.slice(index + searchWord.length);
const title =
index > -1 ? (
<span >
{beforeStr}
<span className="text-theme">{searchWord}</span>
{afterStr} {treeNameSuffixKey && <span>({item?.[treeNameSuffixKey as keyof DataNode] as string ?? 0})</span>}
</span>) : (
<span className='w-[100%] truncate'>{strTitle}{treeNameSuffixKey && <span>({item?.[treeNameSuffixKey as keyof DataNode] as string?? 0})</span>}</span>
)
return {
title:<TreeWithMore dropdownMenu={dropdownMenu?.(item)} onBlur={()=>{setAddStatus(false);setEditingId('')}} editable editingId={editingId} entity={item} afterEdit={(val)=>onEditGroup?.(addStatus && editingId === item.key ? 'addPeer':'rename',item, val)?.then((res)=>{res && setEditingId('') ;res && setAddStatus(false) ; return res})}>{title}</TreeWithMore>,
key: item.key,
id:item.key
};
})
};
return rawData ? loop(rawData) :[];
}
const onSearchWordChange = (e:string)=>{
setSearchWord(e || '')
}
useEffect(()=>{
const n = getTreeData(groupData)
setTreeData(n)
},[groupData,editingId,searchWord])
return (
<>
<Input className="w-[calc(100%-24px)] mx-btnbase my-btnybase" onChange={(e) => debounce(onSearchWordChange, 100)(e.target.value)}
allowClear placeholder={placeholder}
prefix={ <SearchOutlined className="cursor-pointer" />}/>
<div className="max-h-[calc(100%-140px)] overflow-y-auto">
<DirectoryTree
icon={<></>}
blockNode={true}
treeData={treeData}
selectedKeys={selectedKeys}
onSelect={onSelect}
/>
</div>
{addBtnName && <WithPermission access={addBtnAccess}><Button className="h-[22px] mt-[20px] mb-[16px] bottom-[0px] sticky border-none p-0 flex items-center bg-transparent text-theme ml-[10px] hover:text-A_HOVER" key='add' onClick={()=>handlerAction('peer')} >{addBtnName}</Button></WithPermission>}
</>
)
})
export default GroupTree
@@ -4,6 +4,7 @@ import {useNavigate} from "react-router-dom";
import WithPermission from "@common/components/aoplatform/WithPermission";
import { FC, ReactNode } from "react";
import { ArrowLeftOutlined, LeftOutlined } from "@ant-design/icons";
import { $t } from "@common/locales";
class InsidePageProps {
@@ -36,7 +37,7 @@ const InsidePage:FC<InsidePageProps> = ({showBanner=true,pageTitle,tagList,showB
{ showBanner && <div className={`border-[0px] mr-PAGE_INSIDE_X ${showBorder ? 'border-b-[1px] border-solid border-BORDER' : ''}`}>
<div className="mb-[30px]">
{backUrl &&<div className="text-[18px] leading-[25px] mb-[12px]">
<Button type="text" onClick={goBack}><ArrowLeftOutlined className="max-h-[14px]" /></Button>
<Button type="text" onClick={goBack}><ArrowLeftOutlined className="max-h-[14px]" />{$t('返回')}</Button>
</div>}
<div className="flex justify-between mb-[20px] items-center ">
<div className="flex items-center gap-TAG_LEFT ">
@@ -0,0 +1,48 @@
import { Dropdown, Row, Col, Button } from 'antd';
import i18n from '@common/locales';
import { $t } from '@common/locales';
import { memo } from 'react';
import { useGlobalContext } from '@common/contexts/GlobalStateContext';
import { Icon } from '@iconify/react/dist/iconify.js';
const LanguageSetting = () => {
const { dispatch,state} = useGlobalContext()
const items = [
{
key: 'cn',
label: <Button key="cn" type="text" className="border-none p-0 flex items-center bg-transparent ">
{$t('简体')}
</Button>,
title: $t('简体'),
},
{
key: 'en',
label:<Button key="en" type="text" className="border-none p-0 flex items-center bg-transparent ">
{$t('英文')}
</Button>,
title:$t('英文')
}
];
const langLabel = items.find((item) => item?.key === state.language)?.title;
return (
<Dropdown
trigger={['hover']}
menu={{
items,
style:{minWidth:'80px'},
onClick: (e) => {
const { key } = e;
dispatch({ type: 'UPDATE_LANGUAGE', language: key });
i18n.changeLanguage(key);
}
}}
>
<Button className=" text-[#ffffffb3] hover:text-[#fff] border-none" type="default" ghost >
<span className='flex items-center gap-[8px]'> <Icon icon="ic:baseline-language" width="14" height="14"/>{langLabel}</span>
</Button>
</Dropdown>
);
};
export default memo(LanguageSetting);
@@ -2,12 +2,27 @@
import { GetProp, TransferProps, TreeDataNode, theme, Transfer, Tree, Spin } from "antd";
import { DataNode, TreeProps } from "antd/es/tree";
import { Ref, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
import { TransferTableHandle, TransferTableProps } from "./TransferTable";
import { ApartmentOutlined, LoadingOutlined, UserOutlined } from "@ant-design/icons";
import { debounce } from "lodash-es";
import { ColumnsType } from "antd/es/table";
type TransferItem = GetProp<TransferProps, 'dataSource'>[number];
export type TransferTableProps<T> = {
request?:(k?:string)=>Promise<{data:T[],success:boolean}>
columns: ColumnsType<T>
primaryKey:string
onSelect:(selectedData:T[])=>void
tableType?:'member'|'api'
disabledData:string[]
searchPlaceholder?:string
}
export type TransferTableHandle<T> = {
selectedData: () => T[];
selectedRowKeys: () => React.Key[];
}
interface TreeTransferProps {
dataSource: TreeDataNode[];
targetKeys: TransferProps['targetKeys'];
@@ -1,20 +0,0 @@
import { useEffect } from "react";
import { editor } from "monaco-editor";
import useInitializeMonaco from "@common/hooks/useInitializeMonaco";
import { Editor, useMonaco } from '@monaco-editor/react'
export type MonacoEditorRefType = editor.IStandaloneCodeEditor;
const MonacoEditorWrapper: React.FC = (props) => {
useInitializeMonaco();
const monacoInstance = useMonaco();
useEffect(() => {
if (monacoInstance) {
// 在这里你可以访问并配置Monaco实例
}
}, [monacoInstance]);
return <Editor {...props} />;
};
export default MonacoEditorWrapper;
@@ -1,100 +0,0 @@
import {FC, useEffect, useMemo, useState} from 'react';
import type { MenuProps } from 'antd';
import { Menu } from 'antd';
import { useLocation, useNavigate} from "react-router-dom";
import { getNavItem } from '@common/utils/navigation';
import { PERMISSION_DEFINITION } from '@common/const/permissions';
import { useGlobalContext } from '@common/contexts/GlobalStateContext';
import { ProjectFilled } from '@ant-design/icons';
import { Icon } from '@iconify/react';
export type MenuItem = Required<MenuProps>['items'][number];
const APP_MODE = import.meta.env.VITE_APP_MODE;
// avoid changing route within ths same category
export const routerKeyMap = new Map<string, string[]|string>([
['workspace',['tenantManagement','service','team','serviceHub']],
['my',['tenantManagement','service','team']],
['mainPage',['dashboard','systemrunning']],
['operationCenter',['member','user','role','servicecategories']],
['organization',['member','user','role']],
['serviceHubSetting',['servicecategories']],
['maintenanceCenter',['partition','logsettings','resourcesettings','openapi']
]])
export const TOTAL_MENU_ITEMS: MenuProps['items'] = [
getNavItem('工作空间', 'workspace','/tenantManagement',<Icon icon="ic:baseline-space-dashboard" width="18" height="18"/>, [
getNavItem('我的', 'my','/tenantManagement',null,[
getNavItem(<a></a>, 'tenantManagement','/tenantManagement',<Icon icon="ic:baseline-apps" width="18" height="18"/>,undefined,undefined,''),
getNavItem(<a></a>, 'service','/service',<Icon icon="ic:baseline-blinds-closed" width="18" height="18"/>,undefined,undefined,''),
getNavItem(<a></a>, 'team','/team',<Icon icon="ic:baseline-people-alt" width="18" height="18"/>,undefined,undefined,''),
],undefined,''),
getNavItem(<a>API </a>, 'serviceHub','/serviceHub',<Icon icon="ic:baseline-hub" width="18" height="18"/>,undefined,undefined,'system.workspace.api_market.view'),
]),
APP_MODE === 'pro' ? getNavItem('仪表盘', 'mainPage', '/dashboard',<Icon icon="ic:baseline-bar-chart" width="18" height="18"/>,[
getNavItem(<a ></a>, 'dashboard','/dashboard',<ProjectFilled />,undefined,undefined,''),
getNavItem(<a ></a>, 'systemrunning','/systemrunning',<ProjectFilled />,undefined,undefined,''),
]):null,
getNavItem('系统设置', 'operationCenter','/member',<Icon icon="ic:baseline-settings" width="18" height="18"/>, [
getNavItem('组织', 'organization','/member',null,[
getNavItem(<a></a>, 'member','/member',<Icon icon="ic:baseline-people-alt" width="18" height="18"/>,undefined,undefined,'system.organization.member.view'),
getNavItem(<a></a>, 'role','/role',<Icon icon="ic:baseline-verified-user" width="18" height="18"/>,undefined,undefined,'system.organization.role.view'),
],undefined,''),
getNavItem('API 市场', 'serviceHubSetting','/servicecategories',null,[
getNavItem(<a></a>, 'servicecategories','/servicecategories',<Icon icon="ic:baseline-hub" width="18" height="18"/>,undefined,undefined,'system.api_market.service_classification.view'),
],undefined,'system.api_market.service_classification.view'),
getNavItem('运维与集成', 'maintenanceCenter','/cluster', null, [
getNavItem(<a></a>, 'cluster','/cluster',<Icon icon="ic:baseline-device-hub" width="18" height="18"/>,undefined,undefined,'system.devops.cluster.view'),
getNavItem(<a></a>, 'cert','/cert',<Icon icon="ic:baseline-security" width="18" height="18"/>,undefined,undefined,'system.devops.ssl_certificate.view'),
getNavItem(<a></a>, 'logsettings','/logsettings',<Icon icon="ic:baseline-sticky-note-2" width="18" height="18"/>,undefined,undefined,'system.devops.log_configuration.view'),
APP_MODE === 'pro' ? getNavItem(<a></a>, 'resourcesettings','/resourcesettings',null,undefined,undefined,'system.partition.self.view'):null,
APP_MODE === 'pro' ? getNavItem(<a>Open API</a>, 'openapi','/openapi',null,undefined,undefined,'system.openapi.self.view'):null,
]),
]),
];
const Navigation: FC = () => {
const location = useLocation()
const [selectedKeys, setSelectedKeys] = useState<string>('')
const currentUrl = location.pathname
const navigateTo = useNavigate()
const { accessData,checkPermission} = useGlobalContext()
const onClick: MenuProps['onClick'] = (e) => {
if(location.pathname.split('/')[1] === e.key) return
const newUrl = routerKeyMap.get(e.key)
newUrl && navigateTo(newUrl)
};
const menuData = useMemo(()=>{
const filterMenu = (menu:Array<{[k:string]:unknown}>)=>{
return menu.filter(x=> x && (x.access ? checkPermission(x.access as keyof typeof PERMISSION_DEFINITION[0]): true))
}
return TOTAL_MENU_ITEMS!.filter(x=>x).map((x)=> ( x.children ? {...x, children:filterMenu(x.children)} : x))?.filter(x=> x.key === 'service' || (x.children && x.children?.length > 0))
},[accessData])
useEffect(() => {
setSelectedKeys(currentUrl.split('/')[1] === 'template' ? currentUrl.split('/')[2] : currentUrl.split('/')[1])
}, [currentUrl]);
return (
<Menu
onClick={onClick}
theme="dark"
style={{height:'100%' }}
selectedKeys={[selectedKeys]}
defaultOpenKeys={['mainPage','dataAssets','operationCenter','maintenanceCenter']}
mode="inline"
items={[...menuData]}
/>
);
};
export default Navigation;
@@ -15,9 +15,11 @@ import { useGlobalContext } from '../../contexts/GlobalStateContext';
import { PERMISSION_DEFINITION } from '@common/const/permissions';
import { withMinimumDelay } from '@common/utils/ux';
export type PageProColumns<T = any, ValueType = 'text'> = ProColumns<T , ValueType> & {btnNums? : number}
interface PageListProps<T> extends ProTableProps<T, unknown>, RefAttributes<ActionType> {
id?:string
columns: ProColumns<T,'text'>[]
columns: PageProColumns<T,'text'>[]
request?:(params: (ParamsType & {pageSize?: number | undefined, current?: number | undefined, keyword?: string | undefined}), sorter: unknown, filter: unknown)=>Promise<{data:T[], success:boolean}>
dropMenu?:MenuProps
searchPlaceholder?:string
@@ -49,6 +51,7 @@ interface PageListProps<T> extends ProTableProps<T, unknown>, RefAttributes<Acti
}
const PageList = <T extends Record<string, unknown>>(props: React.PropsWithChildren<PageListProps<T>>,ref: React.Ref<ActionType>) => {
const {id,columns,request,dropMenu,searchPlaceholder,showPagination=true,primaryKey='id',addNewBtnTitle,addNewBtnAccess,tableClickAccess,tableClass,onAddNewBtnClick,beforeSearchNode,onSearchWordChange,manualReloadTable,afterNewBtn,dragSortKey,onDragSortEnd,tableTitle,rowSelection,onChange,dataSource,onRowClick,showColSetting=false,minVirtualHeight,noTop,addNewBtnWrapperClass,tableTitleClass,delayLoading = true,besidesTableHeight, noScroll} = props
const parentRef = useRef<HTMLDivElement>(null);
@@ -79,7 +82,6 @@ const PageList = <T extends Record<string, unknown>>(props: React.PropsWithChild
const res = parentRef.current.getBoundingClientRect();
const height = res.height - ((noTop ? 0 : 59) + 54 + (showPagination && !dragSortKey ? 52 : 0) +( besidesTableHeight ?? 0) + 1); // 减去顶部按钮、底部分页、表头高度
setTableWidth(minTableWidth - 5> res.width ? minTableWidth : undefined);
console.log(minTableWidth,res.width )
height && setTableHeight(minVirtualHeight === undefined ? height : (height > minVirtualHeight ? height : minVirtualHeight));
}
};
@@ -114,7 +116,6 @@ const PageList = <T extends Record<string, unknown>>(props: React.PropsWithChild
let width:number = 0
const res = columns?.map(
(x, index)=>{
width += Number(x.width ?? ((x.filters || x.sorter) ? 120 : 100))
const sorter = localStorage.getItem(`${id}_sorter`)
const filters = localStorage.getItem(`${id}_filters`)
x.copyable = x.copyable ?? (index === 0 || x.dataIndex === 'id' || x.dataIndex === 'email')
@@ -129,6 +130,11 @@ const PageList = <T extends Record<string, unknown>>(props: React.PropsWithChild
const xName = Array.isArray(x.dataIndex) ? x.dataIndex.join(','):x.dataIndex
x.defaultFilteredValue = filtersObj?.[xName as string]
}
if((index === columns.length -1 || x.key === 'option') && x.btnNums){
const optionWidth = 24 + 18 * x.btnNums + (x.btnNums - 1) * 21
x.width = Math.max(optionWidth, 54)
}
width += Number(x.width ?? ((x.filters || x.sorter) ? 120 : 100))
return x})
setMinTableWidth(width)
return res
@@ -1,106 +1,13 @@
import {App, Col, Form, Input, Row, Table, Tooltip} from "antd";
import {App, Col, Form, Input, Row, Table} from "antd";
import {forwardRef, useEffect, useImperativeHandle} from "react";
import {PublishApprovalInfoType, PublishVersionTableListItem} from "@common/const/approval/type.tsx";
import {PublishApprovalInfoType, PublishApprovalModalHandle, PublishApprovalModalProps, PublishVersionTableListItem} from "@common/const/approval/type.tsx";
import {useFetch} from "@common/hooks/http.ts";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {BasicResponse, FORM_ERROR_TIPS, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE, VALIDATE_MESSAGE} from "@common/const/const.tsx";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { SYSTEM_PUBLISH_ONLINE_COLUMNS } from "@core/const/system/const.tsx";
import { SystemInsidePublishOnlineItems } from "@core/pages/system/publish/SystemInsidePublishOnline.tsx";
import { $t } from "@common/locales";
import { ApprovalApiColumns, ApprovalUpstreamColumns } from "@common/const/approval/const";
enum ChangeTypeEnum {
'new' = '新增',
'update' = '变更',
'delete' = '删除',
'none' = '无变更',
'error' = '缺失字段'
}
const statusColorClass = {
new: 'text-[#138913]', // 使用 Tailwind 的 Arbitrary Properties
update: 'text-[#03a9f4]',
delete: 'text-[#ff3b30]',
none: 'text-[var(--MAIN_TEXT)]', // 假设你也有一个“none”的状态
};
const apiColumns = [
{
title:'API 名称',
dataIndex:'name',
ellipsis:true
},
{
title:'请求方式',
dataIndex:'method',
ellipsis:true
},
{
title:'路径',
dataIndex:'path',
ellipsis:true
},
{
title:'类型',
dataIndex:'change',
render:(_,entity)=>(
<Tooltip placement="top" title={entity.change === 'error' ?`该 API 缺失 ${entity.proxyStatus == 1 && '转发信息,'} ${entity.docStatus == 1 && '文档信息,'} ${entity.upstreamStatus == 1 && '上游信息,'}请先补充`:''}>
<span className={`${statusColorClass[entity.change as keyof typeof statusColorClass]} truncate block`}>
{ChangeTypeEnum[entity.change as (keyof typeof ChangeTypeEnum)] || '-'}
{entity.change === 'error' ?` 该 API 缺失 ${entity.proxyStatus == 1 && '转发信息,'} ${entity.docStatus == 1 && '文档信息,'} ${entity.upstreamStatus == 1 && '上游信息,'}请先补充`:''}
</span>
</Tooltip>)
}
]
const upstreamColumns = [
{
title:'上游类型',
dataIndex:'type',
ellipsis:true,
// filters: true,
// onFilter: true,
// valueType: 'select',
// filterSearch: true,
valueEnum:{
'static':{
text:'静态上游'
},
// 'dynamic':{
// text:'动态上游'
// }
}
},
{
title:'地址',
dataIndex:'addr',
render:(text:string[])=>(<>{text.join(',')}</>),
ellipsis:true
},
{
title:'类型',
dataIndex:'change',
render:(_,entity)=>(
<Tooltip placement="top" title={entity.change === 'error' ?`该 API 缺失 ${entity.proxyStatus == 1 && '转发信息,'} ${entity.docStatus == 1 && '文档信息,'} ${entity.upstreamStatus == 1 && '上游信息,'}请先补充`:''}>
<span className={`${statusColorClass[entity.change as keyof typeof statusColorClass]} truncate block`}>{ChangeTypeEnum[entity.change as (keyof typeof ChangeTypeEnum)] || '-'}
{entity.change === 'error' ?` 该 API 缺失 ${entity.proxyStatus == 1 && '转发信息,'} ${entity.docStatus == 1 && '文档信息,'} ${entity.upstreamStatus == 1 && '上游信息,'}请先补充`:''}</span>
</Tooltip>)
}
]
type PublishApprovalModalProps = {
type:'approval'|'view'|'add'|'publish'|'online'
data:PublishApprovalInfoType | PublishApprovalInfoType &{id?:string} | PublishVersionTableListItem
insideSystem?:boolean
serviceId:string
teamId:string
clusterPublishStatus?:SystemInsidePublishOnlineItems[]
}
export type PublishApprovalModalHandle = {
save:(operate:'pass'|'refuse') =>Promise<boolean|string>
publish:(notSave?:boolean)=>Promise<boolean|string|Record<string, unknown>>
online:()=>Promise<boolean|string>
}
export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle,PublishApprovalModalProps>((props, ref) => {
const { message } = App.useApp()
@@ -115,19 +22,19 @@ export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle
return form.validateFields().then((value)=>{
if(operate === 'refuse' && form.getFieldValue('opinion') === '' ){
form.setFields([{
name:'opinion',errors:['选择拒绝时,审批意见为必填']
name:'opinion',errors:[FORM_ERROR_TIPS.refuseOpinion]
}])
form.scrollToField('opinion')
return Promise.reject('未填写审核意见')
return Promise.reject(RESPONSE_TIPS.refuseOpinion)
}
return fetchData<BasicResponse<null>>(`service/publish/${operate === 'pass' ? 'accept' : 'refuse'}`,{method: 'PUT',eoBody:({comments:value.opinion}), eoParams:{id:data!.id, project:serviceId},eoTransformKeys:['versionRemark']}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
return Promise.resolve(true)
}else{
message.error(msg || '操作失败')
return Promise.reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
return Promise.reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> Promise.reject(errorInfo))
}).catch((err)=> {form.scrollToField(err.errorFields[0].name[0]); return Promise.reject(err)})
@@ -141,11 +48,11 @@ export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle
notSave ? 'service/publish/apply' : 'service/publish/release/do',{method: 'POST',eoBody:body, eoParams:{service:serviceId, team:teamId},eoTransformKeys:['versionRemark']}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(response)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
@@ -158,11 +65,11 @@ export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle
fetchData<BasicResponse<null>>('service/publish/execute',{method: 'PUT', eoParams:{project:serviceId,id:(data as PublishVersionTableListItem).flowId},eoTransformKeys:['versionRemark']}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
@@ -184,22 +91,22 @@ export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle
<>
{!insideSystem && <>
<Row className="my-mbase">
<Col className="text-left" span={4}><span ></span></Col>
<Col className="text-left" span={4}><span >{$t('申请系统')}</span></Col>
<Col span={18}>{(data as PublishApprovalInfoType).project || '-'}</Col>
</Row>
<Row className="my-mbase">
<Col className="text-left" span={4}><span ></span></Col>
<Col className="text-left" span={4}><span >{$t('所属团队')}</span></Col>
<Col span={18}>{(data as PublishApprovalInfoType).team || '-'}</Col>
</Row>
<Row className="my-mbase">
<Col className="text-left" span={4}><span ></span></Col>
<Col className="text-left" span={4}><span >{$t('申请人')}</span></Col>
<Col span={18}>{(data as PublishApprovalInfoType).applier || '-'}</Col>
</Row>
<Row className="my-mbase">
<Col className="text-left" span={4}><span ></span></Col>
<Col className="text-left" span={4}><span >{$t('申请时间')}</span></Col>
<Col span={18}>{(data as PublishApprovalInfoType).applyTime || '-'}</Col>
</Row>
</> }
@@ -220,54 +127,54 @@ export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle
insideSystem &&
<>
<Form.Item
label="版本号"
label={$t("版本号")}
name="version"
rules={[{required: true, message: '必填项',whitespace:true }]}
rules={[{required: true, message: VALIDATE_MESSAGE.required,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" disabled={type !== 'add'} placeholder="请输入" />
<Input className="w-INPUT_NORMAL" disabled={type !== 'add'} placeholder={PLACEHOLDER.input} />
</Form.Item>
<Form.Item
label="版本说明"
label={$t("版本说明")}
name="versionRemark"
>
<Input.TextArea className="w-INPUT_NORMAL" disabled={type !== 'add' && type !== 'publish'} placeholder="请输入" />
<Input.TextArea className="w-INPUT_NORMAL" disabled={type !== 'add' && type !== 'publish'} placeholder={PLACEHOLDER.input} />
</Form.Item>
</>
}
<Row className="mt-mbase pb-[8px] h-[32px] font-bold" ><span >API </span></Row>
<Row className="mt-mbase pb-[8px] h-[32px] font-bold" ><span >{$t('API 列表')}</span></Row>
<Row className="mb-mbase ">
<Table
columns={apiColumns}
columns={ApprovalApiColumns}
bordered={true}
rowKey="id"
size="small"
dataSource={data.diffs?.apis || []}
pagination={false}
/></Row>
<Row className="mt-mbase pb-[8px] h-[32px] font-bold" ><span ></span></Row>
<Row className="mt-mbase pb-[8px] h-[32px] font-bold" ><span >{$t('上游列表')}</span></Row>
<Row className="mb-mbase ">
<Table
bordered={true}
columns={upstreamColumns}
columns={ApprovalUpstreamColumns}
size="small"
rowKey="id"
dataSource={data.diffs?.upstreams || []}
pagination={false}
/></Row>
<Form.Item
label="备注"
label={$t("备注")}
name="remark"
>
<Input.TextArea className="w-INPUT_NORMAL" disabled={type !== 'add' && type !== 'publish'} placeholder="请输入" />
<Input.TextArea className="w-INPUT_NORMAL" disabled={type !== 'add' && type !== 'publish'} placeholder={PLACEHOLDER.input} />
</Form.Item>
{/*
{type !== 'add' && type !== 'publish' && <Form.Item
label="审批意见"
label={$t("审批意见"
name="opinion"
extra="选择拒绝时,审批意见为必填"
>
<Input.TextArea className="w-INPUT_NORMAL" placeholder="请输入" onChange={()=>{ form.setFields([
<Input.TextArea className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input} onChange={()=>{ form.setFields([
{
name: 'opinion',
errors: [], // 设置为空数组来移除错误信息
@@ -1,134 +0,0 @@
import { Form, Input} from "antd";
import {forwardRef, useEffect, useImperativeHandle} from "react";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { UserInfoType } from "@common/const/type.ts";
type FieldType = {
userName:string
old:string
password:string
confirm:string
}
type ResetPswProps = {
entity?:UserInfoType
}
export type ResetPswHandle = {
save:()=>Promise<boolean|string>
}
export const ResetPsw = forwardRef<ResetPswHandle,ResetPswProps>((props,ref)=>{
const [form] = Form.useForm();
const save:()=>Promise<boolean | string> = ()=>{
return new Promise((resolve)=>{
// form.validateFields().then((value)=>{
// fetchData<BasicResponse<null>>(url,{method,eoBody:(value), eoTransformKeys:['departmentIds']}).then(response=>{
// const {code,msg} = response
// if(code === STATUS_CODE.SUCCESS){
// message.success(msg || '操作成功!')
resolve(true)
// }else{
// message.error(msg || '操作失败')
// reject(msg || '操作失败')
// }
// })
// }).catch((errorInfo)=> reject(errorInfo))
})
}
const getPswStrength = (value: string) => {
const pswRegNum: RegExp = /[0-9]/
const pswRegLowercase: RegExp = /[a-z]/
const pswRegUppercase: RegExp = /[A-Z]/
const pswRegSymbol: RegExp = /!@#$%^&*`~()-+=/
let strength: number = 0
if (pswRegNum.test(value)) {
strength++
}
if (pswRegLowercase.test(value)) {
strength++
}
if (pswRegUppercase.test(value)) {
strength++
}
if (pswRegSymbol.test(value)) {
strength++
}
return strength
}
useImperativeHandle(ref, ()=>({
save
})
)
useEffect(() => {
// form.setFieldsValue({id:entity!.id})
}, []);
return (<WithPermission access="">
<Form
labelAlign='left'
layout='vertical'
form={form}
scrollToFirstError
className="mx-auto mt-mbase ml-mbase"
name="resetPsw"
// labelCol={{ span: 8 }}
// wrapperCol={{ span: 10}}
autoComplete="off"
>
<Form.Item<FieldType>
label="账号"
name="userName"
hidden
rules={[{ required: true, message: '必填项',whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="账号" disabled={true}/>
</Form.Item>
<Form.Item<FieldType>
label="旧密码"
name="old"
rules={[{ required: true, message: '必填项',whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入6-32位字符"/>
</Form.Item>
<Form.Item<FieldType>
label="新密码"
name="password"
hidden
// eslint-disable-next-line @typescript-eslint/no-unused-vars
rules={[{ required: true, message: '必填项',whitespace:true }, ({ getFieldValue }) => ({
validator(_, value) {
if (!value || getPswStrength(value)>1) {
return Promise.resolve();
}
return Promise.reject(new Error('密码强度:弱,建议使用英文、数字、特殊字符组合'));
},
})]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入6-32位字符"/>
</Form.Item>
<Form.Item<FieldType>
label="确认新密码"
name="confirm"
dependencies={['password']}
rules={[{ required: true, message: '必填项',whitespace:true }, ({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('新密码与确认新密码不一致'));
},
})]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入6-32位字符"/>
</Form.Item>
</Form>
</WithPermission>)
})
@@ -1,9 +1,11 @@
import {App, Checkbox, Col, Form, Input, Row} from "antd";
import {App, Col, Form, Input, Row} from "antd";
import { forwardRef, useEffect, useImperativeHandle} from "react";
import {SubscribeApprovalInfoType} from "@common/const/approval/type.tsx";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {BasicResponse, FORM_ERROR_TIPS, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { SubscribeApprovalList } from "@common/const/approval/const";
import { $t } from "@common/locales";
type SubscribeApprovalModalProps = {
type:'approval'|'view'
@@ -22,26 +24,6 @@ type FieldType = {
opinion?:string;
};
const list = [
{
title:'申请方应用',key:'application'
},
{
title:'申请方所属团队',key:'applyTeam'
},
{
title:'申请人',key:'applier'
},
{
title:'申请时间',key:'applyTime'
},
{
title:'申请服务',key:'service'
},
{
title:'服务所属团队',key:'team'
}
]
export const SubscribeApprovalModalContent = forwardRef<SubscribeApprovalModalHandle,SubscribeApprovalModalProps>((props, ref) => {
const { message } = App.useApp()
const {data, type,inSystem=false, teamId, serviceId} = props
@@ -57,20 +39,20 @@ export const SubscribeApprovalModalContent = forwardRef<SubscribeApprovalModalHa
form.validateFields().then((value)=>{
if(operate === 'refuse' && form.getFieldValue('opinion') === ''){
form.setFields([{
name:'opinion',errors:['必填项']
name:'opinion',errors:[FORM_ERROR_TIPS.refuseOpinion]
}])
form.scrollToField('opinion')
reject('未填写审核意见')
reject(RESPONSE_TIPS.refuseOpinion)
return
}
fetchData<BasicResponse<null>>(`${inSystem?'service/':''}approval/subscribe`,{method: 'POST',eoBody:({opinion:value.opinion,operate}), eoParams:(inSystem ? {apply:data!.id, team:teamId} : {id:data!.id,team:teamId})}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
@@ -88,7 +70,7 @@ export const SubscribeApprovalModalContent = forwardRef<SubscribeApprovalModalHa
return (
<div className="my-btnybase">{
list?.map((x)=>(
SubscribeApprovalList?.map((x)=>(
<Row key={x.key} className="leading-[32px] mb-btnbase mx-auto">
<Col className="text-left" span={6}>{x.title}</Col>
<Col >{(data as {[k:string]:unknown})?.[x.key]?.name || (data as {[k:string]:unknown})?.[x.key] || '-'}</Col>
@@ -109,17 +91,17 @@ export const SubscribeApprovalModalContent = forwardRef<SubscribeApprovalModalHa
>
<Form.Item<FieldType>
label="申请原因"
label={$t("申请原因")}
name="reason"
>
<Input.TextArea className="w-INPUT_NORMAL" disabled={true} placeholder=" " />
</Form.Item>
<Form.Item<FieldType>
label="审核意见"
label={$t("审核意见")}
name="opinion"
extra="选择拒绝时,审批意见为必填"
extra={FORM_ERROR_TIPS.refuseOpinion}
>
<Input.TextArea className="w-INPUT_NORMAL" placeholder="请输入" onChange={()=>{ form.setFields([
<Input.TextArea className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input} onChange={()=>{ form.setFields([
{
name: 'opinion',
errors: [], // 设置为空数组来移除错误信息
@@ -1,20 +1,38 @@
import { Button, Tooltip } from "antd"
import { useState, useMemo, useEffect } from "react"
import { useState, useMemo, useEffect, useCallback } from "react"
import { useGlobalContext } from "@common/contexts/GlobalStateContext"
import { useNavigate } from "react-router-dom"
import { PERMISSION_DEFINITION } from "@common/const/permissions"
import { Icon } from "@iconify/react/dist/iconify.js"
import { $t } from "@common/locales"
type TableBtnWithPermissionProps = {
btnTitle:string
access:string,
access?:keyof typeof PERMISSION_DEFINITION[0],
tooltip?:string,
disabled?:boolean,
navigateTo?:string,
onClick?:(args?:unknown)=>void
className?:string
btnType:string
}
const TableIconName={
'add':'ic:baseline-add',
'edit':'ic:baseline-edit',
'delete':'ic:baseline-delete',
'copy':'ic:baseline-file-copy',
'view':'ic:baseline-remove-red-eye',
'publish':'ic:baseline-publish',
'approval':'ic:baseline-approval',
'stop':'ic:baseline-stop-circle',
'online':'ic:baseline-check-circle',
'cancel':'ic:baseline-cancel-schedule-send',
'refresh':'ic:baseline-refresh'
}
// 表格操作栏按钮,受权限控制
const TableBtnWithPermission = ({btnTitle, access, tooltip, disabled, navigateTo, onClick,className}:TableBtnWithPermissionProps) => {
const TableBtnWithPermission = ({btnTitle, access, tooltip, disabled, navigateTo, onClick,className,btnType}:TableBtnWithPermissionProps) => {
const [btnAccess, setBtnAccess] = useState<boolean>(false)
const {accessData,checkPermission} = useGlobalContext()
@@ -22,19 +40,27 @@ const TableBtnWithPermission = ({btnTitle, access, tooltip, disabled, navigateTo
const lastAccess = useMemo(()=>{
if(!access) return true
return checkPermission(access)
},[access, accessData])
},[access, accessData,checkPermission])
useEffect(()=>{
access ? setBtnAccess(lastAccess) : setBtnAccess(true)
},[])
},[access, lastAccess])
const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
navigateTo ? navigate(navigateTo) : onClick?.()
}, [navigateTo, navigate, onClick])
return (<>{
!btnAccess || (disabled&&tooltip) ?
<Tooltip placement="top" title={tooltip ?? `暂无${btnTitle}权限,请联系管理员分配。`}>
<Button type="text" disabled={true} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className}`} key="view" >{btnTitle}</Button>
<Button type="text" disabled={true} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className}`} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18"/>} >{}</Button>
</Tooltip>
:
<Button type="text" disabled={disabled} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className} `} key="view" onClick={(e)=>{e.stopPropagation();navigateTo ? navigate(navigateTo) :onClick?.() }}>{btnTitle}</Button>
<Tooltip placement="top" title={$t(btnTitle)}>
<Button type="text" disabled={disabled} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className} `} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18"/>} onClick={handleClick}>{}</Button>
</Tooltip>
}</>
);
@@ -1,16 +0,0 @@
import {forwardRef} from 'react';
import type { PickerProps } from 'antd/es/date-picker/generatePicker';
import type { Moment } from 'moment';
import DatePicker from './DatePicker';
export interface TimePickerProps extends Omit<PickerProps<Moment>, 'picker'> {}
const TimePicker = forwardRef<unknown, TimePickerProps>((props, ref) => (
<DatePicker {...props} picker="time" mode={undefined} ref={ref} />
));
TimePicker.displayName = 'TimePicker';
export default TimePicker;
@@ -4,6 +4,7 @@ import { Radio, DatePicker, GetProps, RadioChangeEvent } from 'antd';
import dayjs, { Dayjs } from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import "../../index.css"
import { $t } from '@common/locales';
type RangePickerProps = GetProps<typeof DatePicker.RangePicker>;
export type RangeValue = [Dayjs | null, Dayjs | null] | null;
@@ -94,12 +95,12 @@ const disabledDate: RangePickerProps['disabledDate'] = (current) => {
return (
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
{!hideTitle && <label className={`whitespace-nowrap `}></label>}
{!hideTitle && <label className={`whitespace-nowrap `}>{$t('时间')}</label>}
<Radio.Group className="whitespace-nowrap" value={timeButton} onChange={handleRadioChange} buttonStyle="solid">
<Radio.Button value="hour">1</Radio.Button>
<Radio.Button value="day">24</Radio.Button>
<Radio.Button value="threeDays">3</Radio.Button>
<Radio.Button className="rounded-e-none" value="sevenDays">7</Radio.Button>
<Radio.Button value="hour">{$t('近1小时')}</Radio.Button>
<Radio.Button value="day">{$t('近24小时')}</Radio.Button>
<Radio.Button value="threeDays">{$t('近3天')}</Radio.Button>
<Radio.Button className="rounded-e-none" value="sevenDays">{$t('近7天')}</Radio.Button>
</Radio.Group>
<DatePicker.RangePicker
value={datePickerValue}
@@ -1,28 +0,0 @@
.transfer-table-member,
.transfer-table-api{
:global .ant-table-wrapper .ant-table-thead >tr>th,
:global .ant-table-wrapper .ant-table-thead >tr>td{
font-weight: normal;
background-color: var(--MAIN_BG);
border:none;
}
:global .ant-table-wrapper .ant-table-tbody-virtual .ant-table-cell{
border:none;
}
:global .rc-virtual-list-scrollbar.rc-virtual-list-scrollbar-horizontal{
display: none;
}
:global .ant-table-wrapper .ant-table-tbody .ant-table-row > .ant-table-cell-row-hover{
background: #EBEEF2;
}
:global .ant-table-wrapper .ant-table-tbody .ant-table-row.ant-table-row-selected > .ant-table-cell-row-hover{
background: #EBEEF2;
}
:global .ant-table-thead >tr>th:not(:last-child):not(.ant-table-selection-column):not(.ant-table-row-expand-icon-cell):not([colspan])::before{
display: none;
}
}
@@ -1,193 +0,0 @@
import { Input,Table} from "antd";
import {forwardRef, KeyboardEventHandler, Ref, useCallback, useEffect, useImperativeHandle, useRef, useState} from "react";
import styles from './TransferTable.module.css'
import {CloseOutlined, SearchOutlined} from "@ant-design/icons";
import {debounce} from "lodash-es";
import {ColumnsType} from "antd/es/table";
export type TransferTableProps<T> = {
request?:(k?:string)=>Promise<{data:T[],success:boolean}>
columns: ColumnsType<T>
primaryKey:string
onSelect:(selectedData:T[])=>void
tableType?:'member'|'api'
disabledData:string[]
searchPlaceholder?:string
}
export type TransferTableHandle<T> = {
selectedData: () => T[];
selectedRowKeys: () => React.Key[];
}
const TransferTable = forwardRef<TransferTableHandle<{[k:string]:unknown}>, TransferTableProps<{[k:string]:unknown}>>(
<T extends {[k:string]:unknown}>(props: TransferTableProps<T>, ref:Ref<TransferTableHandle<T>>) => {
const {request,columns,primaryKey,onSelect,tableType,disabledData = [],searchPlaceholder} = props
const tblRef: Parameters<typeof Table>[0]['ref'] = useRef(null);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>(disabledData);
const [selectedData, setSelectedData] = useState<Array<T>>([])
const [dataSource, setDataSource] = useState<T[]>([])
const [searchWord, ] = useState<string>('')
const [loading, setLoading] = useState<boolean>(false)
const parentRef = useRef<HTMLDivElement>(null);
const [tableHeight, setTableHeight] = useState(window.innerHeight * 80 / 100 );
const [tableShow, setTableShow] = useState(false);
useImperativeHandle(ref, () =>({
selectedData: () => selectedData,
selectedRowKeys: () => selectedRowKeys,}))
const handlerLeftTableClick = (record:T & {[k:string]:string})=>{
if(disabledData.indexOf(record[primaryKey||'id' as string] )!== -1) return
const tmpSelectedRowKeys = [...selectedRowKeys];
if (tmpSelectedRowKeys.indexOf(record[primaryKey||'id' as string]) !== -1) {
tmpSelectedRowKeys.splice(tmpSelectedRowKeys.indexOf(record[primaryKey||'id' as string]), 1);
} else {
tmpSelectedRowKeys.push(record[primaryKey||'id' as string]);
}
setSelectedRowKeys(tmpSelectedRowKeys);
let tmpSelectedData = [...selectedData]
if(tmpSelectedData.filter((x: T)=>x[primaryKey || 'id'] === record[primaryKey||'id' as string]).length > 0){
tmpSelectedData = tmpSelectedData.filter((x: T)=>x[primaryKey || 'id'] !== record[primaryKey||'id' as string])
}else{
tmpSelectedData.push(record)
}
setSelectedData(tmpSelectedData)
}
// const handlerRightTableClick = (record:T & {[k:string]:string})=>{
// const tmpSelectedRowKeys = [...selectedRowKeys];
// if (tmpSelectedRowKeys.indexOf(record[primaryKey||'id' as string]) >= 0) {
// tmpSelectedRowKeys.splice(tmpSelectedRowKeys.indexOf(record[primaryKey||'id' as string]), 1);
// }
// setSelectedRowKeys(tmpSelectedRowKeys);
// let tmpSelectedData = [...selectedData]
// tmpSelectedData = tmpSelectedData.filter((x: {[k:string]:string})=>x[primaryKey || 'id'] !== record[primaryKey||'id' as string])
// setSelectedData(tmpSelectedData)
// }
const onSelectChange = (newSelectedRowKeys: React.Key[], selectedRow:T[]) => {
setSelectedRowKeys(newSelectedRowKeys);
setSelectedData(selectedRow.filter((x:T)=>disabledData.indexOf(x[primaryKey || 'id'] as string) === -1))
};
const removeItem = (item:T )=>{
setSelectedRowKeys(selectedRowKeys.filter((x)=>{return x!==item[primaryKey || 'id']}))
setSelectedData((prevData)=>prevData.filter((x:T)=>(x[primaryKey || 'id'] !== item[primaryKey || 'id'])))
}
useEffect(() => {
onSelect && onSelect(selectedData)
}, [selectedData]);
const operations = [
{
title: '操作',
key: 'option',
width: 40,
valueType: 'option',
render: (_: React.ReactNode, entity: T) => [
<CloseOutlined className="p-0 w-full h-full hover:bg-bar-theme" onClick={()=>removeItem(entity as T)}/>
],
}
]
const onSearchWordChange = (e: KeyboardEventHandler<HTMLInputElement>)=>{
getDataSource(e.target.value)
}
const getDataSource = (curSearchWord?:string)=>{
setLoading(true)
request && request(curSearchWord ?? searchWord).then((res)=>{
const {data,success} = res
setDataSource(success? data : [])
}).finally(()=>{setLoading(false)})
}
const debouncedSearch = useCallback(
debounce(onSearchWordChange, 600),[]
)
useEffect(() => {
getDataSource()
const handleResize = () => {
if (parentRef.current) {
const res = parentRef.current.getBoundingClientRect();
setTableHeight(res.height - 32 - 12 * 2 -42 -2)
setTimeout(()=>setTableShow(true),100)
// setTableWidth(res.width / 2 - 20 - 2 - 40)
// const height = res.height -( noTop ? 0 :52) - (dragSortKey ? 0 : 53) - 40;// 减去顶部按钮、底部分页、表头高度
// height && setTableHeight(height);
}
};
const debouncedHandleResize = debounce(handleResize, 200);
// 创建一个 ResizeObserver 来监听高度变化
const resizeObserver = new ResizeObserver(debouncedHandleResize);
// 开始监听
if (parentRef.current ) {
resizeObserver.observe(parentRef.current);
}
// 清理函数
return () => {
resizeObserver.disconnect();
};
}, []);
return (
<div ref={parentRef} className={styles['transfer-table-'+`${tableType === 'member'? 'member':'api'}`] + ` flex flex-1 w-[550px] min-h-[404px] overflow-hidden`}>
<div className="flex flex-col border-[1px] border-solid border-BORDER w-[calc(50%-10px)]">
<Input className=" m-btnbase w-[calc(100%-24px)]" onChange={ debouncedSearch} onPressEnter={onSearchWordChange} allowClear placeholder={searchPlaceholder || "请输入"} prefix={<SearchOutlined className="cursor-pointer" onClick={()=>{onSearchWordChange}}/>} />
{tableShow && <Table
columns={columns}
virtual
size="small"
scroll={{ x:100 ,y:tableHeight}}
rowKey={primaryKey || 'id'}
dataSource={dataSource}
pagination={false}
loading={loading}
ref={tblRef}
rowSelection={
{
selectedRowKeys,
onChange: onSelectChange,
columnWidth: 40,
getCheckboxProps: (record: {[k:string]:string}) => ({
disabled: disabledData.length > 0 && disabledData?.indexOf(record[primaryKey || 'id'] as string) !== -1, // Column configuration not to be checked
name: record[primaryKey || 'id'],
}),
}
}
onRow={(record) => ({
onClick: () => {
handlerLeftTableClick(record);
}
})}
/>}
</div>
<div className= {`flex flex-col w-[calc(50%-10px)] ml-[20px] border-[1px] border-solid border-BORDER`}>
<div className="leading-[32px] mt-btnybase mb-[54px]">
<span className="ml-[20px]">{tableType === 'member' ? '成员' : ' API'} <span className="mr-[4px]">({selectedData.length})</span></span>
</div>
<Table
virtual
scroll={{x:200,y:tableHeight}}
size="small"
columns={[...columns?.map((col)=>({...col,className:(col.className || ' ') + 'pl-[20px]'})),...operations]}
showHeader={false}
rowKey={primaryKey}
dataSource={selectedData}
pagination={false}
ref={tblRef}
loading={loading}
/>
</div>
</div>);
})
export default TransferTable
@@ -1,120 +0,0 @@
import {App, Avatar, Dropdown, MenuProps} from "antd";
import {useGlobalContext} from "@common/contexts/GlobalStateContext.tsx";
import {FC, useEffect, useRef, useState} from "react";
import {ResetPsw, ResetPswHandle} from "@common/components/aoplatform/ResetPsw.tsx";
import {useFetch} from "@common/hooks/http.ts";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {useNavigate} from "react-router-dom";
import { UserInfoType, UserProfileHandle } from "@common/const/type.ts";
import { UserProfile } from "./UserProfile.tsx";
import AvatarPic from '@common/assets/avatar_default.svg'
const UserAvatar: FC = () => {
const { modal,message } = App.useApp()
const { dispatch,resetAccess,getGlobalAccessData} = useGlobalContext()
const [userInfo,setUserInfo] = useState<UserInfoType>()
const resetPswRef = useRef<ResetPswHandle>(null)
const userProfileRef = useRef<UserProfileHandle>(null)
const {fetchData} = useFetch()
const navigate = useNavigate();
const getUserInfo = ()=>{
fetchData<BasicResponse<{profile:UserInfoType}>>('account/profile',{method:'GET'})
.then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setUserInfo(data.profile)
dispatch({type:'UPDATE_USERDATA',userData:data.profile})
}else{
message.error(msg || '操作失败')
}
})
}
useEffect(() => {
getUserInfo()
getGlobalAccessData()
}, []);
const logOut = ()=>{
fetchData<BasicResponse<null>>('account/logout',{method:'GET'}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
dispatch({type:'LOGOUT'})
resetAccess()
message.success(msg || '退出成功,将跳转至登录页')
navigate('/login')
}else{
message.error(msg ||'操作失败')
}
})
}
const items: MenuProps['items'] = [
// {
// key: '1',
// label: (
// <a target="_blank" rel="noopener noreferrer" onClick={()=>openModal('userSetting')}>
// 用户设置
// </a>
// ),
// },
// {
// key: '2',
// label: (
// <a target="_blank" rel="noopener noreferrer" onClick={()=>openModal('resetPsw')}>
// 修改密码
// </a>
// ),
// },
{
key: '3',
label: (
<a className="block leading-[32px]" target="_blank" rel="noopener noreferrer" onClick={logOut}>
退
</a>
),
},
];
const openModal = (type:'userSetting'|'resetPsw')=>{
let title:string = ''
let content:string|React.ReactNode = ''
switch (type){
case 'userSetting':
title='用户设置'
content=<UserProfile ref={userProfileRef} entity={userInfo}/>
break;
case 'resetPsw':
title='重置密码'
content=<ResetPsw ref={resetPswRef} entity={userInfo} />
break;
}
modal.confirm({
title,
content,
onOk:()=>{
switch (type){
case 'userSetting':
return userProfileRef.current?.save().then((res)=>{if(res === true) getUserInfo()})
case 'resetPsw':
return resetPswRef.current?.save().then((res)=>{if(res === true) logOut()})
}
},
width:600,
okText:'确认',
cancelText:'取消',
closable:true,
icon:<></>,
})
}
return (
<Dropdown menu={{ items }}>
<span className="flex items-center"><Avatar className="mx-[6px]" src={AvatarPic || userInfo?.avatar}></Avatar><span>{userInfo?.username||'unknown'}</span></span>
</Dropdown>
)
}
export default UserAvatar
@@ -1,152 +0,0 @@
import {App, Form, Input, Upload, UploadFile, UploadProps} from "antd";
import {forwardRef, useEffect, useImperativeHandle, useState} from "react";
import {useFetch} from "@common/hooks/http.ts";
import {RcFile, UploadChangeParam} from "antd/es/upload";
import {LoadingOutlined} from "@ant-design/icons";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import { UserInfoType, UserProfileHandle, UserProfileProps } from "@common/const/type";
import { getImgBase64 } from "@common/utils/dataTransfer";
import { Icon } from "@iconify/react/dist/iconify.js";
export const UserProfile = forwardRef<UserProfileHandle,UserProfileProps>((props,ref)=>{
const { message } = App.useApp()
const [form] = Form.useForm();
const {entity,} = props
const {fetchData} = useFetch()
const [imageBase64, setImageBase64] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const [imageUrl, setImageUrl] = useState<string>();
const save:()=>Promise<boolean | string> = ()=>{
return new Promise((resolve, reject)=>{
form.validateFields().then((value)=>{
fetchData<BasicResponse<null>>('account/profile',{method:'PUT',eoBody:value}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
})
}
useImperativeHandle(ref, ()=>({
save
})
)
const handleChange: UploadProps['onChange'] = (info: UploadChangeParam<UploadFile>) => {
if (info.file.status === 'uploading') {
setLoading(true);
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
getImgBase64(info.file.originFileObj as RcFile, (url) => {
setLoading(false);
setImageUrl(url);
});
}
if (info.fileList.length === 0) {
// 如果文件被移除,清除 logo 字段
form.setFieldValue( "avatar", null );
}
};
const uploadButton = (
<div>
<div className="h-[68px] w-[68px] border-[1px] border-dashed border-BORDER flex items-center justify-center rounded bg-bar-theme cursor-pointer" style={{ marginTop: 8 }}>
{loading ? <LoadingOutlined /> : <Icon icon="ic:baseline-add" width="18" height="18" className='mr-[2px]'/>}</div>
</div>
);
const beforeUpload = (file: RcFile) => {
const reader = new FileReader();
reader.onload = (e: ProgressEvent<FileReader>) => {
setImageBase64(e.target?.result as string);
form.setFieldValue("avatar",e.target?.result)
};
reader.readAsDataURL(file);
return false;
};
useEffect(() => {
form.setFieldsValue(entity)
}, []);
const normFile = (e: unknown) => {
if (Array.isArray(e)) {
return e;
}
return( e as {fileList:unknown} )?.fileList;
};
return (<>
<Form
labelAlign='left'
layout='vertical'
scrollToFirstError
form={form}
className="mx-auto mt-mbase "
name="userProfile"
// labelCol={{ span: 8 }}
// wrapperCol={{ span: 10}}
autoComplete="off"
>
<Form.Item<UserInfoType>
label="账号"
name="username"
rules={[{ required: true, message: '必填项',whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="账号" disabled={true}/>
</Form.Item>
<Form.Item<UserInfoType>
label="昵称"
name="nickname"
rules={[{ required: true, message: '必填项',whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
</Form.Item>
<Form.Item<UserInfoType>
label="头像"
name="avatar"
valuePropName="fileList" getValueFromEvent={normFile}
>
<Upload
listType="picture"
beforeUpload={beforeUpload}
onChange={handleChange}
showUploadList={false}
maxCount={1}
>
{imageBase64 ? <img src={imageBase64} alt="Logo" style={{ maxWidth: '200px'}} /> : uploadButton}
</Upload>
</Form.Item>
<Form.Item<UserInfoType>
label="邮箱"
name="email"
rules={[{ required: true, message: '必填项' ,whitespace:true },{type:'email',message: '输入的不是有效邮箱格式'}]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
</Form.Item>
<Form.Item<UserInfoType>
label="手机号码"
name="phone"
rules={[{pattern:/^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/, message:'输入的不是有效手机号码',warningOnly: true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
</Form.Item>
</Form>
</>)
})
@@ -3,6 +3,7 @@ import { Tooltip } 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[]
@@ -20,7 +21,7 @@ const WithPermission = ({access, tooltip, children,disabled, showDisabled = true
const lastAccess = useMemo(()=>{
if(!access) return true
return checkPermission(access as keyof typeof PERMISSION_DEFINITION[0])
},[access, accessData])
},[access, accessData,checkPermission])
useEffect(()=>{
// 先判断权限,无论权限是否为true,如果disabled为true时则必须为ture
@@ -36,7 +37,7 @@ const WithPermission = ({access, tooltip, children,disabled, showDisabled = true
{editAccess && disabled && <Tooltip title={tooltip}>
{ cloneElement(children, {disabled:true})}
</Tooltip>}
{!editAccess && (children?.type?.displayName !== 'Button' && showDisabled ) && <Tooltip title={tooltip ?? "暂无操作权限,请联系管理员分配。"}>
{!editAccess && (children?.type?.displayName !== 'Button' && showDisabled ) && <Tooltip title={tooltip ?? $t("暂无操作权限,请联系管理员分配。")}>
{ cloneElement(children, {disabled:true})}
</Tooltip>}
@@ -39,6 +39,7 @@ import {
} from '@formily/antd-v5'
import { CustomCodeboxComponent } from './CustomCodeboxComponent.tsx'
import { SimpleMapComponent } from './SimpleMapComponent.tsx'
import { $t } from '@common/locales/index.ts'
const SchemaField = createSchemaField({
components: {
@@ -96,7 +97,7 @@ export const CustomDialogComponent = forwardRef(
className="ant-formily-array-base-config"
onClick={() => {
const dialog = FormDialog(
editPage ? `编辑${title || ''}` : `添加${title || ''}`,
editPage ? $t('编辑(0)',[title||'']) : $t('添加(0)',[title||'']),
() => {
return (
<FormLayout
@@ -1,13 +1,14 @@
import {forwardRef, useImperativeHandle, useState} from 'react'
import { Input } from '@formily/antd-v5'
import { $t } from '@common/locales'
export const SimpleMapComponent = forwardRef(
(props: { [k: string]: unknown }, ref) => {
const {
onChange,
value,
placeholderKey = '请输入Key',
placeholderValue = '请输入Value'
placeholderKey = $t('请输入Key'),
placeholderValue = $t('请输入Value')
} = props
const [kvList, setKvList] = useState(
@@ -43,12 +43,10 @@ import {CustomDialogComponent} from "@common/components/aoplatform/formily2-cust
import {ArrayItemBlankComponent} from "@common/components/aoplatform/formily2-customize/ArrayItemBlankComponent.tsx";
import {DefaultOptionType} from "antd/es/cascader";
import {createSchemaField, FormProvider, RecursionField, useField, useForm} from "@formily/react";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {BasicResponse, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts";
import {App} from "antd";
import { config } from "process";
import { $t } from "@common/locales";
export const DynamicRender = (props) => {
const {schema} = props
@@ -158,7 +156,7 @@ export const IntelligentPluginConfig = forwardRef<IntelligentPluginConfigHandle
properties: {
id: {
type: 'string',
title: 'ID',
title: $t('ID'),
required: true,
pattern: /^[a-zA-Z][a-zA-Z0-9-_]*$/,
'x-decorator': 'FormItem',
@@ -169,13 +167,13 @@ export const IntelligentPluginConfig = forwardRef<IntelligentPluginConfigHandle
},
'x-component': 'Input',
'x-component-props': {
placeholder: '支持字母开头、英文数字中横线下划线组合',
placeholder: PLACEHOLDER.specialStartWithAlphabet,
},
'x-disabled': type === 'edit'
},
title: {
type: 'string',
title: '名称',
title: $t('名称'),
required: true,
'x-decorator': 'FormItem',
'x-decorator-props': {
@@ -185,12 +183,12 @@ export const IntelligentPluginConfig = forwardRef<IntelligentPluginConfigHandle
},
'x-component': 'Input',
'x-component-props': {
placeholder: '请输入名称',
placeholder: PLACEHOLDER.input,
}
},
driver: {
type: 'string',
title: 'Driver',
title: $t('Driver'),
required: true,
'x-decorator': 'FormItem',
'x-decorator-props': {
@@ -207,7 +205,7 @@ export const IntelligentPluginConfig = forwardRef<IntelligentPluginConfigHandle
},
description: {
type: 'string',
title: '描述',
title: $t('描述'),
'x-decorator': 'FormItem',
'x-decorator-props': {
labelCol:4,
@@ -216,7 +214,7 @@ export const IntelligentPluginConfig = forwardRef<IntelligentPluginConfigHandle
},
'x-component': 'Input.TextArea',
'x-component-props': {
placeholder: '请输入描述',
placeholder: PLACEHOLDER.input,
}
},
config: {
@@ -238,11 +236,11 @@ export const IntelligentPluginConfig = forwardRef<IntelligentPluginConfigHandle
fetchData<BasicResponse<null>>(type === 'add'?`dynamic/${moduleId}`:`dynamic/${moduleId}/config`,{method:type === 'add'? 'POST' : 'PUT',eoBody:form.values, eoParams:{...(type !== 'add' && {id:initFormValue.id})}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo:unknown)=> reject(errorInfo))
@@ -262,8 +260,8 @@ export const IntelligentPluginConfig = forwardRef<IntelligentPluginConfigHandle
if(code === STATUS_CODE.SUCCESS){
resolve(data[skill]?.map((x:{name:string,title:string})=>{return{label:x.title, value:x.name}}) || [])
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
})
})
@@ -1,19 +1,20 @@
import PageList from "@common/components/aoplatform/PageList.tsx";
import PageList, { PageProColumns } from "@common/components/aoplatform/PageList.tsx";
import {App, Divider, Spin} from "antd";
import {useEffect, useRef, useState} from "react";
import { useLocation, useOutletContext, useParams} from "react-router-dom";
import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx";
import {ActionType, ParamsType, ProColumns} from "@ant-design/pro-components";
import {ActionType, ParamsType} from "@ant-design/pro-components";
import {RouterParams} from "@core/components/aoplatform/RenderRoutes.tsx";
import {DefaultOptionType} from "antd/es/cascader";
import {IntelligentPluginConfig, IntelligentPluginConfigHandle} from "./IntelligentPluginConfig.tsx";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {BasicResponse, COLUMNS_TITLE, DELETE_TIPS, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts";
import {EntityItem} from "@common/const/type.ts";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import TableBtnWithPermission from "@common/components/aoplatform/TableBtnWithPermission.tsx";
import { DrawerWithFooter } from "@common/components/aoplatform/DrawerWithFooter.tsx";
import { LoadingOutlined } from "@ant-design/icons";
import { $t } from "@common/locales/index.ts";
type DynamicTableField = {
name: string,
@@ -95,7 +96,7 @@ export default function IntelligentPluginList(){
const [tableListDataSource, setTableListDataSource] = useState<DynamicTableItem[]>([]);
const [tableHttpReload, setTableHttpReload] = useState(true);
const [columns,setColumns] = useState<ProColumns<DynamicTableItem>[] >([])
const [columns,setColumns] = useState<PageProColumns<DynamicTableItem>[] >([])
const {fetchData} = useFetch()
const pageListRef = useRef<ActionType>(null);
const [publishBtnLoading, setPublishBtnLoading] = useState<boolean>(false)
@@ -159,7 +160,7 @@ export default function IntelligentPluginList(){
const {title,drivers} = basic
setBreadcrumb([
{title:location.includes('resourcesettings') ? '资源配置': '日志配置'},
{title:location.includes('resourcesettings') ? $t('资源'): $t('日志')},
{
title
}
@@ -178,23 +179,23 @@ export default function IntelligentPluginList(){
setRenderSchema(resp.data.render)
return Promise.resolve(resp.data.render)
}
return Promise.reject(resp.msg || '操作失败')
return Promise.reject(resp.msg || RESPONSE_TIPS.error)
})
}
const operation:ProColumns<DynamicTableItem>[] =[
const operation:PageProColumns<DynamicTableItem>[] =[
{
title: '操作',
title: COLUMNS_TITLE.operate,
key: 'option',
width: 150,
fixed:'right',
valueType: 'option',
btnNums:3,
render: (_: React.ReactNode, entity: DynamicTableItem) => [
<TableBtnWithPermission access={`${accessPrefix}.publish`} key="publish" onClick={()=>{openModal('publish',entity)}} btnTitle={entity.status === '已发布' ? '下线' : '上线'}/>,
<TableBtnWithPermission access={`${accessPrefix}.publish`} key="publish" btnType="publish" onClick={()=>{openModal('publish',entity)}} btnTitle={entity.status === $t('已发布') ? $t('下线') : $t('上线')}/>,
<Divider type="vertical" className="mx-0" key="div1"/>,
<TableBtnWithPermission access={`${accessPrefix}.view`} key="edit" onClick={()=>{openDrawer('edit',entity)}} btnTitle="查看"/>,
<TableBtnWithPermission access={`${accessPrefix}.view`} key="edit" btnType="edit" onClick={()=>{openDrawer('edit',entity)}} btnTitle={$t("查看")}/>,
<Divider type="vertical" className="mx-0" key="div2"/>,
<TableBtnWithPermission access={`${accessPrefix}.delete`} key="delete" onClick={()=>{openModal('delete',entity)}} btnTitle="删除"/>,
<TableBtnWithPermission access={`${accessPrefix}.delete`} key="delete" btnType="delete" onClick={()=>{openModal('delete',entity)}} btnTitle={$t("删除")}/>,
],
}
]
@@ -213,11 +214,11 @@ export default function IntelligentPluginList(){
fetchData<BasicResponse<null>>(`dynamic/${moduleId}/batch`,{method:'DELETE',eoParams:{ids:JSON.stringify([entity!.id])}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
})
})
@@ -239,7 +240,7 @@ export default function IntelligentPluginList(){
}
setCurDetail(data.info)
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
}).finally(()=>setDrawerLoading(false))
break;
@@ -254,25 +255,25 @@ export default function IntelligentPluginList(){
let content:string|React.ReactNode = ''
switch (type){
case 'publish':{
message.loading('正在操作')
await fetchData<BasicResponse<DynamicPublish>>(`dynamic/${moduleId}/${entity!.status === '已发布' ? 'offline':'online'}`, {
message.loading(RESPONSE_TIPS.operating)
await fetchData<BasicResponse<DynamicPublish>>(`dynamic/${moduleId}/${entity!.status === $t('已发布') ? 'offline':'online'}`, {
method: 'PUT',
eoParams:{id:entity!.id},
}).then(response => {
const {code, msg} = response
if (code === STATUS_CODE.SUCCESS) {
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
return Promise.resolve(true)
} else {
message.error(msg || '操作失败')
return Promise.reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
return Promise.reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> Promise.reject(errorInfo))
message.destroy()
return;}
case 'delete':
title='删除'
content=<span><span className="text-status_fail"></span></span>
content=<span>{DELETE_TIPS.default}</span>
break;
}
@@ -287,11 +288,11 @@ export default function IntelligentPluginList(){
}
},
width: type === 'delete'? 600 : 900,
okText:'确认',
okText:$t('确认'),
okButtonProps:{
disabled:false
},
cancelText:'取消',
cancelText:$t('取消'),
closable:true,
icon:<></>,
footer:(_, { OkBtn, CancelBtn }) =>{
@@ -316,8 +317,8 @@ export default function IntelligentPluginList(){
ref={pageListRef}
columns = {[...columns,...operation]}
request={(params)=>getIntelligentPluginTableList(params)}
addNewBtnTitle={`添加${pluginName}`}
searchPlaceholder={`搜索${pluginName}名称`}
addNewBtnTitle={$t('添加(0)',[$t(pluginName)])}
searchPlaceholder={$t('搜索(0)名称',[$t(pluginName)])}
onChange={() => {
setTableHttpReload(false)
}}
@@ -326,7 +327,7 @@ export default function IntelligentPluginList(){
onSearchWordChange={(e)=>{setSearchWord(e.target.value);setTableHttpReload(true);setTableHttpReload(true)}}
/>
<DrawerWithFooter title={`${drawerType === 'add' ? '添加' : '编辑'}${pluginName }`} open={drawerOpen} onClose={()=>{setCurDetail(undefined);setDrawerOpen(false)}} onSubmit={()=>drawerFormRef.current?.save()?.then((res)=>{res && manualReloadTable();return res})} submitAccess=''>
<DrawerWithFooter title={`${drawerType === 'add' ? $t('添加') : $t('编辑')}${pluginName }`} open={drawerOpen} onClose={()=>{setCurDetail(undefined);setDrawerOpen(false)}} onSubmit={()=>drawerFormRef.current?.save()?.then((res)=>{res && manualReloadTable();return res})} submitAccess=''>
<Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin/>} spinning={drawerLoading}>
<IntelligentPluginConfig
ref={drawerFormRef!}
@@ -1,3 +1,5 @@
import { $t } from "@common/locales"
export type PARAM_TYPE =
| 'string'
| 'float'
@@ -113,7 +115,7 @@ export const CODE_SNIPPETS: CODE_LANGUAGE_SNIPPETS_TYPE[] = [
]
},
{
label: '微信小程序',
label: $t('微信小程序'),
value: 21,
isLeaf: true
},
@@ -2,6 +2,7 @@ import { cloneDeep } from 'lodash-es'
import { parseFormData, parseFileValue, parseFileType, parseHeaders, parseRequestBodyToString, parseUri, payloadStr, goCodeParseFormData } from './transform'
// import { getJson } from '../.@common/utils/';
import { ApiBodyType } from '@common/const/api-detail';
import { $t } from '@common/locales';
function sameNameToParams(params: unknown) {
params = cloneDeep(params)
@@ -522,7 +523,7 @@ export function generateCode(
if (multipart) {
code =
'<?php\r\n' +
'//获取文件,需填路径 \r\n' +
`//${$t('获取文件,需填路径')} \r\n` +
'$file_path = "";\r\n' +
`$file_name = "${langTmp.fileValue}";\r\n` +
'$client = new http\\Client;\r\n' +
@@ -599,7 +600,7 @@ export function generateCode(
if (multipart) {
code =
'<?php\r\n\r\n' +
'//获取文件,需填路径 \r\n' +
`//${$t('获取文件,需填路径')} \r\n` +
'$file_path = ""; \r\n\r\n' +
'$curl = curl_init();\r\n\r\n' +
'curl_setopt_array($curl, array(\r\n' +
@@ -12,6 +12,7 @@ import {ApiDetail} from "@common/const/api-detail";
import {Codebox} from "@common/components/postcat//api/Codebox";
import {Collapse} from "@common/components/postcat/api/Collapse";
import {Box} from "@mui/material";
import { $t } from '@common/locales';
type CodeSnippetCompoType = {
title:string
@@ -156,7 +157,7 @@ type CodeSnippetCompoType = {
let tempCode = ''
const getCode = (language: number | string) => {
if (!['HTTPS', 'HTTP'].includes(api.protocol?.toUpperCase())) {
tempCode = '暂不支持生成非 HTTPS 或非 HTTP 协议的代码示例'
tempCode = $t('暂不支持生成非 HTTPS 或非 HTTP 协议的代码示例')
setCode(tempCode)
return
}
@@ -186,7 +187,7 @@ type CodeSnippetCompoType = {
(option) => (option.label as string).toLowerCase().indexOf(inputValue.toLowerCase()) > -1,
);
const [placeholderTxt, setPlaceholderTxt] = useState('搜索编程语言...')
const [placeholderTxt, setPlaceholderTxt] = useState($t('搜索编程语言...'))
const [selectItemTxt, setSelectItemTxt ] = useState('')
return (
@@ -194,7 +195,7 @@ type CodeSnippetCompoType = {
<Collapse title={title}>
<Box width="100%">
<>
<Codebox extraContent={<><span className="ml-[12px]"></span><Cascader
<Codebox extraContent={<><span className="ml-[12px]">{$t('编程语言')}</span><Cascader
options={CODE_LANG}
onChange={(value,record) => onChange(value as unknown as number[],record)}
placeholder={placeholderTxt}
@@ -1,4 +1,4 @@
import * as Mock from 'mockjs'
import {Random} from 'mockjs'
import { PARAM_KEY_REF_TYPE, PARAM_LIST_TYPE, PARAM_LIS_ITEM_TYPE, PARAM_TYPE, PARAM_TYPE_REF_TYPE } from "./code-snippets.type"
const DEFAULT_PARAM_KEY_REF: PARAM_KEY_REF_TYPE = {
key: 'key',
@@ -127,5 +127,5 @@ export function tranformJson(
return result.join('\n') //分隔符会换行
}
export function getRandomDataByType(type: PARAM_TYPE) {
return Mock.Random[Object.keys(Mock.Random).includes(type) ? type : 'string'](0, 5)
return Random[Object.keys(Random).includes(type) ? type : 'string'](0, 5)
}
@@ -5,14 +5,15 @@ import {Collapse} from "@common/components/postcat/api/Collapse";
import {Box} from "@mui/material";
import {Codebox} from "@common/components/postcat/api/Codebox";
import { cloneDeep } from 'lodash-es';
import { $t } from '@common/locales';
export interface ResponseExampleCompoEditorApi {
getData: () => ResultListType[] | []
}
const DEFAULT_RESULT_LIST = [
{id:'success',name:'成功示例',httpCode:'200',content:''},
{id:'failed',name:'失败示例',httpCode:'200',content:''},
{id:'success',name:$t('成功示例'),httpCode:'200',content:''},
{id:'failed',name:$t('失败示例'),httpCode:'200',content:''},
]
export const HTTP_STATUS_CODE = ['200', '403', '404', '410', '422', '500', '502', '503', '504']
@@ -76,7 +77,7 @@ export function ResponseExampleCompo ({ editorRef,title,detail,mode='view' }: {e
value={item.httpCode}
status={item.httpCode ? '' : 'error'}
onSelect={(value)=>updateResultList(item.id,'httpCode',value)}
placeholder="HTTP 状态码"
placeholder={$t("HTTP 状态码")}
/>
}
{mode === 'view' ?
@@ -86,7 +87,7 @@ export function ResponseExampleCompo ({ editorRef,title,detail,mode='view' }: {e
style={{ width: 200 }}
value={item.httpContentType || 'text/html;charset=UTF-8'}
onSelect={(value)=>updateResultList(item.id,'httpContentType',value)}
placeholder="默认 text/html;charset=UTF-8"
placeholder={$t("默认 text/html;charset=UTF-8")}
/>}
</div>
{mode === 'view' ?
@@ -94,7 +95,7 @@ export function ResponseExampleCompo ({ editorRef,title,detail,mode='view' }: {e
{ item.content ?
<pre className="border-[1px] border-solid border-BORDER p-[6px] rounded w-auto min-h-[130px] max-h-[500px] overflow-auto mt-[0px]">{item.content}</pre>
:
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂未填写示例"/>
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={$t("暂未填写示例")}/>
}</>
: <>
<Codebox value={item.content} language='json' width="100%" height={'250px'} onChange={(value)=>updateResultList(item.id,'content',value)}/>
@@ -11,6 +11,8 @@ import { SystemApiDetail, SystemInsideApiProxyHandle } from "@core/const/system/
import SystemInsideApiProxy from "@core/pages/system/api/SystemInsideApiProxy";
import ApiMatch from "./api/ApiPreview/components/ApiMatch";
import {v4 as uuidv4} from 'uuid'
import { PLACEHOLDER } from "@common/const/const";
import { $t } from "@common/locales";
const PROTOCOL_LIST = ['HTTP','HTTPS']
const HTTP_METHOD_LIST = ['POST','GET','PUT', 'DELETE','HEAD','OPTIONS','PATCH']
@@ -165,7 +167,7 @@ export default function ApiEdit({apiInfo,editorRef,loaded,serviceId, teamId}:{ap
getData: () => {
return proxyRef.current?.validate().then((res)=>{
const name = apiNameRef.current?.getData()
if(!name) return Promise.reject('请填写接口名称')
if(!name) return Promise.reject($t('请填写接口名称'))
const newData :{apiInfo:Partial<SystemApiDetail>}= {
apiInfo:{
info:{
@@ -200,7 +202,7 @@ export default function ApiEdit({apiInfo,editorRef,loaded,serviceId, teamId}:{ap
getData:()=>description
}))
return (
<Input.TextArea className="w-full border-none" value={description} onChange={(e)=>setDescription(e.target.value)} placeholder="请输入"/>
<Input.TextArea className="w-full border-none" value={description} onChange={(e)=>setDescription(e.target.value)} placeholder={PLACEHOLDER.input}/>
)
})
@@ -229,25 +231,25 @@ export default function ApiEdit({apiInfo,editorRef,loaded,serviceId, teamId}:{ap
<Box>
<Stack direction="column" spacing={3}>
<ApiName apiInfo={apiInfo} ref={apiNameRef}/>
<Collapse key="description" title='详细说明'>
<Collapse key="description" title={$t('详细说明')}>
<Description initDescription={apiInfo?.description} ref={descriptionRef}/>
</Collapse>
{
apiInfo?.match && apiInfo.match?.length > 0 &&
<ApiMatch title='高级匹配' rows={apiInfo?.match.map((x)=>{x.id = uuidv4();return x})} />
<ApiMatch title={$t('高级匹配')} rows={apiInfo?.match.map((x)=>{x.id = uuidv4();return x})} />
}
<Collapse title='转发配置' key="proxy" >
<Collapse title={$t('转发配置')} key="proxy" >
<SystemInsideApiProxy className="m-[12px] px-[12px]" initProxyValue={apiInfo?.proxy} serviceId={serviceId!} ref={proxyRef} />
</Collapse>
<Collapse title='请求参数' key="request" >
<Collapse title={$t('请求参数')} key="request" >
<ApiRequestEditor editorRef={requestRef} apiInfo={apiInfo?.doc} loaded={loaded} />
</Collapse>
<Collapse title='返回值' key="response">
<Collapse title={$t('返回值')} key="response">
<ApiResponseEditor editorRef={responseRef} apiInfo={apiInfo?.doc} loaded={loaded}/>
</Collapse>
<ResponseExampleCompo editorRef={resultListRef} mode='edit' title='返回示例' detail={resultList}/>
<ResponseExampleCompo editorRef={resultListRef} mode='edit' title={$t('返回示例')} detail={resultList}/>
</Stack>
</Box>
</Box>
@@ -15,11 +15,12 @@ import {MessageType} from "./api/ApiManager/components/MessageDataGrid";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { ThemeProvider } from "@mui/material";
import { theme } from "./ApiEdit.tsx";
import { $t } from "@common/locales/index.ts";
export const SearchBtn = ({entity}:{entity:unknown})=>{
return (
<Tooltip >
<span className="text-disabled"> API</span>
<span className="text-disabled">{$t('测试 API')}</span>
</Tooltip>
)
}
@@ -90,13 +91,13 @@ export default function ApiPreview(props:{testClick?:()=>void, entity:ApiDetail}
{
requestParams?.headerParams?.length > 0 &&
<HeaderFields title='请求 Header' rows={requestParams?.headerParams}
<HeaderFields title={$t('请求 Header')} rows={requestParams?.headerParams}
onMoreSettingChange={setCurrentMoreSettingParam} />
}
{requestBodyList?.length > 0 &&
<MessageBodyComponent
title="请求 Body"
title={$t("请求 Body")}
rows={requestBodyList}
contentType={requestParams?.bodyParams[0]?.contentType}
onMoreSettingChange={setCurrentMoreSettingParam}
@@ -105,40 +106,40 @@ export default function ApiPreview(props:{testClick?:()=>void, entity:ApiDetail}
{
requestParams?.queryParams?.length > 0 &&
<HeaderFields title='Query 参数' rows={requestParams?.queryParams}
<HeaderFields title={$t('Query 参数')} rows={requestParams?.queryParams}
onMoreSettingChange={setCurrentMoreSettingParam} />
}
{
requestParams?.restParams?.length > 0 &&
<HeaderFields title='Rest 参数' rows={requestParams?.restParams}
<HeaderFields title={$t('Rest 参数')} rows={requestParams?.restParams}
onMoreSettingChange={setCurrentMoreSettingParam}/>
}
{/*<h3 className="text-lg mb-btnybase font-normal flex items-center">请求示例代码</h3>*/}
<CodeSnippetCompo
title='请求示例代码'
title={$t('请求示例代码')}
api={entity}
extraContent={ testClick ? <div className="ml-5">
<Tooltip >
<WithPermission access="" >
<Button type='primary' onClick={handleTest} size='small' className='w-[114px]'> API</Button>
<Button type='primary' onClick={handleTest} size='small' className='w-[114px]'>{$t('测试 API')}</Button>
</WithPermission>
</Tooltip>
</div> : undefined }
/>
{resultList?.length > 0 && <ResponseExampleCompo title='响应示例' detail={resultList}/>}
{resultList?.length > 0 && <ResponseExampleCompo title={$t('响应示例')} detail={resultList}/>}
{
responseList?.[0]?.responseParams?.headerParams?.length > 0 &&
<HeaderFields title='响应 Header' rows={ responseList?.[0]?.responseParams?.headerParams}
<HeaderFields title={$t('响应 Header')} rows={ responseList?.[0]?.responseParams?.headerParams}
onMoreSettingChange={setCurrentMoreSettingParam} />
}
{responseBodyList?.length > 0 &&
<MessageBodyComponent
title="响应 Body"
title={$t("响应 Body")}
rows={responseBodyList}
contentType={responseList?.[0]?.contentType}
onMoreSettingChange={setCurrentMoreSettingParam}
@@ -1,10 +1,11 @@
import { $t } from '@common/locales';
import { TextField } from '@mui/material'
import { SyntheticEvent } from 'react'
export function RequestBodyBinary({ value, onChange }: { value: string; onChange: (value: string) => void }) {
return (
<TextField
label="Binary"
label={$t("Binary")}
multiline
rows={4}
value={value}
@@ -10,6 +10,7 @@ import {
import {MessageDataGrid, MessageDataGridApi} from "../MessageDataGrid";
import {Indicator} from "../../../../Indicator";
import { ApiMessageBody, ApiMessageBodyApi } from '../ApiMessageBody';
import { $t } from '@common/locales';
export interface ApiRequestEditorApi {
getData: () => {
@@ -62,7 +63,7 @@ export function ApiRequestEditor({ editorRef ,apiInfo=null,loaded}: { editorRef?
const tabs: ApiRequestEditorTab[] = [
{
label: '请求头部',
label: $t('请求头部'),
element: (
<MessageDataGrid
apiRef={headersRef}
@@ -76,12 +77,12 @@ export function ApiRequestEditor({ editorRef ,apiInfo=null,loaded}: { editorRef?
dirty: false
},
{
label:'请求体',
label:$t('请求体'),
element: <ApiMessageBody bodyApiRef={bodyRef} mode="request" apiInfo={apiInfo} loaded={innerLoaded}/>,
dirty: false
},
{
label: 'Query 参数',
label: $t('Query 参数'),
element: (
<MessageDataGrid
apiRef={queryRef}
@@ -95,7 +96,7 @@ export function ApiRequestEditor({ editorRef ,apiInfo=null,loaded}: { editorRef?
dirty: false
},
{
label: 'REST 参数',
label: $t('REST 参数'),
element: (
<MessageDataGrid
apiRef={restRef}
@@ -123,12 +124,11 @@ export function ApiRequestEditor({ editorRef ,apiInfo=null,loaded}: { editorRef?
return (
<Box sx={{
// borderBottom: 1,
borderColor: 'divider' }}>
<Tabs
value={tabValue}
onChange={handleChange}
aria-label="api request editor"
aria-label={$t("api request editor")}
sx={{
minHeight: tabHeight,
height: tabHeight,
@@ -5,6 +5,7 @@ import {ApiBodyType, BodyParamsType, HeaderParamsType} from "@common/const/api-d
import {Indicator} from "../../../../Indicator";
import { v4 as uuidv4} from 'uuid'
import { ApiMessageBody, ApiMessageBodyApi } from '../ApiMessageBody';
import { $t } from '@common/locales';
interface ApiRequestEditorTab {
label: string
@@ -54,7 +55,7 @@ export function ApiResponseEditor({ editorRef ,apiInfo=null, loaded}: { editorRe
const tabs: ApiRequestEditorTab[] = [
{
label: '返回头部',
label: $t('返回头部'),
element: (
<MessageDataGrid
apiRef={headersRef}
@@ -68,7 +69,7 @@ export function ApiResponseEditor({ editorRef ,apiInfo=null, loaded}: { editorRe
dirty: false
},
{
label: '返回值',
label: $t('返回值'),
element: <ApiMessageBody bodyApiRef={bodyRef} mode="response" apiInfo={apiInfo}
loaded={innerLoaded} />,
dirty: false
@@ -93,7 +94,7 @@ export function ApiResponseEditor({ editorRef ,apiInfo=null, loaded}: { editorRe
<Tabs
value={tabValue}
onChange={handleChange}
aria-label="api request editor"
aria-label={$t("api request editor")}
sx={{
minHeight: tabHeight,
height: tabHeight,
@@ -42,6 +42,7 @@ import {collapseTableSx} from "../../../PreviewTable";
import {IconButton} from "../../../IconButton";
import {Icon} from "../../../Icon";
import {useMoreSettingHiddenConfig} from "./hooks/useMoreSettingHiddenConfig.ts";
import { $t } from '@common/locales/index.ts'
export interface RenderMessageBody extends BodyParamsType {
path?: string[]
@@ -251,14 +252,14 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
const getActions = useCallback(
(params: GridRowParams<RenderMessageBody>) => {
const actions = [
<IconButton title="更多设置" name="more" onClick={() => handleOpenMoreSetting(params)} />
<IconButton title={$t("更多设置")} name="more" onClick={() => handleOpenMoreSetting(params)} />
]
const isXML = contentType === 'XML'
const isRoot = params.row.__globalIndex__ === 0
if (['JSON', 'XML'].includes(contentType)) {
actions.unshift(
<IconButton
title="添加子参数"
title={$t("添加子参数")}
name="add"
onClick={() => {
const newRow = EmptyRow()
@@ -288,7 +289,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
if (!(isXML && isRoot)) {
actions.unshift(
<IconButton
title="向下添加行"
title={$t("向下添加行")}
name="down-small"
onClick={() => {
const newRow = EmptyRow()
@@ -301,7 +302,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
}
}
if (renderRows.length > 1) {
actions.push(<IconButton title="删除" name="delete" onClick={() => handleRowDelete(params)} />)
actions.push(<IconButton title={$t("删除")} name="delete" onClick={() => handleRowDelete(params)} />)
}
return actions
},
@@ -312,7 +313,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
const columns: (GridColDef<RenderMessageBody> | false)[] = [
messageType === 'Header' && {
field: 'name',
headerName: '标签',
headerName: $t('标签'),
editable: true,
sortable:false,
renderEditCell: (params) => {
@@ -340,7 +341,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
},
messageType !== 'Header' && {
field: 'name',
headerName: '参数名',
headerName: $t('参数名'),
width: 200,
editable: true,
sortable: false,
@@ -373,7 +374,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
<TextField
fullWidth
value={params.value}
placeholder='参数名'
placeholder={$t('参数名')}
onChange={(e) => {
const newValue = e.target.value as string
const rowIndex = params.row.__globalIndex__
@@ -395,7 +396,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
},
messageType === 'Body' && {
field: 'dataType',
headerName: '类型',
headerName: $t('类型'),
sortable: false,
width: 120,
type: 'singleSelect',
@@ -429,7 +430,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
},
{
field: 'isRequired',
headerName: '必需',
headerName: $t('必需'),
headerAlign: 'left',
sortable: false,
type: 'boolean',
@@ -443,7 +444,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
indeterminate={selectAll === 'indeterminate'}
onChange={handleSelectAllChange}
/>
<Typography sx={{fontSize:'14px'}}></Typography>
<Typography sx={{fontSize:'14px'}}>{$t('必需')}</Typography>
</Box>
)
},
@@ -463,7 +464,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
},
{
field: 'description',
headerName: '描述',
headerName: $t('描述'),
sortable: false,
flex: 1,
minWidth: 200,
@@ -484,7 +485,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
paddingRight: theme.spacing(1)
}
}}
placeholder='描述'
placeholder={$t('描述')}
/>
)
}
@@ -492,7 +493,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
{
field: 'paramAttr',
sortable: false,
headerName: '示例',
headerName: $t('示例'),
flex: 1,
minWidth: 200,
editable: true,
@@ -515,8 +516,7 @@ export function MessageDataGrid(props: MessageDataGridProps<RenderMessageBody>)
paddingRight: theme.spacing(1)
}
}}
placeholder='示例'
/>
placeholder={$t('示例')} />
)
}
},
@@ -3,6 +3,7 @@ import { SyntheticEvent } from 'react'
import {ParseCurlResult} from "@common/const/api-detail";
import {HTTPMethod, RequestMethod} from "../../../RequestMethod";
import {ParseCurl} from "@common/utils/curl.ts";
import { $t } from '@common/locales';
interface UriInputProps {
inputValue?: string
@@ -51,7 +52,7 @@ export function UriInput({
return (
<TextField
fullWidth
placeholder="输入 URL 或 cURL"
placeholder={$t("输入 URL 或 cURL")}
value={inputValue}
onChange={handleInputChange}
sx={{
@@ -2,6 +2,7 @@
import { Box, Chip, Stack, Typography, Skeleton } from '@mui/material'
import {HTTPMethod, Protocol,RequestMethod} from "../../../RequestMethod";
import {Clipboard} from "../../../Clipboard"
import { $t } from '@common/locales';
interface ApiBasicInfoDisplayProps {
apiName: string
@@ -38,7 +39,7 @@ export default function ApiBasicInfoDisplay(props: Partial<ApiBasicInfoDisplayPr
<Box display="flex">
<Stack direction="row" spacing={1} alignItems="center">
<Chip
label="HTTP"
label={$t("HTTP")}
sx={{
height:'22px',
borderRadius: '4px',
@@ -6,6 +6,7 @@ import {collapseTableSx, previewTableHoverSx} from "../../../PreviewTable";
import {Collapse} from "../../../Collapse";
import { MatchPositionEnum, MatchTypeEnum } from "@core/const/system/const";
import { MatchItem } from "@common/const/type";
import { $t } from "@common/locales";
interface ApiMatchProps {
rows?: MatchItem[]
@@ -29,25 +30,25 @@ export default function ApiMatch({ rows = [], title, loading = false }: ApiMatch
const columns: GridColDef<MatchItem>[] = [
{
field: 'key',
headerName: '参数名',
headerName: $t('参数名'),
hideable: false,
width:200
},
{
field: 'position',
headerName: '参数位置',
headerName: $t('参数位置'),
valueGetter: (params) => MatchPositionEnum[params.row.position],
width:160
},
{
field: 'matchType',
headerName: '匹配类型',
headerName: $t('匹配类型'),
valueGetter: (params) => MatchTypeEnum[params.row.matchType],
width:160
},
{
field: 'pattern',
headerName: '参数值',
headerName: $t('参数值'),
flex:1
}
]
@@ -6,6 +6,7 @@ import { SystemApiProxyType, ProxyHeaderItem } from "@core/const/system/type"
import { previewTableHoverSx, collapseTableSx } from "../../../PreviewTable"
import { RenderMessageBody } from "../MessageBody"
import { Collapse } from "../../../Collapse"
import { $t } from "@common/locales"
interface HeaderFieldsProps {
proxyInfo:SystemApiProxyType
@@ -30,19 +31,19 @@ export default function ApiProxy({ proxyInfo, title, loading = false, onMoreSett
const columns: GridColDef<ProxyHeaderItem>[] = [
{
field: 'key',
headerName: '参数名',
headerName: $t('参数名'),
width: 200,
hideable: false
},
{
field: 'optType',
headerName: '操作类型',
valueGetter: (params) => params.row.optType === 'ADD'?'新增或修改':'删除',
headerName: $t('操作类型'),
valueGetter: (params) => params.row.optType === 'ADD'?$t('新增或修改'):$t('删除'),
width: 200
},
{
field: 'value',
headerName: '匹配参数值',
headerName: $t('匹配参数值'),
flex: 1
},
]
@@ -51,31 +52,31 @@ export default function ApiProxy({ proxyInfo, title, loading = false, onMoreSett
return [
{
key: 'path',
label: '转发上游路径',
label: $t('转发上游路径'),
children: proxyInfo?.path,
style: {paddingBottom: '10px'},
},
{
key: 'timeout',
label: '请求超时时间',
label: $t('请求超时时间'),
children: proxyInfo?.timeout,
style: {paddingBottom: '10px'},
},
// {
// key: 'upstream',
// label: '绑定上游服务',
// label: $t('绑定上游服务',
// children: proxyInfo?.upstream.name,
// style: {paddingBottom: '10px'},
// },
{
key: 'retry',
label: '重试时间',
label: $t('重试时间'),
children: proxyInfo?.retry,
style: {paddingBottom: '10px'},
},
...(proxyInfo.headers.length > 0 ? [{
key: 'headers',
label: '转发上游请求头',
label: $t('转发上游请求头'),
children: '',
style: {paddingBottom: '10px'},
}]:[])
@@ -5,6 +5,7 @@ import { RenderMessageBody } from '../MessageBody'
import {HeaderParamsType} from "@common/const/api-detail";
import {collapseTableSx, PreviewGridActionsCellItem, previewTableHoverSx} from "../../../PreviewTable";
import {Collapse} from "../../../Collapse";
import { $t } from "@common/locales";
interface HeaderFieldsProps {
rows?: HeaderParamsType[]
@@ -29,13 +30,13 @@ export default function HeaderFields({ rows = [], title, loading = false, onMore
const columns: GridColDef<HeaderParamsType>[] = [
{
field: 'name',
headerName: '标签',
headerName: $t('标签'),
width: 200,
hideable: false
},
{
field: 'isRequired',
headerName: '必需',
headerName: $t('必需'),
sortable: false,
valueGetter: (params) => Boolean(params.row.isRequired),
type: 'boolean',
@@ -43,7 +44,7 @@ export default function HeaderFields({ rows = [], title, loading = false, onMore
},
{
field: 'description',
headerName: '描述',
headerName: $t('描述'),
flex: 1
},
{
@@ -57,7 +58,7 @@ export default function HeaderFields({ rows = [], title, loading = false, onMore
getActions: (params) => [
<PreviewGridActionsCellItem
icon="more"
label="More"
label={$t("More")}
key="more"
onClick={() => onMoreSettingChange?.(params.row as unknown as RenderMessageBody)}
/>
@@ -3,7 +3,7 @@ import { TextField } from '@mui/material'
export function PreviewBodyBinary({ value }: { value: string;}) {
return (
<TextField
label="Binary"
label={$t("Binary")}
multiline
disabled={true}
rows={4}
@@ -6,6 +6,7 @@ import {collapseTableSx, PreviewGridActionsCellItem, previewTableHoverSx} from "
import {Collapse} from "../../../Collapse";
import { PreviewBodyBinary } from './components/Binary';
import { PreviewBodyRaw } from './components/Raw';
import { $t } from '@common/locales';
export interface RenderMessageBody extends BodyParamsType {
path: string[]
@@ -50,13 +51,13 @@ export default function MessageBodyComponent({
const columns: GridColDef<RenderMessageBody>[] = [
{
field: 'dataType',
headerName: '类型',
headerName: $t('类型'),
valueGetter: (params) => ApiParamsType[params.row.dataType as ApiParamsType],
width: 80
},
{
field: 'isRequired',
headerName: '必需',
headerName: $t('必需'),
sortable: false,
valueGetter: (params) => Boolean(params.row.isRequired),
type: 'boolean',
@@ -64,12 +65,12 @@ export default function MessageBodyComponent({
},
{
field: 'description',
headerName: '描述',
headerName: $t('描述'),
flex: 1
},
{
field: 'paramAttr',
headerName: '示例',
headerName:$t('示例'),
valueGetter: (params) => params.row.paramAttr?.example,
flex: 1
},
@@ -83,7 +84,7 @@ export default function MessageBodyComponent({
getActions: (params) => [
<PreviewGridActionsCellItem
icon="more"
label="More"
label={$t("More")}
key="more"
onClick={() => onMoreSettingChange?.(params.row)}
/>
@@ -105,7 +106,7 @@ export default function MessageBodyComponent({
rowHeight={40}
pagination={false}
groupingColDef={{
headerName: '参数名',
headerName: $t('参数名'),
sortable:true,
width: 200,
hideable: false,
@@ -6,6 +6,7 @@ import { RenderMessageBody } from '../MessageBody'
import {QueryParamsType} from "@common/const/api-detail";
import {collapseTableSx, PreviewGridActionsCellItem, previewTableHoverSx} from "../../../PreviewTable";
import {Collapse} from "../../../Collapse";
import { $t } from '@common/locales';
interface QueryFieldsProps {
rows?: QueryParamsType[]
@@ -30,13 +31,13 @@ export default function QueryFields({ rows = [], title, loading = false, onMoreS
const columns: GridColDef<QueryParamsType>[] = [
{
field: 'name',
headerName: '参数名',
headerName: $t('参数名'),
width: 200,
hideable: false
},
{
field: 'isRequired',
headerName: '必需',
headerName: $t('必需'),
sortable: false,
valueGetter: (params) => Boolean(params.row.isRequired),
type: 'boolean',
@@ -44,7 +45,7 @@ export default function QueryFields({ rows = [], title, loading = false, onMoreS
},
{
field: 'description',
headerName: '描述',
headerName: $t('描述'),
flex: 1
},
{
@@ -58,7 +59,7 @@ export default function QueryFields({ rows = [], title, loading = false, onMoreS
getActions: (params) => [
<PreviewGridActionsCellItem
icon="more"
label="More"
label={$t("More")}
key="more"
onClick={() => onMoreSettingChange?.(params.row as unknown as RenderMessageBody)}
/>
@@ -4,6 +4,7 @@ import { IconButton } from '../../../../IconButton'
import {BaseDialog} from "../../../../Dialog";
import {Icon} from "../../../../Icon";
import {Codebox} from "../../../../Codebox";
import { $t } from '@common/locales';
export type ImportMessageChangeType = 'replace-all' | 'insert-end' | 'replace-changed'
@@ -65,7 +66,7 @@ export function ImportMessage({ type, onChange }: ImportMessageDialogProps) {
return (
<>
<IconButton name="import" sx={{ height: '30px' }} onClick={() => setOpen(true)} variant="outlined">
{$t('导入')}
</IconButton>
<BaseDialog open={open} onClose={() => setOpen(false)} actionRender={null} title={`Import ${type}`}>
<DialogContent sx={{ paddingTop: 0, minWidth: '800px' }}>
@@ -74,7 +75,7 @@ export function ImportMessage({ type, onChange }: ImportMessageDialogProps) {
<Paper elevation={0} sx={{ padding: 2, bgcolor: theme.palette.grey[200] }}>
<Typography component="div" sx={{ display: 'flex', alignItems: 'center' }}>
<Icon name="attention" mx={0} px={0} sx={{ display: 'inline-flex', marginRight: 0.5 }} />
{$t('导入格式')}
</Typography>
<Typography variant="body2" component="div">
<pre>{example}</pre>
@@ -86,16 +87,16 @@ export function ImportMessage({ type, onChange }: ImportMessageDialogProps) {
</DialogContent>
<Box display="flex" p={3} pt={0} gap={2} justifyContent="flex-end">
<Button variant="outlined" onClick={() => setOpen(false)}>
{$t('取消')}
</Button>
<Button variant="outlined" onClick={() => handleChange('replace-all')}>
{$t('全量替换')}
</Button>
<Button variant="outlined" onClick={() => handleChange('insert-end')}>
{$t('在末端插入')}
</Button>
<Button variant="contained" onClick={() => handleChange('replace-changed')}>
{$t('增量更新')}
</Button>
</Box>
</BaseDialog>
@@ -9,23 +9,23 @@ export const MimeTypes: {
value: ContentType
}[] = [
{
title: 'Text',
title:'Text',
value: 'text/plain'
},
{
title: 'JSON',
title:'JSON',
value: 'application/json'
},
{
title: 'XML',
title:'XML',
value: 'application/xml'
},
{
title: 'HTML',
title:'HTML',
value: 'text/html'
},
{
title: 'JavaScript',
title:'JavaScript',
value: 'application/javascript'
}
]
@@ -35,11 +35,11 @@ export const FormContentTypes: {
value: ContentType
}[] = [
{
title: 'x-www-form-urlencoded',
title:'x-www-form-urlencoded',
value: 'application/x-www-form-urlencoded'
},
{
title: 'multipart/form-data',
title:'multipart/form-data',
value: 'multipart/form-data'
}
]
@@ -17,6 +17,7 @@ import { ImportMessage, ImportMessageChangeType, ImportMessageOption } from './I
import {ApiBodyType, ApiDetail, ParseCurlResult, TestApiBodyType} from "@common/const/api-detail";
import {TestMessageDataGrid, TestMessageDataGridApi} from "../TestMessageDataGrid";
import {Indicator} from "../../../../Indicator";
import { $t } from '@common/locales'
export interface ApiRequestTesterApi {
getEditMeta: () => {
@@ -113,13 +114,13 @@ export function ApiRequestTester({ apiRef, onQueryChange ,apiInfo, loaded=true}:
)
const handleImportChange = (changeType: ImportMessageChangeType, data: ImportMessageOption[]) => {
tabValue === '请求头' && headersApiRef.current?.importData(changeType, data)
tabValue === 'Query 参数' && queryApiRef.current?.importData(changeType, data)
tabValue === $t('请求头') && headersApiRef.current?.importData(changeType, data)
tabValue === $t('Query 参数') && queryApiRef.current?.importData(changeType, data)
}
const tabs = [
{
label: '请求头',
label: $t('请求头'),
element: (
<TestMessageDataGrid
apiRef={headersApiRef}
@@ -131,12 +132,12 @@ export function ApiRequestTester({ apiRef, onQueryChange ,apiInfo, loaded=true}:
dirty: false
},
{
label: '请求体',
label: $t('请求体'),
element: <TestBody bodyApiRef={bodyApiRef} onContentTypeChange={handleContentTypeChange} />,
dirty: false
},
{
label: 'Query 参数',
label: $t('Query 参数'),
element: (
<TestMessageDataGrid
apiRef={queryApiRef}
@@ -150,7 +151,7 @@ export function ApiRequestTester({ apiRef, onQueryChange ,apiInfo, loaded=true}:
dirty: false
},
{
label: 'Rest 参数',
label: $t('Rest 参数'),
element: <TestMessageDataGrid apiRef={restApiRef} initialRows={apiRest} messageType="REST" />,
dirty: false
}
@@ -1,6 +1,7 @@
import { Box, Chip, Typography } from '@mui/material'
import {IconButton} from "../../../../IconButton";
import {byteToString} from "@common/utils/postcat.tsx";
import { $t } from '@common/locales';
interface ResponseIndicatorProps {
statusCode: number
@@ -14,12 +15,12 @@ export function ResponseIndicator({ statusCode, size, time, onDownload }: Partia
<Box gap={2} px={1} display="flex" alignItems="center">
<Chip label={<Typography>{statusCode}</Typography>} />
<Typography>
: {byteToString(size || 0)}
{$t('大小')}: {byteToString(size || 0)}
</Typography>
<Typography>
: {time} ms
{$t('时间')}: {time} ms
</Typography>
<IconButton name="download" title='另存为文件' onClick={onDownload} />
<IconButton name="download" title={$t('另存为文件')} onClick={onDownload} />
</Box>
) : null
}
@@ -6,6 +6,7 @@ import { HeaderPreview } from './components/HeaderPreview'
import { ResponseIndicator } from './components/ResponseIndicator'
import {TestResponse} from "@common/hooks/useTest.ts";
import {downloadFile} from "@common/utils/download.ts";
import { $t } from '@common/locales'
type TabType = 'Response' | 'Response Headers' | 'Body' | 'Request Headers'
@@ -16,12 +17,12 @@ interface ApiResponseProps {
export function ApiResponse({ data }: ApiResponseProps) {
const tabHeight = 30
const theme = useTheme()
const [tabValue, setTabValue] = useState<TabType>('Response')
const handleTabValueChange = (_evt: SyntheticEvent, value: TabType): void => {
const handleTabValueChange = useCallback((_evt: SyntheticEvent, value: TabType): void => {
setTabValue(value)
}
},[])
const response = data?.report.response
const handleDownload = useCallback(() => {
@@ -42,7 +43,7 @@ export function ApiResponse({ data }: ApiResponseProps) {
const response = data?.report.response
return [
{
title: '响应',
title: $t('响应'),
name: 'Response',
hidden: false,
element: (
@@ -57,19 +58,19 @@ export function ApiResponse({ data }: ApiResponseProps) {
)
},
{
title: '响应头',
title: $t('响应头'),
name: 'Response Headers',
hidden: !response?.headers.length,
element: <HeaderPreview data={response?.headers || []} />
},
{
title:'正文',
title:$t('正文'),
name: 'Body',
hidden: !request?.body.length,
element: <Body data={request?.body} />
},
{
title: '请求头',
title: $t('请求头'),
name: 'Request Headers',
hidden: !request?.headers.length,
element: <HeaderPreview data={request?.headers || []} />
@@ -1,4 +1,5 @@
// import { TabRouteObject, useTabStore } from '@/stores/tab'
import { $t } from '@common/locales'
import { Button, Typography } from '@mui/material'
import { TouchRippleActions } from '@mui/material/ButtonBase/TouchRipple'
import { useEffect, useRef, useState } from 'react'
@@ -66,15 +67,15 @@ export function TestControl({ onTest, onAbort, loading }: TestControlProps) {
<>
{!loading ? (
<Button touchRippleRef={testRippleRef} variant="contained" onClick={onTest}>
(Enter)
{$t('发送(Enter)')}
</Button>
) : (
<Button variant="outlined" color="warning" onClick={onAbort}>
<Typography></Typography>
<Typography>{$t('中止')}</Typography>
{loadingTime ? (
<Typography sx={{ paddingLeft: 1 }}>
{' '}
({loadingTime} {'秒'}){' '}
({loadingTime} {$t('秒')}){' '}
</Typography>
) : null}
</Button>
@@ -27,6 +27,7 @@ import {Icon} from "../../../Icon";
import {ApiParamsTypeOptions} from "../../../ApiManager/components/ApiMessageBody/constants.ts";
import {UploadButton} from "../../../UploadButton";
import {isNil} from "lodash-es";
import { $t } from '@common/locales/index.ts';
type SafeAny = unknown
export interface RenderBodyParamsType extends BodyParamsType {
@@ -171,7 +172,7 @@ export function TestMessageDataGrid(props: TestMessageDataGridProps<BodyParamsTy
const columns: (GridColDef<RenderBodyParamsType> | false)[] = [
messageType === 'Headers' && {
field: 'name',
headerName: '标签',
headerName: $t('标签'),
editable: true,
sortable: false,
renderEditCell: (params) => {
@@ -214,7 +215,7 @@ export function TestMessageDataGrid(props: TestMessageDataGridProps<BodyParamsTy
},
messageType !== 'Headers' && {
field: 'name',
headerName:'参数名',
headerName:$t('参数名'),
width: 200,
editable: true,
sortable: false,
@@ -244,7 +245,7 @@ export function TestMessageDataGrid(props: TestMessageDataGridProps<BodyParamsTy
},
messageType === 'Body' && {
field: 'dataType',
headerName: '类型',
headerName: $t('类型'),
sortable: false,
width: 120,
type: 'singleSelect',
@@ -286,7 +287,7 @@ export function TestMessageDataGrid(props: TestMessageDataGridProps<BodyParamsTy
{
field: 'paramAttr',
sortable: false,
headerName: '参数值',
headerName: $t('参数值'),
flex: 1,
minWidth: 200,
editable: true,
@@ -4,6 +4,7 @@ import type { ReactNode } from 'react'
import { Box } from '@mui/material'
import { IconButton } from '../IconButton'
import useCopyToClipboard from "@common/hooks/copy.ts";
import { $t } from '@common/locales';
export interface ClipboardProps {
text: string
@@ -14,7 +15,7 @@ export interface ClipboardProps {
export function Clipboard(props: ClipboardProps): JSX.Element {
const { text, children, onError, onSuccess } = props
const DefaultText = '复制'
const DefaultText = $t('复制')
const [buttonTitle, setButtonTitle] = useState(DefaultText)
const { copyToClipboard } = useCopyToClipboard();
const handleCopy = (): void => {
@@ -5,6 +5,8 @@ import { Editor, useMonaco } from '@monaco-editor/react'
import { type editor as MonacoEditor } from 'monaco-editor'
import { IconButton } from '../IconButton'
import { message } from 'antd'
import { $t } from '@common/locales'
import { RESPONSE_TIPS } from '@common/const/const'
export interface CodeboxApiRef {
insertCode: (value: string) => void
@@ -126,7 +128,7 @@ export const Codebox = memo((props: CodeboxProps) => {
const copyCode = async (): Promise<void> => {
if (editorRef.current) {
await navigator.clipboard.writeText(editorRef.current.getValue())
message.success('复制成功')
message.success(RESPONSE_TIPS.copySuccess)
}
}
@@ -166,16 +168,16 @@ export const Codebox = memo((props: CodeboxProps) => {
{extraContent}
<IconButton name="code" onClick={formatCode} sx={{color:'#333',transition:'none','&.MuiButtonBase-root:hover':{background:'transparent',color:'#3D46F2',transition:'none'}}}>
{$t('格式化')}
</IconButton>
<IconButton name="copy" onClick={copyCode} sx={{color:'#333',transition:'none','&.MuiButtonBase-root:hover':{background:'transparent',color:'#3D46F2',transition:'none'}}}>
{$t('复制')}
</IconButton>
<IconButton name="search" onClick={searchInCode} sx={{color:'#333',transition:'none','&.MuiButtonBase-root:hover':{background:'transparent',color:'#3D46F2',transition:'none'}}}>
{$t('搜索')}
</IconButton>
{!readOnly &&<IconButton name="file-text" onClick={replaceInCode} sx={{color:'#333',transition:'none','&.MuiButtonBase-root:hover':{background:'transparent',color:'#3D46F2',transition:'none'}}}>
{$t('替代')}
</IconButton>}
</Box></>
) : null}
@@ -7,6 +7,7 @@ import { type ReactNode } from 'react'
import type { LoadingButtonProps } from '@mui/lab'
import { LoadingButton } from '@mui/lab'
import {renderComponent} from "@common/utils/postcat.tsx";
import { $t } from '@common/locales'
export interface BaseDialogProps extends DialogActionProps {
open: boolean
@@ -56,9 +57,8 @@ interface DialogActionProps {
export function DialogActions(props: DialogActionProps): JSX.Element {
const CancelText = '取消'
const ConfirmText = '确定'
const CancelText = $t('取消')
const ConfirmText = $t('确定')
const {
onClose,
confirmBtn,
@@ -1,5 +1,6 @@
import { Box, Typography, useTheme } from '@mui/material'
import {Codebox} from "../../Codebox";
import { $t } from '@common/locales';
interface ExampleProps {
code: string
@@ -29,7 +30,7 @@ export function Example({ code, onChange, readOnly = false }: ExampleProps) {
}}
>
<Typography fontSize={14} px={'12px'}>
{$t('示例')}
</Typography>
</Box>
<Codebox
@@ -1,6 +1,7 @@
import { ChangeEvent, useEffect, useState } from 'react'
import { FormControl, TextField, Box } from '@mui/material'
import { $t } from '@common/locales';
interface ParamLimitProps {
min: number | null
@@ -17,15 +18,15 @@ export function ParamLimit({ min, max, onChange, minLabel = 'Minimum', maxLabel
const validate = (minVal: number, maxVal: number) => {
if (isNaN(minVal) || minVal < 0) {
return `The ${minLabel} must not be negative.`
return $t('The (0) must not be negative.', [minLabel])
}
if (isNaN(maxVal) || maxVal < 0) {
return `The ${maxLabel} must not be negative.`
return $t('The (0) must not be negative.',[maxLabel])
}
if (minVal > maxVal) {
return `The ${maxLabel} must be greater than or equal to the ${minLabel}.`
return $t('The (0) must be greater than or equal to the (1).',[maxLabel, minLabel])
}
return null
@@ -2,6 +2,7 @@ import { Box } from '@mui/material'
import { DataGridPro, GridColDef, useGridApiRef } from '@mui/x-data-grid-pro'
import { useEffect, useMemo } from 'react'
import {previewTableHoverSx } from '../../PreviewTable'
import { $t } from '@common/locales'
interface ParamPreviewProps {
name?: string
@@ -48,17 +49,17 @@ export function ParamPreview(props: ParamPreviewProps) {
const columns: GridColDef[] = [
{
field: 'name',
headerName: '参数名',
headerName: $t('参数名'),
width: 120
},
{
field: 'type',
headerName: '类型',
headerName: $t('类型'),
width: 120
},
{
field: 'required',
headerName: '必需',
headerName: $t('必需'),
sortable: false,
valueGetter: (params) => Boolean(params.row.isRequired),
type: 'boolean',
@@ -66,7 +67,7 @@ export function ParamPreview(props: ParamPreviewProps) {
},
{
field: 'description',
headerName: '描述',
headerName: $t('描述'),
flex: 1
}
]
@@ -13,6 +13,7 @@ import {IconButton} from "../../IconButton";
import {flattenTree, generateId, getActionColWidth} from "@common/utils/postcat.tsx";
import {EditableDataGridSx} from "../../ApiManager/components/EditableDataGrid";
import { commonTableSx } from '@common/const/api-detail/index.ts';
import { $t } from '@common/locales';
export interface ValueEnum {
value: string
@@ -97,7 +98,7 @@ export function ValueEnum({ data, apiRef,readOnly = false }: ValueEnumProps) {
const columns: GridColDef<Row>[] = [
{
field: 'value',
headerName: '值枚举',
headerName: $t('值枚举'),
type: 'string',
sortable: false,
flex: 1,
@@ -109,7 +110,7 @@ export function ValueEnum({ data, apiRef,readOnly = false }: ValueEnumProps) {
<TextField
fullWidth
value={params.value}
placeholder='枚举'
placeholder={$t('枚举')}
sx={{
input: {
paddingLeft: `${theme.spacing(1)} !important`,
@@ -132,7 +133,7 @@ export function ValueEnum({ data, apiRef,readOnly = false }: ValueEnumProps) {
},
{
field: 'description',
headerName: '描述',
headerName: $t('描述'),
type: 'string',
sortable: false,
flex: 1,
@@ -154,7 +155,7 @@ export function ValueEnum({ data, apiRef,readOnly = false }: ValueEnumProps) {
paddingRight: `${theme.spacing(1)} !important`
}
}}
placeholder='示例'
placeholder={$t('示例')}
/>
)
}
@@ -170,7 +171,7 @@ export function ValueEnum({ data, apiRef,readOnly = false }: ValueEnumProps) {
if (renderRows.length <= 1) return []
return [
<IconButton
title='删除'
title={$t('删除')}
name="delete"
onClick={() => {
handleRowDelete(params)
@@ -9,6 +9,7 @@ import { ParamLimit } from './components/ParamLimit'
import {ParamAttrType} from "@common/const/api-detail";
import { BaseDialog} from "../Dialog/base-dialog.tsx";
import {ApiParamsTypeOptions} from "../ApiManager/components/ApiMessageBody/constants.ts";
import { $t } from '@common/locales/index.ts'
interface MoreSettingProps {
open: boolean
@@ -77,7 +78,7 @@ export function MoreSetting({ open, readOnly,onClose, param, onChange, hiddenCon
}
return (
<BaseDialog open={open} onClose={onClose} title='更多设置' onConfirm={handleConfirm}>
<BaseDialog open={open} onClose={onClose} title={$t('更多设置')} onConfirm={handleConfirm}>
<Box px={2} pb={2} width={880}>
<Stack spacing={2}>
<ParamPreview
@@ -90,8 +91,8 @@ export function MoreSetting({ open, readOnly,onClose, param, onChange, hiddenCon
<ParamLimit
min={param?.paramAttr?.minLength ?? 0}
max={param?.paramAttr?.maxLength ?? 0}
minLabel='最小长度'
maxLabel='最大长度'
minLabel={$t('最小长度')}
maxLabel={$t('最大长度')}
onChange={handleParamLengthChange}
/>
) : null}
@@ -99,8 +100,8 @@ export function MoreSetting({ open, readOnly,onClose, param, onChange, hiddenCon
<ParamLimit
min={param?.paramAttr?.minValue ?? 0}
max={param?.paramAttr?.maxValue ?? 0}
minLabel='最小值'
maxLabel='最大值'
minLabel={$t('最小值')}
maxLabel={$t('最大值')}
onChange={handleParamValueChange}
/>
) : null}
@@ -4,6 +4,7 @@ import { useDropzone } from 'react-dropzone'
import { useAutoAnimate } from '@formkit/auto-animate/react'
import { Icon } from '../Icon'
import { IconButton } from '../IconButton'
import { $t } from '@common/locales'
export interface UploadProps {
value?: File | null
@@ -48,7 +49,7 @@ export function Upload({ value, onChange }: UploadProps): JSX.Element {
<Box>
<Icon size="30px" name="link-cloud" />
</Box>
<Typography>{'将文件拖拽至此处上传,或点击选择文件上传'}</Typography>
<Typography>{$t('将文件拖拽至此处上传,或点击选择文件上传')}</Typography>
</Paper>
{value ? (
<Box>
@@ -2,6 +2,7 @@ import { Box, Button, Typography } from '@mui/material'
import type { ChangeEvent } from 'react'
import { useRef } from 'react'
import {file2Base64} from "@common/utils/postcat.tsx";
import { $t } from '@common/locales';
interface UploadButtonProps {
value?:
@@ -42,9 +43,9 @@ export function UploadButton({ value, onChange }: UploadButtonProps): JSX.Elemen
<Box display="flex" alignItems="center">
<input multiple type="file" ref={fileInputRef} style={{ display: 'none' }} onChange={handleFileChange} />
<Button variant="outlined" color="primary" onClick={handleButtonClick}>
Upload Files
{$t('Upload Files')}
</Button>
{value?.length ? <Typography sx={{ marginLeft: 1 }}>Files Selected: {value.length}</Typography> : null}
{value?.length ? <Typography sx={{ marginLeft: 1 }}>{$t('Files Selected')}: {value.length}</Typography> : null}
</Box>
)
}
@@ -9,34 +9,6 @@ export interface MenuItem {
content?: unknown
}
export const GetMenuItem = (org_domain_id: string, project_domain_id: string, commonQuestionRes: unknown) => {
const Menus = [
{ key: 'introduction', name: '介绍', emoji: '📃', path: `/${org_domain_id}/api/${project_domain_id}/introduction` },
{ key: 'apiDocument', name: 'API 文档', emoji: '🔗', path: `/${org_domain_id}/api/${project_domain_id}/apiDocument` },
{ key: 'price', name: '价格套餐', emoji: '💎', path: `/${org_domain_id}/api/${project_domain_id}/price` },
{ key: 'guidence', name: '接入指南', emoji: '💡', path: `/${org_domain_id}/api/${project_domain_id}/guidence` }
];
if (commonQuestionRes?.success && commonQuestionRes?.data?.content) {
Menus.splice(2, 0, { key: 'commonQuestion', name: '常见问题', emoji: '🌷', path: `/${org_domain_id}/api/${project_domain_id}/commonQuestion` });
}
return Menus;
};
export const SKU_LIST = [
{
name: '流量包',
key: 'flow'
},
{
name: '订阅套餐',
key: 'subscribe'
}
]
export const PROMISE_TEXT = ['服务保障', '未使用部分七天无理由退款', '正规企业商品来源', '交易流程全程监控']
export const DATA_TYPE = {
JSON: '[json]',
@@ -1,13 +1,14 @@
import {ProColumns} from "@ant-design/pro-components";
import { ApprovalTableListItem, PublishTableListItem } from "./type";
import { Tooltip } from "antd";
import { $t } from "@common/locales";
import { PageProColumns } from "@common/components/aoplatform/PageList";
export const TODO_LIST_COLUMN_NOT_INCLUDE_KEY:string[] = ['status','approver','approvalTime']
export const SUBSCRIBE_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>[] = [
export const SUBSCRIBE_APPROVAL_TABLE_COLUMN : PageProColumns<ApprovalTableListItem>[] = [
{
title: '申请时间',
title:$t('申请时间'),
dataIndex: 'applyTime',
ellipsis:true,
width:182,
@@ -17,44 +18,44 @@ export const SUBSCRIBE_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>
},
},
{
title: '申请方-应用',
title:$t('申请方-应用'),
dataIndex: ['application','name'],
ellipsis:true
},
{
title: '申请服务',
title:$t('申请服务'),
dataIndex: ['service','name'],
ellipsis:true
},
{
title: '服务所属系统',
title:$t('服务所属系统'),
dataIndex: ['service','name'],
ellipsis:true
},
{
title: '服务所属团队',
title:$t('服务所属团队'),
dataIndex: ['team','name'],
ellipsis:true
},
{
title: '审批状态',
title:$t('审批状态'),
dataIndex: 'status',
valueType: 'text',
},
{
title: '申请人',
title:$t('申请人'),
dataIndex: ['applier','name'],
ellipsis: true,
width:88,
},
{
title: '审批人',
title:$t('审批人'),
dataIndex: ['approver','name'],
ellipsis: true,
width:88
},
{
title: '审批时间',
title:$t('审批时间'),
dataIndex: 'approvalTime',
ellipsis: true,
// sorter: true,,
@@ -65,9 +66,9 @@ export const SUBSCRIBE_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>
},
];
export const SUBSCRIBE_APPROVAL_INNER_TODO_TABLE_COLUMN : ProColumns<SubscribeApprovalTableListItem>[] = [
export const SUBSCRIBE_APPROVAL_INNER_TODO_TABLE_COLUMN : PageProColumns<SubscribeApprovalTableListItem>[] = [
{
title: '申请时间',
title:$t('申请时间'),
dataIndex: 'applyTime',
// sorter: true,
ellipsis:true,
@@ -78,13 +79,13 @@ export const SUBSCRIBE_APPROVAL_INNER_TODO_TABLE_COLUMN : ProColumns<SubscribeAp
},
},
{
title: '申请方-应用',
title:$t('申请方-应用'),
dataIndex: ['application','name'],
ellipsis:true
},
{
// title: '申请人',
title: <Tooltip title="申请人" ></Tooltip>,
// title:$t('申请人',
title: <Tooltip title={$t("申请人")} >{$t('申请人')}</Tooltip>,
dataIndex: ['applier','name'],
ellipsis: true,
filters: true,
@@ -93,16 +94,16 @@ export const SUBSCRIBE_APPROVAL_INNER_TODO_TABLE_COLUMN : ProColumns<SubscribeAp
filterSearch: true
},
{
title: '申请服务',
title:$t('申请服务'),
dataIndex: ['service','name'],
ellipsis:true
},
];
export const SUBSCRIBE_APPROVAL_INNER_DONE_TABLE_COLUMN : ProColumns<SubscribeApprovalTableListItem>[] = [
export const SUBSCRIBE_APPROVAL_INNER_DONE_TABLE_COLUMN : PageProColumns<SubscribeApprovalTableListItem>[] = [
{
title: '申请时间',
title:$t('申请时间'),
dataIndex: 'applyTime',
// sorter: true,
ellipsis:true,
@@ -113,13 +114,13 @@ export const SUBSCRIBE_APPROVAL_INNER_DONE_TABLE_COLUMN : ProColumns<SubscribeAp
},
},
{
title: '申请方-应用',
title:$t('申请方-应用'),
dataIndex: ['application','name'],
ellipsis:true
},
{
// title: '申请人',
title: <Tooltip title="申请人" ></Tooltip>,
// title:$t('申请人',
title: <Tooltip title={$t("申请人")} >{$t('申请人')}</Tooltip>,
dataIndex: ['applier','name'],
ellipsis: true,
filters: true,
@@ -128,24 +129,24 @@ export const SUBSCRIBE_APPROVAL_INNER_DONE_TABLE_COLUMN : ProColumns<SubscribeAp
filterSearch: true,
},
{
title: '申请服务',
title:$t('申请服务'),
dataIndex: ['service','name'],
ellipsis:true
},
{
title: '审批状态',
title:$t('审批状态'),
dataIndex: 'status',
valueType: 'select',
ellipsis: true,
filters: true,
onFilter: true,
valueEnum: new Map([
[0, <span className="text-status_fail"></span>],
[2,<span className="text-status_success"></span>],
[0, <span className="text-status_fail">{$t('拒绝')}</span>],
[2,<span className="text-status_success">{$t('通过')}</span>],
]),
},
{
title: '审批人',
title:$t('审批人'),
dataIndex: ['approver','name'],
ellipsis: true,
width:88,
@@ -155,7 +156,7 @@ export const SUBSCRIBE_APPROVAL_INNER_DONE_TABLE_COLUMN : ProColumns<SubscribeAp
filterSearch: true,
},
{
title: '审批时间',
title:$t('审批时间'),
dataIndex: 'approvalTime',
ellipsis: true,
// sorter: true,,
@@ -179,17 +180,17 @@ export type SubscribeApprovalTableListItem = {
};
export enum PublishApplyStatusEnum{
'accept'="审批完成",
'apply'="发布审批中",
'running'="在线",
'none'="-",
'refuse'="已拒绝",
export const PublishApplyStatusEnum = {
'accept': $t("审批完成"),
'apply': $t("发布审批中"),
'running': $t("在线"),
'none': $t("-"),
'refuse': $t("已拒绝"),
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
'close' = '-',
'stop' = '中止',
'error' = '发布异常',
'publishing' = '发布中'
'close' : $t('-'),
'stop' : $t('中止'),
'error' : $t('发布异常'),
'publishing' : $t('发布中')
}
@@ -209,33 +210,99 @@ export const PublishTableStatusColorClass = {
'publishing':'text-[#46BE11]',
}
export const ApprovalStatusColorClass = {
new: 'text-[#138913]', // 使用 Tailwind 的 Arbitrary Properties
update: 'text-[#03a9f4]',
delete: 'text-[#ff3b30]',
none: 'text-[var(--MAIN_TEXT)]', // 假设你也有一个“none”的状态
};
enum PublishStatusEnum{
'apply' = '待审批',
'accept' = '审批通过',
'done' = '已发布',
'stop' = '发布终止',
'close' = '已关闭',
'refuse' = '已拒绝',
'error' = '发布异常',
'publishing' = '发布中'
export const ApprovalApiColumns = [
{
title:$t('API 名称'),
dataIndex:'name',
ellipsis:true
},
{
title:$t('请求方式'),
dataIndex:'method',
ellipsis:true
},
{
title:$t('路径'),
dataIndex:'path',
ellipsis:true
},
{
title:$t('类型'),
dataIndex:'change',
render:(_,entity)=>(
<Tooltip placement="top" title={entity.change === 'error' ?$t('该 API 缺失(0)(1)(2)请先补充',[entity.proxyStatus == 1 && $t('转发信息,'),entity.docStatus == 1 && $t('文档信息,'),entity.upstreamStatus == 1 && $t('上游信息,')]):''}>
<span className={`${ApprovalStatusColorClass[entity.change as keyof typeof ApprovalStatusColorClass]} truncate block`}>
{ChangeTypeEnum[entity.change as (keyof typeof ChangeTypeEnum)] || '-'}
{entity.change === 'error' ?$t('该 API 缺失(0)(1)(2)请先补充',[entity.proxyStatus == 1 && $t('转发信息,'),entity.docStatus == 1 && $t('文档信息,'),entity.upstreamStatus == 1 && $t('上游信息,')]):''}
</span>
</Tooltip>)
}
]
export const ApprovalUpstreamColumns = [
{
title:$t('上游类型'),
dataIndex:'type',
ellipsis:true,
valueEnum:{
'static':{
text:$t('静态上游')
}
}
},
{
title:$t('地址'),
dataIndex:'addr',
render:(text:string[])=>(<>{text.join(',')}</>),
ellipsis:true
},
{
title:$t('类型'),
dataIndex:'change',
render:(_,entity)=>(
<Tooltip placement="top" title={entity.change === 'error' ? $t('该 API 缺失(0)(1)(2)请先补充',[entity.proxyStatus == 1 && $t('转发信息,'),entity.docStatus == 1 && $t('文档信息,'),entity.upstreamStatus == 1 && $t('上游信息,')]):''}>
<span className={`${ApprovalStatusColorClass[entity.change as keyof typeof ApprovalStatusColorClass]} truncate block`}>{ChangeTypeEnum[entity.change as (keyof typeof ChangeTypeEnum)] || '-'}
{entity.change === 'error' ?$t('该 API 缺失(0)(1)(2)请先补充',[entity.proxyStatus == 1 && $t('转发信息,'),entity.docStatus == 1 && $t('文档信息,'),entity.upstreamStatus == 1 && $t('上游信息,')]):''}</span>
</Tooltip>)
}
]
const PublishStatusEnum = {
'apply': $t('待审批'),
'accept' : $t('审批通过'),
'done' : $t('已发布'),
'stop': $t('发布终止'),
'close': $t('已关闭'),
'refuse' : $t('已拒绝'),
'error' : $t('发布异常'),
'publishing' : $t('发布中')
}
export const PUBLISH_APPROVAL_VERSION_INNER_TABLE_COLUMN : ProColumns<PublishTableListItem>[] = [
export const PUBLISH_APPROVAL_VERSION_INNER_TABLE_COLUMN : PageProColumns<PublishTableListItem>[] = [
{
title: '发布版本',
title:$t('发布版本'),
dataIndex: 'version',
ellipsis:true,
width:160,
fixed:'left'
},
{
title: '版本说明',
title:$t('版本说明'),
dataIndex: 'remark',
ellipsis:true
},
{
title: '创建版本时间',
title:$t('创建版本时间'),
dataIndex: 'createTime',
ellipsis:true,
sorter: (a,b)=> {
@@ -243,7 +310,7 @@ export const PUBLISH_APPROVAL_VERSION_INNER_TABLE_COLUMN : ProColumns<PublishTab
},
},
{
title: '版本状态',
title:$t('版本状态'),
dataIndex: 'status',
ellipsis:true,
filters: true,
@@ -260,7 +327,7 @@ export const PUBLISH_APPROVAL_VERSION_INNER_TABLE_COLUMN : ProColumns<PublishTab
])
},
{
title: '创建人',
title:$t('创建人'),
dataIndex: ['creator','name'],
ellipsis: true,
width:88,
@@ -271,32 +338,32 @@ export const PUBLISH_APPROVAL_VERSION_INNER_TABLE_COLUMN : ProColumns<PublishTab
}
];
export const PUBLISH_APPROVAL_RECORD_INNER_TABLE_COLUMN : ProColumns<PublishTableListItem>[] = [
export const PUBLISH_APPROVAL_RECORD_INNER_TABLE_COLUMN : PageProColumns<PublishTableListItem>[] = [
{
title: '申请时间',
title:$t('申请时间'),
dataIndex: 'applyTime',
ellipsis:true,
width:182,
fixed:'left',
},
{
title: '审核时间',
title:$t('审核时间'),
dataIndex: 'approveTime',
ellipsis:true,
width:182,
},
{
title: '版本号',
title:$t('版本号'),
dataIndex: 'version',
ellipsis:true
},
{
title: '版本说明',
title:$t('版本说明'),
dataIndex: 'remark',
ellipsis:true
},
{
title: '发布状态',
title:$t('发布状态'),
dataIndex: 'status',
ellipsis:true,
valueEnum:new Map([
@@ -311,12 +378,12 @@ export const PUBLISH_APPROVAL_RECORD_INNER_TABLE_COLUMN : ProColumns<PublishTabl
])
},
{
title: '备注',
title:$t('备注'),
dataIndex: 'comments',
ellipsis:true
},
{
title: '申请人',
title:$t('申请人'),
dataIndex: ['applicant','name'],
ellipsis: true,
width:88,
@@ -326,7 +393,7 @@ export const PUBLISH_APPROVAL_RECORD_INNER_TABLE_COLUMN : ProColumns<PublishTabl
filterSearch: true,
},
{
title: '审批人',
title:$t('审批人'),
dataIndex: ['approver','name'],
ellipsis: true,
width:88,
@@ -337,9 +404,9 @@ export const PUBLISH_APPROVAL_RECORD_INNER_TABLE_COLUMN : ProColumns<PublishTabl
},
];
export const PUBLISH_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>[] = [
export const PUBLISH_APPROVAL_TABLE_COLUMN : PageProColumns<ApprovalTableListItem>[] = [
{
title: '申请时间',
title:$t('申请时间'),
dataIndex: 'applyTime',
ellipsis:true,
width:182,
@@ -349,17 +416,17 @@ export const PUBLISH_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>[]
},
},
{
title: '申请系统',
title:$t('申请系统'),
dataIndex: ['service','name'],
ellipsis:true
},
{
title: '所属团队',
title:$t('所属团队'),
dataIndex: ['team','name'],
ellipsis:true
},
{
title: '审批状态',
title:$t('审批状态'),
dataIndex: 'status',
ellipsis:{
showTitle:true
@@ -369,7 +436,7 @@ export const PUBLISH_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>[]
valueType: 'select',
},
{
title: '申请人',
title:$t('申请人'),
dataIndex: ['applier','name'],
ellipsis: true,
width:88,
@@ -379,7 +446,7 @@ export const PUBLISH_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>[]
filterSearch: true,
},
{
title: '审批人',
title:$t('审批人'),
dataIndex: ['approver','name'],
ellipsis: true,
width:88,
@@ -389,7 +456,7 @@ export const PUBLISH_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>[]
filterSearch: true,
},
{
title: '审批时间',
title:$t('审批时间'),
dataIndex: 'approvalTime',
// sorter: true,
ellipsis:true,
@@ -400,3 +467,42 @@ export const PUBLISH_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>[]
},
},
];
export const ChangeTypeEnum = {
'new': $t('新增'),
'update': $t('变更'),
'delete' : $t('删除'),
'none' : $t('无变更'),
'error' : $t('缺失字段')
}
// export const APPROVAL_I18NEXT_FOR_ENUM = {
// [SubscribeEnum.Rejected]:$t('驳回'),
// [SubscribeEnum.Reviewing]:$t('审核中'),
// [SubscribeEnum.Subscribed]:$t('已订阅'),
// [SubscribeEnum.Unsubscribed]:$t('取消订阅'),
// [SubscribeEnum.CancelRequest]:$t('取消申请'),
// [SubscribeFromEnum.manual]:$t('手动添加'),
// [SubscribeFromEnum.subscribe]:$t('订阅申请'),
// }
export const SubscribeApprovalList = [
{
title:$t('申请方应用'),key:'application'
},
{
title:$t('申请方所属团队'),key:'applyTeam'
},
{
title:$t('申请人'),key:'applier'
},
{
title:$t('申请时间'),key:'applyTime'
},
{
title:$t('申请服务'),key:'service'
},
{
title:$t('服务所属团队'),key:'team'
}
]
@@ -100,4 +100,20 @@ export type PublishTableListItem = {
service:EntityItem
team:EntityItem
status:keyof typeof PublishApplyStatusEnum
}
export type PublishApprovalModalProps = {
type:'approval'|'view'|'add'|'publish'|'online'
data:PublishApprovalInfoType | PublishApprovalInfoType &{id?:string} | PublishVersionTableListItem
insideSystem?:boolean
serviceId:string
teamId:string
clusterPublishStatus?:SystemInsidePublishOnlineItems[]
}
export type PublishApprovalModalHandle = {
save:(operate:'pass'|'refuse') =>Promise<boolean|string>
publish:(notSave?:boolean)=>Promise<boolean|string|Record<string, unknown>>
online:()=>Promise<boolean|string>
}
@@ -1,3 +1,5 @@
import { $t } from "@common/locales"
const CODE_LANG = [
{
label: 'Java(OK HTTP)',
@@ -68,7 +70,7 @@ const CODE_LANG = [
]
},
{
label: '微信小程序',
label: $t('微信小程序'),
value: 21
},
// {
@@ -1,20 +0,0 @@
export type BasicResponse<T> = {
code:number
data:T
msg:string
}
export const STATUS_CODE = {
SUCCESS:0,
UNANTHORIZED:401,
FORBIDDEN:403
}
export const STATUS_COLOR = {
'done':'text-[#03a9f4]',
'error':'text-[#ff3b30]'
}
const NAV_HEIGHT = 72
@@ -0,0 +1,71 @@
import { $t } from "@common/locales"
export type BasicResponse<T> = {
code:number
data:T
msg:string
}
export const STATUS_CODE = {
SUCCESS:0,
UNANTHORIZED:401,
FORBIDDEN:403
}
export const STATUS_COLOR = {
'done':'text-[#03a9f4]',
'error':'text-[#ff3b30]'
}
// avoid changing route within ths same category
export const routerKeyMap = new Map<string, string[]|string>([
['workspace',['tenantManagement','service','team','serviceHub']],
['my',['tenantManagement','service','team']],
['mainPage',['dashboard','systemrunning']],
['operationCenter',['member','user','role','servicecategories']],
['organization',['member','user','role']],
['serviceHubSetting',['servicecategories']],
['maintenanceCenter',['partition','logsettings','resourcesettings','openapi']
]])
export const COLUMNS_TITLE = {
operate : $t('操作')
}
export const VALIDATE_MESSAGE = {
required: $t('必填项'),
email:$t('不是有效邮箱地址')
}
export const PLACEHOLDER = {
input:$t('请输入'),
select:$t('请选择'),
startWithAlphabet:$t('英文数字下划线任意一种,首字母必须为英文'),
specialStartWithAlphabet:$t('支持字母开头、英文数字中横线下划线组合')
}
export const FORM_ERROR_TIPS = {
refuseOpinion: $t('选择拒绝时,审批意见为必填'),
clusterTest:$t('无法连接集群,请检查集群地址是否正确或防火墙配置'),
}
export const RESPONSE_TIPS = {
success: $t('操作成功'),
error: $t('操作失败'),
operating:$t('正在操作'),
loading:$t('正在加载数据'),
dataError:$t('获取数据失败'),
loginSuccess: $t('登录成功'),
logoutSuccess:$t('退出成功,将跳转至登录页'),
refuseOpinion: $t('未填写审核意见'),
copySuccess:$t('复制成功'),
copyError:$t('复制失败,请手动复制')
}
export const DELETE_TIPS = {
default:$t('该数据删除后将无法找回,请确认是否删除?')
}
+2 -2
View File
@@ -63,8 +63,8 @@ export type SimpleTeamItem = {
}
export type MatchItem = {
position:MatchPositionEnum
matchType:MatchTypeEnum
position:typeof MatchPositionEnum
matchType:typeof MatchTypeEnum
key:string
pattern:string
id?:string
@@ -1,7 +1,7 @@
import {createContext, Dispatch, FC, ReactNode, useContext, useReducer, useState} from "react";
import { useFetch } from "@common/hooks/http";
import { App } from "antd";
import { BasicResponse, STATUS_CODE } from "@common/const/const";
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
import { checkAccess } from "@common/utils/permission";
import { PERMISSION_DEFINITION } from "@common/const/permissions";
@@ -11,31 +11,31 @@ interface GlobalState {
version: string;
updateDate: string;
powered:string;
mainPage:string
mainPage:string;
language:string;
}
// Define the shape of the user data
interface UserData {
username: string;
// Add other user-related fields as needed
}
// Define actions for state updates
type Action =
export type GlobalAction =
| { type: 'LOGIN'}
| { type: 'LOGOUT' }
| { type: 'UPDATE_USERDATA'; userData: UserData }
| { type: 'UPDATE_VERSION'; version: string }
| { type: 'UPDATE_DATE'; updateDate: string }
| { type: 'UPDATE_POWER'; powered: string }
| { type: 'UPDATE_MAIN_PAGE'; mainPage: string };
| { type: 'UPDATE_MAIN_PAGE'; mainPage: string }
| { type: 'UPDATE_LANGUAGE'; language: string }
/*
*/
const GlobalContext = createContext<{
export const GlobalContext = createContext<{
state: GlobalState;
dispatch: Dispatch<Action>;
dispatch: Dispatch<GlobalAction>;
accessData:Map<string,string[]>;
pluginAccessDictionary:{[k:string]:string};
getGlobalAccessData:()=>void;
@@ -49,8 +49,7 @@ const GlobalContext = createContext<{
} | undefined>(undefined);
// Define a reducer function to handle state updates
const globalReducer = (state: GlobalState, action: Action): GlobalState => {
const globalReducer = (state: GlobalState, action: GlobalAction): GlobalState => {
switch (action.type) {
case 'LOGIN':
return {
@@ -88,6 +87,11 @@ const globalReducer = (state: GlobalState, action: Action): GlobalState => {
...state,
mainPage: action.mainPage,
};
case 'UPDATE_LANGUAGE':
return {
...state,
language: action.language,
};
default:
return state;
}
@@ -103,7 +107,8 @@ export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
version: '1.0.0',
updateDate: '2024-07-01',
powered:'Powered by https://apipark.com',
mainPage:'/service/list'
mainPage:'/service/list',
language:'en'
});
const [accessData,setAccessData] = useState<Map<string,string[]>>(new Map())
const [pluginAccessDictionary, setPluginAccessDictionary] = useState<{[k:string]:string}>({})
@@ -119,8 +124,8 @@ export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
setAccessData(prevData => new Map(prevData).set('system', data.access))
resolve(data.response)
}else{
message.error(msg || '操作失败')
reject(data.msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(data.msg || RESPONSE_TIPS.error)
}
})
)
@@ -134,7 +139,7 @@ export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
setAccessData(prevData => new Map(prevData).set('team', data.access))
setTeamDataFlushed(true)
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
})
}
+4 -9
View File
@@ -1,10 +1,5 @@
/*
* @Name:
* @Description:
* @Copyright: 广
* @LastEditors: maggieyyy
* @LastEditTime: 2024-05-10 16:38:56
*/
import { RESPONSE_TIPS } from '@common/const/const';
import { message } from 'antd';
import { useEffect, useState } from 'react';
@@ -14,7 +9,7 @@ const useCopyToClipboard = () => {
const copyToClipboard = (text: string) => {
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text)?.then(() => {
message.success('复制成功')
message.success(RESPONSE_TIPS.copySuccess)
setIsCopied(true)
})
.catch((error) => {
@@ -34,7 +29,7 @@ const useCopyToClipboard = () => {
textArea.select();
new Promise<void>((resolve, reject) => {
if(document.execCommand('copy')) {
message.success('复制成功')
message.success(RESPONSE_TIPS.copySuccess)
setIsCopied(true)
resolve()
} else {
+3 -2
View File
@@ -2,12 +2,13 @@
import * as ExcelJS from 'exceljs';
import { saveAs } from 'file-saver';
import { ProColumnType } from '@ant-design/pro-components';
import { $t } from '@common/locales';
export const useExcelExport = <T>() => {
const createExcel = (sheetTitle: string, columns: ExcelJS.Column[], tableData: T[]) => {
const workBook = new ExcelJS.Workbook()
const sheet = workBook.addWorksheet(sheetTitle || '默认工作表');
const sheet = workBook.addWorksheet(sheetTitle || $t('默认工作表'));
sheet.columns = columns;
sheet.addRows(tableData);
return workBook
@@ -47,7 +48,7 @@ export const useExcelExport = <T>() => {
const getFileName = (fileTitle: string, date: [number, number]): string => {
const [start, end] = date.map((time) => getDateFormat(time));
return `${fileTitle}-${start}${end}`;
return `${fileTitle}-${start}${$t('至')}${end}`;
};
const getDateFormat = (time: number): string => {
@@ -0,0 +1,57 @@
import i18n from 'i18next';
import { initReactI18next, useTranslation } from 'react-i18next';
// i18next-browser-languagedetector插件 这是一个 i18next 语言检测插件,用于检测浏览器中的用户语言,
import LanguageDetector from 'i18next-browser-languagedetector';
import crc32 from 'crc/crc32';
// 引入需要实现国际化的简体、繁体、英文三种数据的json文件
import zhCN from 'antd/locale/zh_CN';
import enGB from 'antd/locale/en_GB';
import localZh_CN from './scan/zh-CN.json'; // 本地翻译中文文件
import localEn_GB from './scan/en-GB.json'; // 本地翻译英文文件
// import config from '../../../../i18next-scanner.config.js';
const resources = {
cn: {
translation: localZh_CN,
...zhCN
},
en: {
translation: localEn_GB,
...enGB
}
};
i18n
.use(LanguageDetector) // 嗅探当前浏览器语言 zh-CN
.use(initReactI18next) // 将 i18n 向下传递给 react-i18next
.init({
// 初始化
resources, // 本地多语言数据
// fallbackLng: config.lang, // 默认当前环境的语言
detection: {
caches: ['localStorage', 'sessionStorage', 'cookie']
}
});
// --------这里是i18next-scanner新增的配置-------------
export const $t = (key: string, params?: any[]): string => {
const hashKey = `K${crc32(key).toString(16)}`; // 将中文转换成crc32格式去匹配对应的json语言包
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) => {
return params[b];
});
}
return words;
};
export default i18n;
@@ -0,0 +1,570 @@
{
"Kc0e5ef9f": "workspace",
"K48325b6": "Search for (0) names",
"K3d7465f7":"file log",
"K3863c722": "我的",
"Kfe93ef35": "应用",
"Kb58e0c3f": "服务",
"Kc9e489f5": "团队",
"K61c89f5f": "API 市场",
"K16d71239": "仪表盘",
"K714c192d": "运行视图",
"Kd57dfe97": "系统拓扑图",
"K3fe97dcc": "系统设置",
"K67ef3525": "组织",
"K74aef1ad": "成员",
"Kf644225f": "角色",
"K958da71f": "服务分类管理",
"Kf270ca55": "运维与集成",
"Ke93d36ed": "集群",
"K481e8a05": "证书",
"Kca53edd0": "日志",
"Kb283e720": "资源",
"K631d646f": "Open API",
"K6535ff9c": "账号设置",
"Kf15499b4": "退出登录",
"Kabbd6e6": "文档",
"K1196b104": "APIPark - 企业API数据开放平台",
"K1f42de3": "HTTP 状态码",
"K4770dff4": "系统状态码",
"Kf89e58f1": "描述",
"K9e53c664": "提交",
"Kf8e7294c": "上一步",
"Ka0451c97": "取消",
"Kb1dedda3": "关闭",
"Kb2fc7600": "添加配置",
"K4e07217d": "编辑配置",
"K4ea968fe": "编辑(0)",
"Ka7aaaeb": "添加(0)",
"Kaff78ecf": "请输入Key",
"K65d46535": "请输入Value",
"Kc14b2ea3": "返回",
"K11d3633a": "ID",
"Kbff43de3": "名称",
"K16ca79ef": "Driver",
"K7a369eef": "已发布",
"Kcfa1a4d2": "下线",
"K771dc3b7": "上线",
"K530f5951": "查看",
"Kecbd7449": "删除",
"K1cbe2507": "确认",
"Ka1d885c1": "添加",
"Kad207008": "编辑",
"K1ff96ff": "申请系统",
"K9bf855d6": "所属团队",
"K11b994ed": "申请人",
"K939baba7": "申请时间",
"Kdab2e63b": "版本号",
"K8b29c460": "版本说明",
"K36a72ad1": "API 列表",
"K54e44357": "上游列表",
"Kb8e8e6f5": "备注",
"K1ab0ae5b": "申请原因",
"K53c00c3c": "审核意见",
"K7edf331d": "时间",
"Kef45b208": "近1小时",
"K9dbf22b8": "近24小时",
"K820fbfab": "近3天",
"Kd6d28fc": "近7天",
"K23fda291": "暂无操作权限,请联系管理员分配。",
"K4618cb0a": "微信小程序",
"Ka854f511": "获取文件,需填路径",
"Kaa11a695": "暂不支持生成非 HTTPS 或非 HTTP 协议的代码示例",
"Kbe46924e": "搜索编程语言...",
"Ke8e4f258": "编程语言",
"K29c07a47": "成功示例",
"K1f5c814d": "失败示例",
"K4ef022d7": "默认 text/html;charset=UTF-8",
"Kd061b5bf": "暂未填写示例",
"Kc14cec33": "Binary",
"K48b4d9e3": "请求头部",
"Kcd347eaf": "请求体",
"K9e100bfe": "Query 参数",
"K3e9f12fd": "REST 参数",
"K2bfa290c": "api request editor",
"Kb36d111a": "返回头部",
"K980bde79": "返回值",
"Kb04d201a": "更多设置",
"Kee74f5b4": "添加子参数",
"Kc7d3106c": "向下添加行",
"Keaabd222": "标签",
"K8ad2c50e": "参数名",
"K67d68dd1": "类型",
"K29245f47": "必需",
"Ke32cbcd3": "示例",
"Kc13936c6": "输入 URL 或 cURL",
"Ka1ede006": "HTTP",
"K152ac44e": "参数位置",
"K1660ae72": "匹配类型",
"K91ced765": "参数值",
"K5b265628": "操作类型",
"K1826982d": "新增或修改",
"Kd65b55f5": "匹配参数值",
"K15f35bf2": "转发上游路径",
"K79dec0dd": "请求超时时间",
"K7d465645": "绑定上游服务",
"K63a6404d": "重试时间",
"K47740727": "转发上游请求头",
"K2b605d42": "More",
"K1df9fbd5": "导入",
"K5e85df18": "导入格式",
"K9eaf7885": "全量替换",
"Kf8c3a80b": "在末端插入",
"Kd96b2d7d": "增量更新",
"Kf2fc08eb": "请求头",
"Ka45f1d8": "Rest 参数",
"K94bb113a": "大小",
"K359919b5": "另存为文件",
"K38bf1b90": "响应",
"K59f4186e": "响应头",
"K5f1e23fd": "正文",
"Kf404ef7d": "发送(Enter)",
"K2dbfd648": "中止",
"Kacabc771": "秒",
"K13ae6a93": "复制",
"Ke54a14a3": "格式化",
"K43934f6d": "搜索",
"K741decac": "替代",
"Kd507abff": "确定",
"Kca2d1624": "The (0) must not be negative.",
"K792b255a": "The (0) must be greater than or equal to the (1).",
"Kf0bed26d": "值枚举",
"K633a03ca": "枚举",
"Kd2766caf": "最小长度",
"Kd6d52485": "最大长度",
"Kea15f66c": "最小值",
"K1af340ff": "最大值",
"K68691e16": "将文件拖拽至此处上传,或点击选择文件上传",
"Kcec46ae": "Upload Files",
"K760fb044": "Files Selected",
"Kea2bdee0": "请填写接口名称",
"K49053438": "详细说明",
"K148f6fa4": "高级匹配",
"K3ae4c789": "转发配置",
"K2f4d0a37": "请求参数",
"Kde2d6dbd": "返回示例",
"K70e6069c": "测试 API",
"Ke4603448": "请求 Header",
"K89fd86b3": "请求 Body",
"K8747e3c4": "请求示例代码",
"K8613e6e7": "响应示例",
"Kab1c2159": "响应 Header",
"Kd2be51d1": "响应 Body",
"K831aa6c0": "申请方-应用",
"K58ca9485": "申请服务",
"K283f55b4": "服务所属系统",
"Kd60d204": "服务所属团队",
"K3a9a3b75": "审批状态",
"K4f57b2de": "审批人",
"K31dabc6b": "审批时间",
"K8582af3f": "拒绝",
"K54e27f57": "通过",
"K7eeca185": "审批完成",
"Kd4061161": "发布审批中",
"K823bfe63": "在线",
"K97ddb3f8": "-",
"Kc9315fa1": "已拒绝",
"K3fbe7511": "发布异常",
"Ke64e695c": "发布中",
"K17f93984": "API 名称",
"K1365fe45": "请求方式",
"Kc380335f": "路径",
"K4ee62e8": "该 API 缺失(0)(1)(2)请先补充",
"K385591f3": "转发信息,",
"K68415c14": "文档信息,",
"K133b75e9": "上游信息,",
"Kad98e030": "上游类型",
"Kdeed8399": "静态上游",
"K78b1ca25": "地址",
"K6208054": "待审批",
"K1be7f021": "审批通过",
"K677a4959": "发布终止",
"Kfd6d2d3d": "已关闭",
"K9d7e880a": "发布版本",
"K855135f": "创建版本时间",
"Kcbf39b82": "版本状态",
"K339d15b5": "创建人",
"K7194cea2": "审核时间",
"K69827c60": "发布状态",
"K1644b775": "新增",
"K4fdf4dcc": "变更",
"K33d66e26": "无变更",
"K9b70c007": "缺失字段",
"Kd85208a3": "驳回",
"K8adf7f8b": "审核中",
"Kad6aa439": "已订阅",
"K3118fdb0": "取消订阅",
"K9a68443b": "取消申请",
"K18307d56": "手动添加",
"K705fe9f5": "订阅申请",
"Kbc96ebec": "申请方应用",
"K1f89176d": "申请方所属团队",
"Kfe731dfc": "操作",
"K71661ee8": "必填项",
"Kcbee3f8": "不是有效邮箱地址",
"K442cfba1": "请输入",
"K3bb646e4": "请选择",
"Ka4ecfa40": "英文数字下划线任意一种,首字母必须为英文",
"K39686a7f": "支持字母开头、英文数字中横线下划线组合",
"K4d6a0190": "选择拒绝时,审批意见为必填",
"K37318b68": "无法连接集群,请检查集群地址是否正确或防火墙配置",
"K7f0c746d": "操作成功",
"K6a365d01": "操作失败",
"K978062b6": "正在操作",
"Kca7bd6d4": "正在加载数据",
"K3c93b77e": "获取数据失败",
"Ke108c369": "登录成功",
"K9168d3e": "退出成功,将跳转至登录页",
"K2f8a7ab7": "未填写审核意见",
"Kb858d78a": "复制成功",
"K26e85d15": "复制失败,请手动复制",
"K5cfdd950": "该数据删除后将无法找回,请确认是否删除?",
"K2a3f24ac": "默认工作表",
"K7e1ab4b0": "至",
"Kf1b166e7": "详情",
"K28555332": "暂不支持带有双斜杠//的url",
"Keb9fcdad": "用户名",
"Kc654b275": "邮箱",
"Kbe2ecc69": "部门",
"K759fb403": "状态",
"K52c8a730": "启用",
"K718c9310": "禁用",
"K2c5882be": "绑定域名",
"K1cc07937": "过期日期",
"K8b7c2592": "更新者",
"Keefda53d": "更新时间",
"K9f3db3ca": "集群名称",
"Ke7487049": "集群 ID",
"Kb660ffe8": "节点名称",
"Kf12b3034": "管理地址",
"K867e6faf": "服务地址",
"K37348a5e": "集群同步地址",
"K151d2bb7": "环境名称",
"Kfa744afd": "集群数量",
"K23a3bd72": "异常",
"Ke039b9b5": "正常",
"K5c123bad": "角色名称",
"K76036e25": "HTTP 请求头",
"K8d4cbf50": "Cookie",
"K44607e3f": "全等匹配",
"Kc287500a": "前缀匹配",
"Kfc0b1147": "后缀匹配",
"Ka4a92043": "子串匹配",
"K30b2e44f": "非等匹配",
"Kb1587991": "空值匹配",
"K1e97dbd8": "存在匹配",
"Kc8ee3e62": "不存在匹配",
"K87c5a801": "区分大小写的正则匹配",
"K95f062f1": "不区分大小写的正则匹配",
"Kfbd230a5": "任意匹配",
"K413b9869": "服务名称",
"K1e84ad04": "服务 ID",
"K72b0c0b3": "API 数量",
"Kf7200cd9": "负责人",
"Kefaf9956": "创建时间",
"K98db2cb9": "申请状态",
"Ke792d01c": "所属服务",
"K61b62ace": "来源",
"Ke63767cf": "添加时间",
"K3a67ea90": "订阅方",
"K442937c4": "订阅时间",
"K34111022": "协议/方法",
"K62d10724": "URL",
"Ka9481f95": "创建者",
"Kf88d56fd": "上游 ID",
"K11a92fb2": "更新人",
"K2c2bc64f": "动态服务发现",
"Kc9a2a716": "HTTPS",
"Ka7f8266f": "带权轮询",
"Kd17edabd": "IP Hash",
"Kaeba0229": "透传客户端请求 Host",
"K6d7e2fd0": "使用上游服务 Host",
"K31332633": "重写 Host",
"Ke65db976": "权重",
"Kf966b12d": "内部服务:可通过网关访问,但不展示在服务广场",
"Kfc939e49": "公开服务:可通过网关访问,展示在服务广场,可被其他应用订阅",
"Ke96ccf45": " ",
"K5582ac8": "请求路径",
"K92485dd1": "所有 API",
"Ke64e43a": "隐藏鉴权信息",
"K1a78e6f0": "过期时间",
"K40bbb0a3": "服务ID",
"K9919285b": "服务类型",
"K63938137": "公开服务",
"Kfb20a12": "内部服务",
"Kedd64e4d": "停用",
"Ka29b346f": "地址(IP 端口或域名)",
"K63b1e0dc": "权重(0-999",
"K74ab00a3": "已审批",
"Kea996156": "发布申请记录",
"Kbea7d266": "所属系统",
"Ka36c13cc": "调用系统名称",
"Kd78d73a7": "调用服务名称",
"K73c144eb": "当前系统名称",
"K285bd65e": "被调用服务名称",
"K5cbab635": "被调用系统名称",
"K93c2696e": "上线结果",
"K43fcaf94": "成功",
"Kc71c6a9": "上线失败",
"K56c686f8": "失败",
"K3ba29a85": "API",
"Kda8d5ea1": "上游",
"Kdefa9caa": "使用说明",
"K36856e71": "发布",
"K6382bbfd": "订阅管理",
"K2eef4e4": "订阅审批",
"Ka97bd9e5": "订阅方管理",
"K5974bf24": "管理",
"K3fa5c4c3": "调用拓扑图",
"Kb5c7b82d": "设置",
"K2472615e": "服务数量",
"Kc02aa5f1": "API数量",
"Ke08ff808": "添加日期",
"Kd7d84192": "姓名",
"Kc88e03b6": "团队角色",
"Kdf35c48c": "所有成员",
"K3818f03d": "审批",
"K56b4254f": "发布申请",
"Kd2c1a316": "登录",
"Kf076f63c": "请输入账号",
"K80a560a1": "账号",
"K25c895d5": "请输入密码",
"K551b0348": "密码",
"K480045ce": "Version (0)-(1)",
"Kadee8e49": "日志配置",
"K3453272": "APIPark 提供详尽的 API 调用日志,帮助企业监控、分析和审计 API 的运行状况。",
"K33c76dbc": "部门名称",
"K84829ca9": "父部门 ID",
"K4d7fc74b": "子部门名称",
"Ka16e6c44": "未激活、已禁用的成员无法加入到部门",
"Ked03ba97": "请选择成员需要新加入的部门",
"K5e237e06": "name",
"K184d3473": "添加账号",
"K1ecb35f2": "编辑成员信息",
"Ke6f00b44": "加入部门",
"K501cb1e7": "确定删除成员?此操作无法恢复,确认操作?",
"Kf20863b5": "成员与部门",
"K5f27a546": "输入用户名、邮箱查找成员",
"K26c698bb": "添加部门",
"Kb9cf2a7d": "添加子部门",
"Kc83551f5": "重命名",
"K3f1077c9": "设置成员和对应的角色,成员只能够看到权限范围内的功能和数据。",
"Kdce62a6": "搜索部门",
"K8ef69ee2": "密钥",
"Kba3507d6": "上传密钥",
"K93ac0f23": "密钥文件的后缀名一般为 .key 的文件内容",
"K7cdd1331": "上传证书",
"K6d91905d": "证书文件的后缀名一般为 .crt 或 .pem 的文件内容",
"Kd0f6ded7": "添加证书",
"Ke5732d60": "修改证书",
"K3ca07a70": "证书管理",
"Kdb927f83": "通过为 API 服务配置和管理 SSL 证书,企业可以加密数据传输,防止敏感信息被窃取或篡改。",
"K877985b7": "修改配置",
"Kdf66a675": "设置访问 API 的集群,让 API 在分布式环境中稳定运行,并且能够根据业务需求进行灵活扩展和优化。",
"Kaf074220": "未配置",
"K5878440c": "集群地址",
"K5e9022f8": "下一步",
"K8e7a0f80": "资源配置",
"Kabfe9512": "保存",
"K95c3fd8b": "设置角色的权限范围。",
"K138facd3": "系统级别角色",
"K6eac768d": "添加角色",
"Kb9c2cf02": "团队级别角色",
"Kb4ceecea": "添加子分类",
"K67479e88": "修改分类名称",
"K2bc75e2c": "添加分类",
"Kab4aab44": "重命名分类",
"K8e0e6977": "设置服务可选择的分类,方便团队成员快速找到API。",
"Ke595a20a": "分类名称",
"K9679728f": "父分类 ID",
"K9b2d08fd": "子分类名称",
"Kf14e76e5": "副本",
"K2e050340": "API 基础信息",
"K90f3c02f": "转发规则设置",
"K6ea8d549": "编辑文档",
"Kff5c18ac": "最后编辑人",
"K2eb99415": "转发规则",
"Ke93388fd": "编辑 API",
"K1b1ae3b0": "复制 API",
"K84aabfd4": "添加 API",
"K6a662463": "输入名称、URL 查找 API",
"K59bc6280": "API 详情",
"K2a16c93b": "单位:ms,最小值:1",
"K469e475a": "重试次数",
"Kd568e15c": "发布结果",
"K35f990b0": "查看详情",
"Kdbc1f6cb": "申请发布",
"Kb6860a3f": "回滚",
"Ka3494f4b": "请确认是否回滚?",
"Kb397a99f": "撤销申请",
"K7d401c0f": "请确认是否撤销申请?",
"Ke1b79b93": "终止发布",
"Ka2449180": "请确认是否终止发布?",
"K2cb02f38": "新建版本",
"Ka9c08390": "只允许上传PNG、JPG或SVG格式的图片",
"Kcf756b7a": "API 调用前缀",
"K43d101a": "选填,作为服务内所有API的前缀,比如host/{service_name}/{api_path},一旦保存无法修改",
"Kdc840242": "图标",
"K427a5bd5": "仅支持 .png .jpg .jpeg .svg 格式的图片文件, 大于 1KB 的文件将被压缩",
"K44bc352d": "Logo",
"Kf52a584d": "所属服务分类",
"K72b21be5": "设置服务展示在服务市场中的哪个分类下",
"Kde6bae17": "删除服务",
"K885ea699": "删除操作不可恢复,请谨慎操作!",
"K617f34f1": "最近一次更新者",
"K6ebca204": "最近一次更新时间",
"K39ab0358": "新增订阅方",
"K2d6658ed": "添加服务",
"K7b8f623f": "输入名称、ID、所属团队、负责人查找服务",
"Kdd9b5008": "后端默认使用的IP地址",
"K6bc47edb": "请求协议",
"Kc9acdb25": "负载均衡",
"K632dba5c": "转发 Host",
"Kc1f08a63": "重写域名",
"K628f6851": "超时时间",
"Kaff62621": "超时重试次数",
"Kc41ca30e": "调用频率限制",
"K813e1c0a": "团队名称",
"K692f5aa6": "团队 ID",
"K5de0bc2": "团队 ID(team_id)可用于检索团队,一旦保存无法修改。",
"Ka63dd985": "团队负责人",
"Ka6bcd272": "负责人对团队内的团队、服务、成员有管理权限",
"Ka2012bdd": "删除团队",
"Kbde1f3d": "服务数据清除后,方可删除",
"K395acc14": "移除成员",
"Kec46a57f": "添加成员",
"K48724410": "输入姓名查找",
"Kb9052305": "搜索用户名、邮箱",
"K5ece3bac": "设置团队和成员,然后你可以在团队内创建服务和应用、订阅API,成员只能看到所属团队内的服务和应用。",
"K510cdd27": "添加团队",
"K9244ae14": "输入名称、ID、负责人查找团队",
"Kc7b24b4b": "配置团队",
"Kecb51e2c": "旧密码",
"K8266bcf2": "新密码",
"Ka9aef039": "确认密码",
"Kcf42dcda": "两次密码不一致",
"Kf876a42d": "修改密码",
"K8ed884f": "管理个人账号",
"K9be8e1d7": "API调用统计",
"K521ab28e": "选择服务",
"Kcc8265e1": "选择API",
"K8aefc1e4": "请输入请求路径进行搜索",
"K50d471b2": "重置",
"Kee8ae330": "查询",
"Ka2c794a2": "导出",
"Kaf70c3b": "退出全屏",
"Kd22841a4": "(0)调用详情",
"K1512e983": "应用调用统计",
"Kb4d2007f": "请选择应用",
"K8c7f2d2e": "调用趋势",
"K657c3452": "(0)-(1)调用趋势",
"Kc04efb87": "调用量统计",
"Keb98266e": "加入总体数据对比",
"K18c2ed46": "(0)调用量",
"Kc3741830": "(0)调用成功率",
"Ka6aa5863": "请求总数",
"K9eaef42": "请求成功率",
"K7082a4af": "转发总数",
"K1ce386fb": "转发成功率",
"K87d6877e": "状态码4xx数",
"K4c8a54db": "状态码5xx数",
"Kd566283e": "调用总体趋势",
"Kd23a0be6": "请求报文量",
"Kec3e8361": "响应报文量",
"Ke6250744": "状态码4XX数",
"K2d79d4e1": "状态码5XX数",
"Kcf6553c6": "服务调用统计",
"Kffcfe375": "请选择服务",
"Ka65f739c": "调用详情",
"K89b7ac79": "API 请求量 Top10",
"Kc0915603": "应用调用量 Top10",
"Kf90b54": "服务被调用量 Top10",
"Kfb26388": "暂无请求统计数据",
"Kc8cbd8f8": "请求统计",
"K8dece48": "暂无转发统计数据",
"K1ee32434": "转发统计",
"Kcd125e4d": "暂无调用量统计数据",
"Kaa114e8b": "暂无报文量统计数据",
"K3ad84406": "报文量统计",
"K19a3ebe0": "请求成功数",
"Kcaa8259": "转发成功数",
"K888f038f": "失败状态码数",
"K42d2bef2": "平均响应时间(ms)",
"K9197c994": "最大响应时间(ms)",
"K7c2f3fee": "最小响应时间(ms)",
"K3d85ea54": "平均请求流量(KB)",
"Keec09d32": "最大请求流量(KB)",
"K3786b48": "最小请求流量(KB)",
"K5168eb63": "应用名称",
"K546e46f": "应用 ID",
"K4a1a14": "监控总览",
"K69741ea7": "服务被调用统计",
"K9c8d9933": "API 调用统计",
"K28cf9613": "每分钟",
"K18f25019": "每5分钟",
"Kf00f01ca": "每小时",
"Kfcda87fc": "每天",
"K29ec75dc": "每周",
"K145e4941": "亿",
"Ke6a935d": "万",
"K8f7abcab": " 次",
"K146477a8": "服务标签",
"K4de0af74": "服务分类",
"Kcce1af60": "订阅的服务",
"Kb6e9328f": "访问授权",
"Kb7e869a4": "应用管理",
"Kd59290a2": "搜索分类或标签",
"K6b75bdbc": "暂无API数据",
"Kd8a7a689": "搜索或选择应用",
"K4b15d6f5": "申请理由",
"Kb71b5a13": "鉴权类型",
"K4d1465ee": "Iss",
"K5dcd7ed8": "签名算法",
"K5b0eedd3": "Secret",
"K44f4ffe1": "RSA 公钥",
"Kc5ecd7d9": "用户名 JsonPath",
"K417d85cf": "校验字段",
"K3b82fe1d": "是否 Base64 加密",
"K49b5f4a3": "AK",
"K31418470": "SK",
"Kbfeb5297": "Apikey",
"K95764d1d": "删除应用",
"K217cb125": "鉴权详情",
"K2bb63eca": "添加鉴权",
"Kd74d69b7": "编辑鉴权",
"K9cbe1e0": "修改",
"Kd23d1716": "添加授权",
"K9dfa2c97": "永不过期",
"Kfa920c0": "到期时间",
"Kbeb4e991": "审批详情",
"Ked811bb1": "请确认是否取消订阅?",
"K50c39a62": "取消订阅申请",
"K1856c229": "请确认是否取消订阅申请?",
"K66ea2f0": "搜索服务",
"Kfeb2559b": "审批中",
"Ka2b6d281": "API 文档",
"K667bbbe7": "添加应用",
"Ka4b45550": "暂无服务描述",
"K3c7b175f": "订阅的服务数量:已通过 (0) 个,申请中 (1) 个",
"K850b4b2d": "状态码",
"Kbe3e9335": "退出测试",
"K370a3eb2": "服务市场",
"Kf7ec36d": "服务详情",
"K59cdbec3": "介绍",
"K4aa9ed2c": "申请",
"K6c060779": "服务信息",
"K8723422e": "接入应用",
"Kb97544cb": "供应方",
"Kb32f0afe": "分类",
"K81634069": "版本",
"K96a2f1c8": "无标签",
"K93d5a66e": "接入应用数量",
"K3e770a75": "鉴权 Token",
"K96059c69": "关联标签",
"K32263abd": "添加 Open Api",
"K7829bb78": "配置 Open Api",
"Kcdf76005": "Open Api",
"Ke2601944": "调用服务",
"K8504bca8": "放大",
"K693c1b41": "缩小"
}
@@ -0,0 +1,569 @@
{
"Kc0e5ef9f": "工作空间",
"K3863c722": "我的",
"Kfe93ef35": "应用",
"Kb58e0c3f": "服务",
"Kc9e489f5": "团队",
"K61c89f5f": "API 市场",
"K16d71239": "仪表盘",
"K714c192d": "运行视图",
"Kd57dfe97": "系统拓扑图",
"K3fe97dcc": "系统设置",
"K67ef3525": "组织",
"K74aef1ad": "成员",
"Kf644225f": "角色",
"K958da71f": "服务分类管理",
"Kf270ca55": "运维与集成",
"Ke93d36ed": "集群",
"K481e8a05": "证书",
"Kca53edd0": "日志",
"Kb283e720": "资源",
"K631d646f": "Open API",
"K6535ff9c": "账号设置",
"Kf15499b4": "退出登录",
"Kabbd6e6": "文档",
"K1196b104": "APIPark - 企业API数据开放平台",
"K1f42de3": "HTTP 状态码",
"K4770dff4": "系统状态码",
"Kf89e58f1": "描述",
"K9e53c664": "提交",
"Kf8e7294c": "上一步",
"Ka0451c97": "取消",
"Kb1dedda3": "关闭",
"Kb2fc7600": "添加配置",
"K4e07217d": "编辑配置",
"K4ea968fe": "编辑(0)",
"Ka7aaaeb": "添加(0)",
"Kaff78ecf": "请输入Key",
"K65d46535": "请输入Value",
"Kc14b2ea3": "返回",
"K11d3633a": "ID",
"Kbff43de3": "名称",
"K16ca79ef": "Driver",
"K7a369eef": "已发布",
"Kcfa1a4d2": "下线",
"K771dc3b7": "上线",
"K530f5951": "查看",
"Kecbd7449": "删除",
"K1cbe2507": "确认",
"K48325b6": "搜索(0)名称",
"Ka1d885c1": "添加",
"Kad207008": "编辑",
"K1ff96ff": "申请系统",
"K9bf855d6": "所属团队",
"K11b994ed": "申请人",
"K939baba7": "申请时间",
"Kdab2e63b": "版本号",
"K8b29c460": "版本说明",
"K36a72ad1": "API 列表",
"K54e44357": "上游列表",
"Kb8e8e6f5": "备注",
"K1ab0ae5b": "申请原因",
"K53c00c3c": "审核意见",
"K7edf331d": "时间",
"Kef45b208": "近1小时",
"K9dbf22b8": "近24小时",
"K820fbfab": "近3天",
"Kd6d28fc": "近7天",
"K23fda291": "暂无操作权限,请联系管理员分配。",
"K4618cb0a": "微信小程序",
"Ka854f511": "获取文件,需填路径",
"Kaa11a695": "暂不支持生成非 HTTPS 或非 HTTP 协议的代码示例",
"Kbe46924e": "搜索编程语言...",
"Ke8e4f258": "编程语言",
"K29c07a47": "成功示例",
"K1f5c814d": "失败示例",
"K4ef022d7": "默认 text/html;charset=UTF-8",
"Kd061b5bf": "暂未填写示例",
"Kc14cec33": "Binary",
"K48b4d9e3": "请求头部",
"Kcd347eaf": "请求体",
"K9e100bfe": "Query 参数",
"K3e9f12fd": "REST 参数",
"K2bfa290c": "api request editor",
"Kb36d111a": "返回头部",
"K980bde79": "返回值",
"Kb04d201a": "更多设置",
"Kee74f5b4": "添加子参数",
"Kc7d3106c": "向下添加行",
"Keaabd222": "标签",
"K8ad2c50e": "参数名",
"K67d68dd1": "类型",
"K29245f47": "必需",
"Ke32cbcd3": "示例",
"Kc13936c6": "输入 URL 或 cURL",
"Ka1ede006": "HTTP",
"K152ac44e": "参数位置",
"K1660ae72": "匹配类型",
"K91ced765": "参数值",
"K5b265628": "操作类型",
"K1826982d": "新增或修改",
"Kd65b55f5": "匹配参数值",
"K15f35bf2": "转发上游路径",
"K79dec0dd": "请求超时时间",
"K7d465645": "绑定上游服务",
"K63a6404d": "重试时间",
"K47740727": "转发上游请求头",
"K2b605d42": "More",
"K1df9fbd5": "导入",
"K5e85df18": "导入格式",
"K9eaf7885": "全量替换",
"Kf8c3a80b": "在末端插入",
"Kd96b2d7d": "增量更新",
"Kf2fc08eb": "请求头",
"Ka45f1d8": "Rest 参数",
"K94bb113a": "大小",
"K359919b5": "另存为文件",
"K38bf1b90": "响应",
"K59f4186e": "响应头",
"K5f1e23fd": "正文",
"Kf404ef7d": "发送(Enter)",
"K2dbfd648": "中止",
"Kacabc771": "秒",
"K13ae6a93": "复制",
"Ke54a14a3": "格式化",
"K43934f6d": "搜索",
"K741decac": "替代",
"Kd507abff": "确定",
"Kca2d1624": "The (0) must not be negative.",
"K792b255a": "The (0) must be greater than or equal to the (1).",
"Kf0bed26d": "值枚举",
"K633a03ca": "枚举",
"Kd2766caf": "最小长度",
"Kd6d52485": "最大长度",
"Kea15f66c": "最小值",
"K1af340ff": "最大值",
"K68691e16": "将文件拖拽至此处上传,或点击选择文件上传",
"Kcec46ae": "Upload Files",
"K760fb044": "Files Selected",
"Kea2bdee0": "请填写接口名称",
"K49053438": "详细说明",
"K148f6fa4": "高级匹配",
"K3ae4c789": "转发配置",
"K2f4d0a37": "请求参数",
"Kde2d6dbd": "返回示例",
"K70e6069c": "测试 API",
"Ke4603448": "请求 Header",
"K89fd86b3": "请求 Body",
"K8747e3c4": "请求示例代码",
"K8613e6e7": "响应示例",
"Kab1c2159": "响应 Header",
"Kd2be51d1": "响应 Body",
"K831aa6c0": "申请方-应用",
"K58ca9485": "申请服务",
"K283f55b4": "服务所属系统",
"Kd60d204": "服务所属团队",
"K3a9a3b75": "审批状态",
"K4f57b2de": "审批人",
"K31dabc6b": "审批时间",
"K8582af3f": "拒绝",
"K54e27f57": "通过",
"K7eeca185": "审批完成",
"Kd4061161": "发布审批中",
"K823bfe63": "在线",
"K97ddb3f8": "-",
"Kc9315fa1": "已拒绝",
"K3fbe7511": "发布异常",
"Ke64e695c": "发布中",
"K17f93984": "API 名称",
"K1365fe45": "请求方式",
"Kc380335f": "路径",
"K4ee62e8": "该 API 缺失(0)(1)(2)请先补充",
"K385591f3": "转发信息,",
"K68415c14": "文档信息,",
"K133b75e9": "上游信息,",
"Kad98e030": "上游类型",
"Kdeed8399": "静态上游",
"K78b1ca25": "地址",
"K6208054": "待审批",
"K1be7f021": "审批通过",
"K677a4959": "发布终止",
"Kfd6d2d3d": "已关闭",
"K9d7e880a": "发布版本",
"K855135f": "创建版本时间",
"Kcbf39b82": "版本状态",
"K339d15b5": "创建人",
"K7194cea2": "审核时间",
"K69827c60": "发布状态",
"K1644b775": "新增",
"K4fdf4dcc": "变更",
"K33d66e26": "无变更",
"K9b70c007": "缺失字段",
"Kd85208a3": "驳回",
"K8adf7f8b": "审核中",
"Kad6aa439": "已订阅",
"K3118fdb0": "取消订阅",
"K9a68443b": "取消申请",
"K18307d56": "手动添加",
"K705fe9f5": "订阅申请",
"Kbc96ebec": "申请方应用",
"K1f89176d": "申请方所属团队",
"Kfe731dfc": "操作",
"K71661ee8": "必填项",
"Kcbee3f8": "不是有效邮箱地址",
"K442cfba1": "请输入",
"K3bb646e4": "请选择",
"Ka4ecfa40": "英文数字下划线任意一种,首字母必须为英文",
"K39686a7f": "支持字母开头、英文数字中横线下划线组合",
"K4d6a0190": "选择拒绝时,审批意见为必填",
"K37318b68": "无法连接集群,请检查集群地址是否正确或防火墙配置",
"K7f0c746d": "操作成功",
"K6a365d01": "操作失败",
"K978062b6": "正在操作",
"Kca7bd6d4": "正在加载数据",
"K3c93b77e": "获取数据失败",
"Ke108c369": "登录成功",
"K9168d3e": "退出成功,将跳转至登录页",
"K2f8a7ab7": "未填写审核意见",
"Kb858d78a": "复制成功",
"K26e85d15": "复制失败,请手动复制",
"K5cfdd950": "该数据删除后将无法找回,请确认是否删除?",
"K2a3f24ac": "默认工作表",
"K7e1ab4b0": "至",
"Kf1b166e7": "详情",
"K28555332": "暂不支持带有双斜杠//的url",
"Keb9fcdad": "用户名",
"Kc654b275": "邮箱",
"Kbe2ecc69": "部门",
"K759fb403": "状态",
"K52c8a730": "启用",
"K718c9310": "禁用",
"K2c5882be": "绑定域名",
"K1cc07937": "过期日期",
"K8b7c2592": "更新者",
"Keefda53d": "更新时间",
"K9f3db3ca": "集群名称",
"Ke7487049": "集群 ID",
"Kb660ffe8": "节点名称",
"Kf12b3034": "管理地址",
"K867e6faf": "服务地址",
"K37348a5e": "集群同步地址",
"K151d2bb7": "环境名称",
"Kfa744afd": "集群数量",
"K23a3bd72": "异常",
"Ke039b9b5": "正常",
"K5c123bad": "角色名称",
"K76036e25": "HTTP 请求头",
"K8d4cbf50": "Cookie",
"K44607e3f": "全等匹配",
"Kc287500a": "前缀匹配",
"Kfc0b1147": "后缀匹配",
"Ka4a92043": "子串匹配",
"K30b2e44f": "非等匹配",
"Kb1587991": "空值匹配",
"K1e97dbd8": "存在匹配",
"Kc8ee3e62": "不存在匹配",
"K87c5a801": "区分大小写的正则匹配",
"K95f062f1": "不区分大小写的正则匹配",
"Kfbd230a5": "任意匹配",
"K413b9869": "服务名称",
"K1e84ad04": "服务 ID",
"K72b0c0b3": "API 数量",
"Kf7200cd9": "负责人",
"Kefaf9956": "创建时间",
"K98db2cb9": "申请状态",
"Ke792d01c": "所属服务",
"K61b62ace": "来源",
"Ke63767cf": "添加时间",
"K3a67ea90": "订阅方",
"K442937c4": "订阅时间",
"K34111022": "协议/方法",
"K62d10724": "URL",
"Ka9481f95": "创建者",
"Kf88d56fd": "上游 ID",
"K11a92fb2": "更新人",
"K2c2bc64f": "动态服务发现",
"Kc9a2a716": "HTTPS",
"Ka7f8266f": "带权轮询",
"Kd17edabd": "IP Hash",
"Kaeba0229": "透传客户端请求 Host",
"K6d7e2fd0": "使用上游服务 Host",
"K31332633": "重写 Host",
"Ke65db976": "权重",
"Kf966b12d": "内部服务:可通过网关访问,但不展示在服务广场",
"Kfc939e49": "公开服务:可通过网关访问,展示在服务广场,可被其他应用订阅",
"Ke96ccf45": " ",
"K5582ac8": "请求路径",
"K92485dd1": "所有 API",
"Ke64e43a": "隐藏鉴权信息",
"K1a78e6f0": "过期时间",
"K40bbb0a3": "服务ID",
"K9919285b": "服务类型",
"K63938137": "公开服务",
"Kfb20a12": "内部服务",
"Kedd64e4d": "停用",
"Ka29b346f": "地址(IP 端口或域名)",
"K63b1e0dc": "权重(0-999",
"K74ab00a3": "已审批",
"Kea996156": "发布申请记录",
"Kbea7d266": "所属系统",
"Ka36c13cc": "调用系统名称",
"Kd78d73a7": "调用服务名称",
"K73c144eb": "当前系统名称",
"K285bd65e": "被调用服务名称",
"K5cbab635": "被调用系统名称",
"K93c2696e": "上线结果",
"K43fcaf94": "成功",
"Kc71c6a9": "上线失败",
"K56c686f8": "失败",
"K3ba29a85": "API",
"Kda8d5ea1": "上游",
"Kdefa9caa": "使用说明",
"K36856e71": "发布",
"K6382bbfd": "订阅管理",
"K2eef4e4": "订阅审批",
"Ka97bd9e5": "订阅方管理",
"K5974bf24": "管理",
"K3fa5c4c3": "调用拓扑图",
"Kb5c7b82d": "设置",
"K2472615e": "服务数量",
"Kc02aa5f1": "API数量",
"Ke08ff808": "添加日期",
"Kd7d84192": "姓名",
"Kc88e03b6": "团队角色",
"Kdf35c48c": "所有成员",
"K3818f03d": "审批",
"K56b4254f": "发布申请",
"Kd2c1a316": "登录",
"Kf076f63c": "请输入账号",
"K80a560a1": "账号",
"K25c895d5": "请输入密码",
"K551b0348": "密码",
"K480045ce": "Version (0)-(1)",
"Kadee8e49": "日志配置",
"K3453272": "APIPark 提供详尽的 API 调用日志,帮助企业监控、分析和审计 API 的运行状况。",
"K33c76dbc": "部门名称",
"K84829ca9": "父部门 ID",
"K4d7fc74b": "子部门名称",
"Ka16e6c44": "未激活、已禁用的成员无法加入到部门",
"Ked03ba97": "请选择成员需要新加入的部门",
"K5e237e06": "name",
"K184d3473": "添加账号",
"K1ecb35f2": "编辑成员信息",
"Ke6f00b44": "加入部门",
"K501cb1e7": "确定删除成员?此操作无法恢复,确认操作?",
"Kf20863b5": "成员与部门",
"K5f27a546": "输入用户名、邮箱查找成员",
"K26c698bb": "添加部门",
"Kb9cf2a7d": "添加子部门",
"Kc83551f5": "重命名",
"K3f1077c9": "设置成员和对应的角色,成员只能够看到权限范围内的功能和数据。",
"Kdce62a6": "搜索部门",
"K8ef69ee2": "密钥",
"Kba3507d6": "上传密钥",
"K93ac0f23": "密钥文件的后缀名一般为 .key 的文件内容",
"K7cdd1331": "上传证书",
"K6d91905d": "证书文件的后缀名一般为 .crt 或 .pem 的文件内容",
"Kd0f6ded7": "添加证书",
"Ke5732d60": "修改证书",
"K3ca07a70": "证书管理",
"Kdb927f83": "通过为 API 服务配置和管理 SSL 证书,企业可以加密数据传输,防止敏感信息被窃取或篡改。",
"K877985b7": "修改配置",
"Kdf66a675": "设置访问 API 的集群,让 API 在分布式环境中稳定运行,并且能够根据业务需求进行灵活扩展和优化。",
"Kaf074220": "未配置",
"K5878440c": "集群地址",
"K5e9022f8": "下一步",
"K8e7a0f80": "资源配置",
"Kabfe9512": "保存",
"K95c3fd8b": "设置角色的权限范围。",
"K138facd3": "系统级别角色",
"K6eac768d": "添加角色",
"Kb9c2cf02": "团队级别角色",
"Kb4ceecea": "添加子分类",
"K67479e88": "修改分类名称",
"K2bc75e2c": "添加分类",
"Kab4aab44": "重命名分类",
"K8e0e6977": "设置服务可选择的分类,方便团队成员快速找到API。",
"Ke595a20a": "分类名称",
"K9679728f": "父分类 ID",
"K9b2d08fd": "子分类名称",
"Kf14e76e5": "副本",
"K2e050340": "API 基础信息",
"K90f3c02f": "转发规则设置",
"K6ea8d549": "编辑文档",
"Kff5c18ac": "最后编辑人",
"K2eb99415": "转发规则",
"Ke93388fd": "编辑 API",
"K1b1ae3b0": "复制 API",
"K84aabfd4": "添加 API",
"K6a662463": "输入名称、URL 查找 API",
"K59bc6280": "API 详情",
"K2a16c93b": "单位:ms,最小值:1",
"K469e475a": "重试次数",
"Kd568e15c": "发布结果",
"K35f990b0": "查看详情",
"Kdbc1f6cb": "申请发布",
"Kb6860a3f": "回滚",
"Ka3494f4b": "请确认是否回滚?",
"Kb397a99f": "撤销申请",
"K7d401c0f": "请确认是否撤销申请?",
"Ke1b79b93": "终止发布",
"Ka2449180": "请确认是否终止发布?",
"K2cb02f38": "新建版本",
"Ka9c08390": "只允许上传PNG、JPG或SVG格式的图片",
"Kcf756b7a": "API 调用前缀",
"K43d101a": "选填,作为服务内所有API的前缀,比如host/{service_name}/{api_path},一旦保存无法修改",
"Kdc840242": "图标",
"K427a5bd5": "仅支持 .png .jpg .jpeg .svg 格式的图片文件, 大于 1KB 的文件将被压缩",
"K44bc352d": "Logo",
"Kf52a584d": "所属服务分类",
"K72b21be5": "设置服务展示在服务市场中的哪个分类下",
"Kde6bae17": "删除服务",
"K885ea699": "删除操作不可恢复,请谨慎操作!",
"K617f34f1": "最近一次更新者",
"K6ebca204": "最近一次更新时间",
"K39ab0358": "新增订阅方",
"K2d6658ed": "添加服务",
"K7b8f623f": "输入名称、ID、所属团队、负责人查找服务",
"Kdd9b5008": "后端默认使用的IP地址",
"K6bc47edb": "请求协议",
"Kc9acdb25": "负载均衡",
"K632dba5c": "转发 Host",
"Kc1f08a63": "重写域名",
"K628f6851": "超时时间",
"Kaff62621": "超时重试次数",
"Kc41ca30e": "调用频率限制",
"K813e1c0a": "团队名称",
"K692f5aa6": "团队 ID",
"K5de0bc2": "团队 ID(team_id)可用于检索团队,一旦保存无法修改。",
"Ka63dd985": "团队负责人",
"Ka6bcd272": "负责人对团队内的团队、服务、成员有管理权限",
"Ka2012bdd": "删除团队",
"Kbde1f3d": "服务数据清除后,方可删除",
"K395acc14": "移除成员",
"Kec46a57f": "添加成员",
"K48724410": "输入姓名查找",
"Kb9052305": "搜索用户名、邮箱",
"K5ece3bac": "设置团队和成员,然后你可以在团队内创建服务和应用、订阅API,成员只能看到所属团队内的服务和应用。",
"K510cdd27": "添加团队",
"K9244ae14": "输入名称、ID、负责人查找团队",
"Kc7b24b4b": "配置团队",
"Kecb51e2c": "旧密码",
"K8266bcf2": "新密码",
"Ka9aef039": "确认密码",
"Kcf42dcda": "两次密码不一致",
"Kf876a42d": "修改密码",
"K8ed884f": "管理个人账号",
"K9be8e1d7": "API调用统计",
"K521ab28e": "选择服务",
"Kcc8265e1": "选择API",
"K8aefc1e4": "请输入请求路径进行搜索",
"K50d471b2": "重置",
"Kee8ae330": "查询",
"Ka2c794a2": "导出",
"Kaf70c3b": "退出全屏",
"Kd22841a4": "(0)调用详情",
"K1512e983": "应用调用统计",
"Kb4d2007f": "请选择应用",
"K8c7f2d2e": "调用趋势",
"K657c3452": "(0)-(1)调用趋势",
"Kc04efb87": "调用量统计",
"Keb98266e": "加入总体数据对比",
"K18c2ed46": "(0)调用量",
"Kc3741830": "(0)调用成功率",
"Ka6aa5863": "请求总数",
"K9eaef42": "请求成功率",
"K7082a4af": "转发总数",
"K1ce386fb": "转发成功率",
"K87d6877e": "状态码4xx数",
"K4c8a54db": "状态码5xx数",
"Kd566283e": "调用总体趋势",
"Kd23a0be6": "请求报文量",
"Kec3e8361": "响应报文量",
"Ke6250744": "状态码4XX数",
"K2d79d4e1": "状态码5XX数",
"Kcf6553c6": "服务调用统计",
"Kffcfe375": "请选择服务",
"Ka65f739c": "调用详情",
"K89b7ac79": "API 请求量 Top10",
"Kc0915603": "应用调用量 Top10",
"Kf90b54": "服务被调用量 Top10",
"Kfb26388": "暂无请求统计数据",
"Kc8cbd8f8": "请求统计",
"K8dece48": "暂无转发统计数据",
"K1ee32434": "转发统计",
"Kcd125e4d": "暂无调用量统计数据",
"Kaa114e8b": "暂无报文量统计数据",
"K3ad84406": "报文量统计",
"K19a3ebe0": "请求成功数",
"Kcaa8259": "转发成功数",
"K888f038f": "失败状态码数",
"K42d2bef2": "平均响应时间(ms)",
"K9197c994": "最大响应时间(ms)",
"K7c2f3fee": "最小响应时间(ms)",
"K3d85ea54": "平均请求流量(KB)",
"Keec09d32": "最大请求流量(KB)",
"K3786b48": "最小请求流量(KB)",
"K5168eb63": "应用名称",
"K546e46f": "应用 ID",
"K4a1a14": "监控总览",
"K69741ea7": "服务被调用统计",
"K9c8d9933": "API 调用统计",
"K28cf9613": "每分钟",
"K18f25019": "每5分钟",
"Kf00f01ca": "每小时",
"Kfcda87fc": "每天",
"K29ec75dc": "每周",
"K145e4941": "亿",
"Ke6a935d": "万",
"K8f7abcab": " 次",
"K146477a8": "服务标签",
"K4de0af74": "服务分类",
"Kcce1af60": "订阅的服务",
"Kb6e9328f": "访问授权",
"Kb7e869a4": "应用管理",
"Kd59290a2": "搜索分类或标签",
"K6b75bdbc": "暂无API数据",
"Kd8a7a689": "搜索或选择应用",
"K4b15d6f5": "申请理由",
"Kb71b5a13": "鉴权类型",
"K4d1465ee": "Iss",
"K5dcd7ed8": "签名算法",
"K5b0eedd3": "Secret",
"K44f4ffe1": "RSA 公钥",
"Kc5ecd7d9": "用户名 JsonPath",
"K417d85cf": "校验字段",
"K3b82fe1d": "是否 Base64 加密",
"K49b5f4a3": "AK",
"K31418470": "SK",
"Kbfeb5297": "Apikey",
"K95764d1d": "删除应用",
"K217cb125": "鉴权详情",
"K2bb63eca": "添加鉴权",
"Kd74d69b7": "编辑鉴权",
"K9cbe1e0": "修改",
"Kd23d1716": "添加授权",
"K9dfa2c97": "永不过期",
"Kfa920c0": "到期时间",
"Kbeb4e991": "审批详情",
"Ked811bb1": "请确认是否取消订阅?",
"K50c39a62": "取消订阅申请",
"K1856c229": "请确认是否取消订阅申请?",
"K66ea2f0": "搜索服务",
"Kfeb2559b": "审批中",
"Ka2b6d281": "API 文档",
"K667bbbe7": "添加应用",
"Ka4b45550": "暂无服务描述",
"K3c7b175f": "订阅的服务数量:已通过 (0) 个,申请中 (1) 个",
"K850b4b2d": "状态码",
"Kbe3e9335": "退出测试",
"K370a3eb2": "服务市场",
"Kf7ec36d": "服务详情",
"K59cdbec3": "介绍",
"K4aa9ed2c": "申请",
"K6c060779": "服务信息",
"K8723422e": "接入应用",
"Kb97544cb": "供应方",
"Kb32f0afe": "分类",
"K81634069": "版本",
"K96a2f1c8": "无标签",
"K93d5a66e": "接入应用数量",
"K3e770a75": "鉴权 Token",
"K96059c69": "关联标签",
"K32263abd": "添加 Open Api",
"K7829bb78": "配置 Open Api",
"Kcdf76005": "Open Api",
"Ke2601944": "调用服务",
"K8504bca8": "放大",
"K693c1b41": "缩小"
}
@@ -0,0 +1,569 @@
{
"Kc0e5ef9f": "工作空间",
"K3863c722": "我的",
"Kfe93ef35": "应用",
"Kb58e0c3f": "服务",
"Kc9e489f5": "团队",
"K61c89f5f": "API 市场",
"K16d71239": "仪表盘",
"K714c192d": "运行视图",
"Kd57dfe97": "系统拓扑图",
"K3fe97dcc": "系统设置",
"K67ef3525": "组织",
"K74aef1ad": "成员",
"Kf644225f": "角色",
"K958da71f": "服务分类管理",
"Kf270ca55": "运维与集成",
"Ke93d36ed": "集群",
"K481e8a05": "证书",
"Kca53edd0": "日志",
"Kb283e720": "资源",
"K631d646f": "Open API",
"K6535ff9c": "账号设置",
"Kf15499b4": "退出登录",
"Kabbd6e6": "文档",
"K1196b104": "APIPark - 企业API数据开放平台",
"K1f42de3": "HTTP 状态码",
"K4770dff4": "系统状态码",
"Kf89e58f1": "描述",
"K9e53c664": "提交",
"Kf8e7294c": "上一步",
"Ka0451c97": "取消",
"Kb1dedda3": "关闭",
"Kb2fc7600": "添加配置",
"K4e07217d": "编辑配置",
"K4ea968fe": "编辑(0)",
"Ka7aaaeb": "添加(0)",
"Kaff78ecf": "请输入Key",
"K65d46535": "请输入Value",
"Kc14b2ea3": "返回",
"K11d3633a": "ID",
"Kbff43de3": "名称",
"K16ca79ef": "Driver",
"K7a369eef": "已发布",
"Kcfa1a4d2": "下线",
"K771dc3b7": "上线",
"K530f5951": "查看",
"Kecbd7449": "删除",
"K1cbe2507": "确认",
"K48325b6": "搜索(0)名称",
"Ka1d885c1": "添加",
"Kad207008": "编辑",
"K1ff96ff": "申请系统",
"K9bf855d6": "所属团队",
"K11b994ed": "申请人",
"K939baba7": "申请时间",
"Kdab2e63b": "版本号",
"K8b29c460": "版本说明",
"K36a72ad1": "API 列表",
"K54e44357": "上游列表",
"Kb8e8e6f5": "备注",
"K1ab0ae5b": "申请原因",
"K53c00c3c": "审核意见",
"K7edf331d": "时间",
"Kef45b208": "近1小时",
"K9dbf22b8": "近24小时",
"K820fbfab": "近3天",
"Kd6d28fc": "近7天",
"K23fda291": "暂无操作权限,请联系管理员分配。",
"K4618cb0a": "微信小程序",
"Ka854f511": "获取文件,需填路径",
"Kaa11a695": "暂不支持生成非 HTTPS 或非 HTTP 协议的代码示例",
"Kbe46924e": "搜索编程语言...",
"Ke8e4f258": "编程语言",
"K29c07a47": "成功示例",
"K1f5c814d": "失败示例",
"K4ef022d7": "默认 text/html;charset=UTF-8",
"Kd061b5bf": "暂未填写示例",
"Kc14cec33": "Binary",
"K48b4d9e3": "请求头部",
"Kcd347eaf": "请求体",
"K9e100bfe": "Query 参数",
"K3e9f12fd": "REST 参数",
"K2bfa290c": "api request editor",
"Kb36d111a": "返回头部",
"K980bde79": "返回值",
"Kb04d201a": "更多设置",
"Kee74f5b4": "添加子参数",
"Kc7d3106c": "向下添加行",
"Keaabd222": "标签",
"K8ad2c50e": "参数名",
"K67d68dd1": "类型",
"K29245f47": "必需",
"Ke32cbcd3": "示例",
"Kc13936c6": "输入 URL 或 cURL",
"Ka1ede006": "HTTP",
"K152ac44e": "参数位置",
"K1660ae72": "匹配类型",
"K91ced765": "参数值",
"K5b265628": "操作类型",
"K1826982d": "新增或修改",
"Kd65b55f5": "匹配参数值",
"K15f35bf2": "转发上游路径",
"K79dec0dd": "请求超时时间",
"K7d465645": "绑定上游服务",
"K63a6404d": "重试时间",
"K47740727": "转发上游请求头",
"K2b605d42": "More",
"K1df9fbd5": "导入",
"K5e85df18": "导入格式",
"K9eaf7885": "全量替换",
"Kf8c3a80b": "在末端插入",
"Kd96b2d7d": "增量更新",
"Kf2fc08eb": "请求头",
"Ka45f1d8": "Rest 参数",
"K94bb113a": "大小",
"K359919b5": "另存为文件",
"K38bf1b90": "响应",
"K59f4186e": "响应头",
"K5f1e23fd": "正文",
"Kf404ef7d": "发送(Enter)",
"K2dbfd648": "中止",
"Kacabc771": "秒",
"K13ae6a93": "复制",
"Ke54a14a3": "格式化",
"K43934f6d": "搜索",
"K741decac": "替代",
"Kd507abff": "确定",
"Kca2d1624": "The (0) must not be negative.",
"K792b255a": "The (0) must be greater than or equal to the (1).",
"Kf0bed26d": "值枚举",
"K633a03ca": "枚举",
"Kd2766caf": "最小长度",
"Kd6d52485": "最大长度",
"Kea15f66c": "最小值",
"K1af340ff": "最大值",
"K68691e16": "将文件拖拽至此处上传,或点击选择文件上传",
"Kcec46ae": "Upload Files",
"K760fb044": "Files Selected",
"Kea2bdee0": "请填写接口名称",
"K49053438": "详细说明",
"K148f6fa4": "高级匹配",
"K3ae4c789": "转发配置",
"K2f4d0a37": "请求参数",
"Kde2d6dbd": "返回示例",
"K70e6069c": "测试 API",
"Ke4603448": "请求 Header",
"K89fd86b3": "请求 Body",
"K8747e3c4": "请求示例代码",
"K8613e6e7": "响应示例",
"Kab1c2159": "响应 Header",
"Kd2be51d1": "响应 Body",
"K831aa6c0": "申请方-应用",
"K58ca9485": "申请服务",
"K283f55b4": "服务所属系统",
"Kd60d204": "服务所属团队",
"K3a9a3b75": "审批状态",
"K4f57b2de": "审批人",
"K31dabc6b": "审批时间",
"K8582af3f": "拒绝",
"K54e27f57": "通过",
"K7eeca185": "审批完成",
"Kd4061161": "发布审批中",
"K823bfe63": "在线",
"K97ddb3f8": "-",
"Kc9315fa1": "已拒绝",
"K3fbe7511": "发布异常",
"Ke64e695c": "发布中",
"K17f93984": "API 名称",
"K1365fe45": "请求方式",
"Kc380335f": "路径",
"K4ee62e8": "该 API 缺失(0)(1)(2)请先补充",
"K385591f3": "转发信息,",
"K68415c14": "文档信息,",
"K133b75e9": "上游信息,",
"Kad98e030": "上游类型",
"Kdeed8399": "静态上游",
"K78b1ca25": "地址",
"K6208054": "待审批",
"K1be7f021": "审批通过",
"K677a4959": "发布终止",
"Kfd6d2d3d": "已关闭",
"K9d7e880a": "发布版本",
"K855135f": "创建版本时间",
"Kcbf39b82": "版本状态",
"K339d15b5": "创建人",
"K7194cea2": "审核时间",
"K69827c60": "发布状态",
"K1644b775": "新增",
"K4fdf4dcc": "变更",
"K33d66e26": "无变更",
"K9b70c007": "缺失字段",
"Kd85208a3": "驳回",
"K8adf7f8b": "审核中",
"Kad6aa439": "已订阅",
"K3118fdb0": "取消订阅",
"K9a68443b": "取消申请",
"K18307d56": "手动添加",
"K705fe9f5": "订阅申请",
"Kbc96ebec": "申请方应用",
"K1f89176d": "申请方所属团队",
"Kfe731dfc": "操作",
"K71661ee8": "必填项",
"Kcbee3f8": "不是有效邮箱地址",
"K442cfba1": "请输入",
"K3bb646e4": "请选择",
"Ka4ecfa40": "英文数字下划线任意一种,首字母必须为英文",
"K39686a7f": "支持字母开头、英文数字中横线下划线组合",
"K4d6a0190": "选择拒绝时,审批意见为必填",
"K37318b68": "无法连接集群,请检查集群地址是否正确或防火墙配置",
"K7f0c746d": "操作成功",
"K6a365d01": "操作失败",
"K978062b6": "正在操作",
"Kca7bd6d4": "正在加载数据",
"K3c93b77e": "获取数据失败",
"Ke108c369": "登录成功",
"K9168d3e": "退出成功,将跳转至登录页",
"K2f8a7ab7": "未填写审核意见",
"Kb858d78a": "复制成功",
"K26e85d15": "复制失败,请手动复制",
"K5cfdd950": "该数据删除后将无法找回,请确认是否删除?",
"K2a3f24ac": "默认工作表",
"K7e1ab4b0": "至",
"Kf1b166e7": "详情",
"K28555332": "暂不支持带有双斜杠//的url",
"Keb9fcdad": "用户名",
"Kc654b275": "邮箱",
"Kbe2ecc69": "部门",
"K759fb403": "状态",
"K52c8a730": "启用",
"K718c9310": "禁用",
"K2c5882be": "绑定域名",
"K1cc07937": "过期日期",
"K8b7c2592": "更新者",
"Keefda53d": "更新时间",
"K9f3db3ca": "集群名称",
"Ke7487049": "集群 ID",
"Kb660ffe8": "节点名称",
"Kf12b3034": "管理地址",
"K867e6faf": "服务地址",
"K37348a5e": "集群同步地址",
"K151d2bb7": "环境名称",
"Kfa744afd": "集群数量",
"K23a3bd72": "异常",
"Ke039b9b5": "正常",
"K5c123bad": "角色名称",
"K76036e25": "HTTP 请求头",
"K8d4cbf50": "Cookie",
"K44607e3f": "全等匹配",
"Kc287500a": "前缀匹配",
"Kfc0b1147": "后缀匹配",
"Ka4a92043": "子串匹配",
"K30b2e44f": "非等匹配",
"Kb1587991": "空值匹配",
"K1e97dbd8": "存在匹配",
"Kc8ee3e62": "不存在匹配",
"K87c5a801": "区分大小写的正则匹配",
"K95f062f1": "不区分大小写的正则匹配",
"Kfbd230a5": "任意匹配",
"K413b9869": "服务名称",
"K1e84ad04": "服务 ID",
"K72b0c0b3": "API 数量",
"Kf7200cd9": "负责人",
"Kefaf9956": "创建时间",
"K98db2cb9": "申请状态",
"Ke792d01c": "所属服务",
"K61b62ace": "来源",
"Ke63767cf": "添加时间",
"K3a67ea90": "订阅方",
"K442937c4": "订阅时间",
"K34111022": "协议/方法",
"K62d10724": "URL",
"Ka9481f95": "创建者",
"Kf88d56fd": "上游 ID",
"K11a92fb2": "更新人",
"K2c2bc64f": "动态服务发现",
"Kc9a2a716": "HTTPS",
"Ka7f8266f": "带权轮询",
"Kd17edabd": "IP Hash",
"Kaeba0229": "透传客户端请求 Host",
"K6d7e2fd0": "使用上游服务 Host",
"K31332633": "重写 Host",
"Ke65db976": "权重",
"Kf966b12d": "内部服务:可通过网关访问,但不展示在服务广场",
"Kfc939e49": "公开服务:可通过网关访问,展示在服务广场,可被其他应用订阅",
"Ke96ccf45": " ",
"K5582ac8": "请求路径",
"K92485dd1": "所有 API",
"Ke64e43a": "隐藏鉴权信息",
"K1a78e6f0": "过期时间",
"K40bbb0a3": "服务ID",
"K9919285b": "服务类型",
"K63938137": "公开服务",
"Kfb20a12": "内部服务",
"Kedd64e4d": "停用",
"Ka29b346f": "地址(IP 端口或域名)",
"K63b1e0dc": "权重(0-999",
"K74ab00a3": "已审批",
"Kea996156": "发布申请记录",
"Kbea7d266": "所属系统",
"Ka36c13cc": "调用系统名称",
"Kd78d73a7": "调用服务名称",
"K73c144eb": "当前系统名称",
"K285bd65e": "被调用服务名称",
"K5cbab635": "被调用系统名称",
"K93c2696e": "上线结果",
"K43fcaf94": "成功",
"Kc71c6a9": "上线失败",
"K56c686f8": "失败",
"K3ba29a85": "API",
"Kda8d5ea1": "上游",
"Kdefa9caa": "使用说明",
"K36856e71": "发布",
"K6382bbfd": "订阅管理",
"K2eef4e4": "订阅审批",
"Ka97bd9e5": "订阅方管理",
"K5974bf24": "管理",
"K3fa5c4c3": "调用拓扑图",
"Kb5c7b82d": "设置",
"K2472615e": "服务数量",
"Kc02aa5f1": "API数量",
"Ke08ff808": "添加日期",
"Kd7d84192": "姓名",
"Kc88e03b6": "团队角色",
"Kdf35c48c": "所有成员",
"K3818f03d": "审批",
"K56b4254f": "发布申请",
"Kd2c1a316": "登录",
"Kf076f63c": "请输入账号",
"K80a560a1": "账号",
"K25c895d5": "请输入密码",
"K551b0348": "密码",
"K480045ce": "Version (0)-(1)",
"Kadee8e49": "日志配置",
"K3453272": "APIPark 提供详尽的 API 调用日志,帮助企业监控、分析和审计 API 的运行状况。",
"K33c76dbc": "部门名称",
"K84829ca9": "父部门 ID",
"K4d7fc74b": "子部门名称",
"Ka16e6c44": "未激活、已禁用的成员无法加入到部门",
"Ked03ba97": "请选择成员需要新加入的部门",
"K5e237e06": "name",
"K184d3473": "添加账号",
"K1ecb35f2": "编辑成员信息",
"Ke6f00b44": "加入部门",
"K501cb1e7": "确定删除成员?此操作无法恢复,确认操作?",
"Kf20863b5": "成员与部门",
"K5f27a546": "输入用户名、邮箱查找成员",
"K26c698bb": "添加部门",
"Kb9cf2a7d": "添加子部门",
"Kc83551f5": "重命名",
"K3f1077c9": "设置成员和对应的角色,成员只能够看到权限范围内的功能和数据。",
"Kdce62a6": "搜索部门",
"K8ef69ee2": "密钥",
"Kba3507d6": "上传密钥",
"K93ac0f23": "密钥文件的后缀名一般为 .key 的文件内容",
"K7cdd1331": "上传证书",
"K6d91905d": "证书文件的后缀名一般为 .crt 或 .pem 的文件内容",
"Kd0f6ded7": "添加证书",
"Ke5732d60": "修改证书",
"K3ca07a70": "证书管理",
"Kdb927f83": "通过为 API 服务配置和管理 SSL 证书,企业可以加密数据传输,防止敏感信息被窃取或篡改。",
"K877985b7": "修改配置",
"Kdf66a675": "设置访问 API 的集群,让 API 在分布式环境中稳定运行,并且能够根据业务需求进行灵活扩展和优化。",
"Kaf074220": "未配置",
"K5878440c": "集群地址",
"K5e9022f8": "下一步",
"K8e7a0f80": "资源配置",
"Kabfe9512": "保存",
"K95c3fd8b": "设置角色的权限范围。",
"K138facd3": "系统级别角色",
"K6eac768d": "添加角色",
"Kb9c2cf02": "团队级别角色",
"Kb4ceecea": "添加子分类",
"K67479e88": "修改分类名称",
"K2bc75e2c": "添加分类",
"Kab4aab44": "重命名分类",
"K8e0e6977": "设置服务可选择的分类,方便团队成员快速找到API。",
"Ke595a20a": "分类名称",
"K9679728f": "父分类 ID",
"K9b2d08fd": "子分类名称",
"Kf14e76e5": "副本",
"K2e050340": "API 基础信息",
"K90f3c02f": "转发规则设置",
"K6ea8d549": "编辑文档",
"Kff5c18ac": "最后编辑人",
"K2eb99415": "转发规则",
"Ke93388fd": "编辑 API",
"K1b1ae3b0": "复制 API",
"K84aabfd4": "添加 API",
"K6a662463": "输入名称、URL 查找 API",
"K59bc6280": "API 详情",
"K2a16c93b": "单位:ms,最小值:1",
"K469e475a": "重试次数",
"Kd568e15c": "发布结果",
"K35f990b0": "查看详情",
"Kdbc1f6cb": "申请发布",
"Kb6860a3f": "回滚",
"Ka3494f4b": "请确认是否回滚?",
"Kb397a99f": "撤销申请",
"K7d401c0f": "请确认是否撤销申请?",
"Ke1b79b93": "终止发布",
"Ka2449180": "请确认是否终止发布?",
"K2cb02f38": "新建版本",
"Ka9c08390": "只允许上传PNG、JPG或SVG格式的图片",
"Kcf756b7a": "API 调用前缀",
"K43d101a": "选填,作为服务内所有API的前缀,比如host/{service_name}/{api_path},一旦保存无法修改",
"Kdc840242": "图标",
"K427a5bd5": "仅支持 .png .jpg .jpeg .svg 格式的图片文件, 大于 1KB 的文件将被压缩",
"K44bc352d": "Logo",
"Kf52a584d": "所属服务分类",
"K72b21be5": "设置服务展示在服务市场中的哪个分类下",
"Kde6bae17": "删除服务",
"K885ea699": "删除操作不可恢复,请谨慎操作!",
"K617f34f1": "最近一次更新者",
"K6ebca204": "最近一次更新时间",
"K39ab0358": "新增订阅方",
"K2d6658ed": "添加服务",
"K7b8f623f": "输入名称、ID、所属团队、负责人查找服务",
"Kdd9b5008": "后端默认使用的IP地址",
"K6bc47edb": "请求协议",
"Kc9acdb25": "负载均衡",
"K632dba5c": "转发 Host",
"Kc1f08a63": "重写域名",
"K628f6851": "超时时间",
"Kaff62621": "超时重试次数",
"Kc41ca30e": "调用频率限制",
"K813e1c0a": "团队名称",
"K692f5aa6": "团队 ID",
"K5de0bc2": "团队 ID(team_id)可用于检索团队,一旦保存无法修改。",
"Ka63dd985": "团队负责人",
"Ka6bcd272": "负责人对团队内的团队、服务、成员有管理权限",
"Ka2012bdd": "删除团队",
"Kbde1f3d": "服务数据清除后,方可删除",
"K395acc14": "移除成员",
"Kec46a57f": "添加成员",
"K48724410": "输入姓名查找",
"Kb9052305": "搜索用户名、邮箱",
"K5ece3bac": "设置团队和成员,然后你可以在团队内创建服务和应用、订阅API,成员只能看到所属团队内的服务和应用。",
"K510cdd27": "添加团队",
"K9244ae14": "输入名称、ID、负责人查找团队",
"Kc7b24b4b": "配置团队",
"Kecb51e2c": "旧密码",
"K8266bcf2": "新密码",
"Ka9aef039": "确认密码",
"Kcf42dcda": "两次密码不一致",
"Kf876a42d": "修改密码",
"K8ed884f": "管理个人账号",
"K9be8e1d7": "API调用统计",
"K521ab28e": "选择服务",
"Kcc8265e1": "选择API",
"K8aefc1e4": "请输入请求路径进行搜索",
"K50d471b2": "重置",
"Kee8ae330": "查询",
"Ka2c794a2": "导出",
"Kaf70c3b": "退出全屏",
"Kd22841a4": "(0)调用详情",
"K1512e983": "应用调用统计",
"Kb4d2007f": "请选择应用",
"K8c7f2d2e": "调用趋势",
"K657c3452": "(0)-(1)调用趋势",
"Kc04efb87": "调用量统计",
"Keb98266e": "加入总体数据对比",
"K18c2ed46": "(0)调用量",
"Kc3741830": "(0)调用成功率",
"Ka6aa5863": "请求总数",
"K9eaef42": "请求成功率",
"K7082a4af": "转发总数",
"K1ce386fb": "转发成功率",
"K87d6877e": "状态码4xx数",
"K4c8a54db": "状态码5xx数",
"Kd566283e": "调用总体趋势",
"Kd23a0be6": "请求报文量",
"Kec3e8361": "响应报文量",
"Ke6250744": "状态码4XX数",
"K2d79d4e1": "状态码5XX数",
"Kcf6553c6": "服务调用统计",
"Kffcfe375": "请选择服务",
"Ka65f739c": "调用详情",
"K89b7ac79": "API 请求量 Top10",
"Kc0915603": "应用调用量 Top10",
"Kf90b54": "服务被调用量 Top10",
"Kfb26388": "暂无请求统计数据",
"Kc8cbd8f8": "请求统计",
"K8dece48": "暂无转发统计数据",
"K1ee32434": "转发统计",
"Kcd125e4d": "暂无调用量统计数据",
"Kaa114e8b": "暂无报文量统计数据",
"K3ad84406": "报文量统计",
"K19a3ebe0": "请求成功数",
"Kcaa8259": "转发成功数",
"K888f038f": "失败状态码数",
"K42d2bef2": "平均响应时间(ms)",
"K9197c994": "最大响应时间(ms)",
"K7c2f3fee": "最小响应时间(ms)",
"K3d85ea54": "平均请求流量(KB)",
"Keec09d32": "最大请求流量(KB)",
"K3786b48": "最小请求流量(KB)",
"K5168eb63": "应用名称",
"K546e46f": "应用 ID",
"K4a1a14": "监控总览",
"K69741ea7": "服务被调用统计",
"K9c8d9933": "API 调用统计",
"K28cf9613": "每分钟",
"K18f25019": "每5分钟",
"Kf00f01ca": "每小时",
"Kfcda87fc": "每天",
"K29ec75dc": "每周",
"K145e4941": "亿",
"Ke6a935d": "万",
"K8f7abcab": " 次",
"K146477a8": "服务标签",
"K4de0af74": "服务分类",
"Kcce1af60": "订阅的服务",
"Kb6e9328f": "访问授权",
"Kb7e869a4": "应用管理",
"Kd59290a2": "搜索分类或标签",
"K6b75bdbc": "暂无API数据",
"Kd8a7a689": "搜索或选择应用",
"K4b15d6f5": "申请理由",
"Kb71b5a13": "鉴权类型",
"K4d1465ee": "Iss",
"K5dcd7ed8": "签名算法",
"K5b0eedd3": "Secret",
"K44f4ffe1": "RSA 公钥",
"Kc5ecd7d9": "用户名 JsonPath",
"K417d85cf": "校验字段",
"K3b82fe1d": "是否 Base64 加密",
"K49b5f4a3": "AK",
"K31418470": "SK",
"Kbfeb5297": "Apikey",
"K95764d1d": "删除应用",
"K217cb125": "鉴权详情",
"K2bb63eca": "添加鉴权",
"Kd74d69b7": "编辑鉴权",
"K9cbe1e0": "修改",
"Kd23d1716": "添加授权",
"K9dfa2c97": "永不过期",
"Kfa920c0": "到期时间",
"Kbeb4e991": "审批详情",
"Ked811bb1": "请确认是否取消订阅?",
"K50c39a62": "取消订阅申请",
"K1856c229": "请确认是否取消订阅申请?",
"K66ea2f0": "搜索服务",
"Kfeb2559b": "审批中",
"Ka2b6d281": "API 文档",
"K667bbbe7": "添加应用",
"Ka4b45550": "暂无服务描述",
"K3c7b175f": "订阅的服务数量:已通过 (0) 个,申请中 (1) 个",
"K850b4b2d": "状态码",
"Kbe3e9335": "退出测试",
"K370a3eb2": "服务市场",
"Kf7ec36d": "服务详情",
"K59cdbec3": "介绍",
"K4aa9ed2c": "申请",
"K6c060779": "服务信息",
"K8723422e": "接入应用",
"Kb97544cb": "供应方",
"Kb32f0afe": "分类",
"K81634069": "版本",
"K96a2f1c8": "无标签",
"K93d5a66e": "接入应用数量",
"K3e770a75": "鉴权 Token",
"K96059c69": "关联标签",
"K32263abd": "添加 Open Api",
"K7829bb78": "配置 Open Api",
"Kcdf76005": "Open Api",
"Ke2601944": "调用服务",
"K8504bca8": "放大",
"K693c1b41": "缩小"
}
@@ -0,0 +1,570 @@
{
"Kc0e5ef9f": "工作空间",
"K48325b6": "搜索(0)名称",
"K3d7465f7":"文件日志",
"K3863c722": "我的",
"Kfe93ef35": "应用",
"Kb58e0c3f": "服务",
"Kc9e489f5": "团队",
"K61c89f5f": "API 市场",
"K16d71239": "仪表盘",
"K714c192d": "运行视图",
"Kd57dfe97": "系统拓扑图",
"K3fe97dcc": "系统设置",
"K67ef3525": "组织",
"K74aef1ad": "成员",
"Kf644225f": "角色",
"K958da71f": "服务分类管理",
"Kf270ca55": "运维与集成",
"Ke93d36ed": "集群",
"K481e8a05": "证书",
"Kca53edd0": "日志",
"Kb283e720": "资源",
"K631d646f": "Open API",
"K6535ff9c": "账号设置",
"Kf15499b4": "退出登录",
"Kabbd6e6": "文档",
"K1196b104": "APIPark - 企业API数据开放平台",
"K1f42de3": "HTTP 状态码",
"K4770dff4": "系统状态码",
"Kf89e58f1": "描述",
"K9e53c664": "提交",
"Kf8e7294c": "上一步",
"Ka0451c97": "取消",
"Kb1dedda3": "关闭",
"Kb2fc7600": "添加配置",
"K4e07217d": "编辑配置",
"K4ea968fe": "编辑(0)",
"Ka7aaaeb": "添加(0)",
"Kaff78ecf": "请输入Key",
"K65d46535": "请输入Value",
"Kc14b2ea3": "返回",
"K11d3633a": "ID",
"Kbff43de3": "名称",
"K16ca79ef": "Driver",
"K7a369eef": "已发布",
"Kcfa1a4d2": "下线",
"K771dc3b7": "上线",
"K530f5951": "查看",
"Kecbd7449": "删除",
"K1cbe2507": "确认",
"Ka1d885c1": "添加",
"Kad207008": "编辑",
"K1ff96ff": "申请系统",
"K9bf855d6": "所属团队",
"K11b994ed": "申请人",
"K939baba7": "申请时间",
"Kdab2e63b": "版本号",
"K8b29c460": "版本说明",
"K36a72ad1": "API 列表",
"K54e44357": "上游列表",
"Kb8e8e6f5": "备注",
"K1ab0ae5b": "申请原因",
"K53c00c3c": "审核意见",
"K7edf331d": "时间",
"Kef45b208": "近1小时",
"K9dbf22b8": "近24小时",
"K820fbfab": "近3天",
"Kd6d28fc": "近7天",
"K23fda291": "暂无操作权限,请联系管理员分配。",
"K4618cb0a": "微信小程序",
"Ka854f511": "获取文件,需填路径",
"Kaa11a695": "暂不支持生成非 HTTPS 或非 HTTP 协议的代码示例",
"Kbe46924e": "搜索编程语言...",
"Ke8e4f258": "编程语言",
"K29c07a47": "成功示例",
"K1f5c814d": "失败示例",
"K4ef022d7": "默认 text/html;charset=UTF-8",
"Kd061b5bf": "暂未填写示例",
"Kc14cec33": "Binary",
"K48b4d9e3": "请求头部",
"Kcd347eaf": "请求体",
"K9e100bfe": "Query 参数",
"K3e9f12fd": "REST 参数",
"K2bfa290c": "api request editor",
"Kb36d111a": "返回头部",
"K980bde79": "返回值",
"Kb04d201a": "更多设置",
"Kee74f5b4": "添加子参数",
"Kc7d3106c": "向下添加行",
"Keaabd222": "标签",
"K8ad2c50e": "参数名",
"K67d68dd1": "类型",
"K29245f47": "必需",
"Ke32cbcd3": "示例",
"Kc13936c6": "输入 URL 或 cURL",
"Ka1ede006": "HTTP",
"K152ac44e": "参数位置",
"K1660ae72": "匹配类型",
"K91ced765": "参数值",
"K5b265628": "操作类型",
"K1826982d": "新增或修改",
"Kd65b55f5": "匹配参数值",
"K15f35bf2": "转发上游路径",
"K79dec0dd": "请求超时时间",
"K7d465645": "绑定上游服务",
"K63a6404d": "重试时间",
"K47740727": "转发上游请求头",
"K2b605d42": "More",
"K1df9fbd5": "导入",
"K5e85df18": "导入格式",
"K9eaf7885": "全量替换",
"Kf8c3a80b": "在末端插入",
"Kd96b2d7d": "增量更新",
"Kf2fc08eb": "请求头",
"Ka45f1d8": "Rest 参数",
"K94bb113a": "大小",
"K359919b5": "另存为文件",
"K38bf1b90": "响应",
"K59f4186e": "响应头",
"K5f1e23fd": "正文",
"Kf404ef7d": "发送(Enter)",
"K2dbfd648": "中止",
"Kacabc771": "秒",
"K13ae6a93": "复制",
"Ke54a14a3": "格式化",
"K43934f6d": "搜索",
"K741decac": "替代",
"Kd507abff": "确定",
"Kca2d1624": "The (0) must not be negative.",
"K792b255a": "The (0) must be greater than or equal to the (1).",
"Kf0bed26d": "值枚举",
"K633a03ca": "枚举",
"Kd2766caf": "最小长度",
"Kd6d52485": "最大长度",
"Kea15f66c": "最小值",
"K1af340ff": "最大值",
"K68691e16": "将文件拖拽至此处上传,或点击选择文件上传",
"Kcec46ae": "Upload Files",
"K760fb044": "Files Selected",
"Kea2bdee0": "请填写接口名称",
"K49053438": "详细说明",
"K148f6fa4": "高级匹配",
"K3ae4c789": "转发配置",
"K2f4d0a37": "请求参数",
"Kde2d6dbd": "返回示例",
"K70e6069c": "测试 API",
"Ke4603448": "请求 Header",
"K89fd86b3": "请求 Body",
"K8747e3c4": "请求示例代码",
"K8613e6e7": "响应示例",
"Kab1c2159": "响应 Header",
"Kd2be51d1": "响应 Body",
"K831aa6c0": "申请方-应用",
"K58ca9485": "申请服务",
"K283f55b4": "服务所属系统",
"Kd60d204": "服务所属团队",
"K3a9a3b75": "审批状态",
"K4f57b2de": "审批人",
"K31dabc6b": "审批时间",
"K8582af3f": "拒绝",
"K54e27f57": "通过",
"K7eeca185": "审批完成",
"Kd4061161": "发布审批中",
"K823bfe63": "在线",
"K97ddb3f8": "-",
"Kc9315fa1": "已拒绝",
"K3fbe7511": "发布异常",
"Ke64e695c": "发布中",
"K17f93984": "API 名称",
"K1365fe45": "请求方式",
"Kc380335f": "路径",
"K4ee62e8": "该 API 缺失(0)(1)(2)请先补充",
"K385591f3": "转发信息,",
"K68415c14": "文档信息,",
"K133b75e9": "上游信息,",
"Kad98e030": "上游类型",
"Kdeed8399": "静态上游",
"K78b1ca25": "地址",
"K6208054": "待审批",
"K1be7f021": "审批通过",
"K677a4959": "发布终止",
"Kfd6d2d3d": "已关闭",
"K9d7e880a": "发布版本",
"K855135f": "创建版本时间",
"Kcbf39b82": "版本状态",
"K339d15b5": "创建人",
"K7194cea2": "审核时间",
"K69827c60": "发布状态",
"K1644b775": "新增",
"K4fdf4dcc": "变更",
"K33d66e26": "无变更",
"K9b70c007": "缺失字段",
"Kd85208a3": "驳回",
"K8adf7f8b": "审核中",
"Kad6aa439": "已订阅",
"K3118fdb0": "取消订阅",
"K9a68443b": "取消申请",
"K18307d56": "手动添加",
"K705fe9f5": "订阅申请",
"Kbc96ebec": "申请方应用",
"K1f89176d": "申请方所属团队",
"Kfe731dfc": "操作",
"K71661ee8": "必填项",
"Kcbee3f8": "不是有效邮箱地址",
"K442cfba1": "请输入",
"K3bb646e4": "请选择",
"Ka4ecfa40": "英文数字下划线任意一种,首字母必须为英文",
"K39686a7f": "支持字母开头、英文数字中横线下划线组合",
"K4d6a0190": "选择拒绝时,审批意见为必填",
"K37318b68": "无法连接集群,请检查集群地址是否正确或防火墙配置",
"K7f0c746d": "操作成功",
"K6a365d01": "操作失败",
"K978062b6": "正在操作",
"Kca7bd6d4": "正在加载数据",
"K3c93b77e": "获取数据失败",
"Ke108c369": "登录成功",
"K9168d3e": "退出成功,将跳转至登录页",
"K2f8a7ab7": "未填写审核意见",
"Kb858d78a": "复制成功",
"K26e85d15": "复制失败,请手动复制",
"K5cfdd950": "该数据删除后将无法找回,请确认是否删除?",
"K2a3f24ac": "默认工作表",
"K7e1ab4b0": "至",
"Kf1b166e7": "详情",
"K28555332": "暂不支持带有双斜杠//的url",
"Keb9fcdad": "用户名",
"Kc654b275": "邮箱",
"Kbe2ecc69": "部门",
"K759fb403": "状态",
"K52c8a730": "启用",
"K718c9310": "禁用",
"K2c5882be": "绑定域名",
"K1cc07937": "过期日期",
"K8b7c2592": "更新者",
"Keefda53d": "更新时间",
"K9f3db3ca": "集群名称",
"Ke7487049": "集群 ID",
"Kb660ffe8": "节点名称",
"Kf12b3034": "管理地址",
"K867e6faf": "服务地址",
"K37348a5e": "集群同步地址",
"K151d2bb7": "环境名称",
"Kfa744afd": "集群数量",
"K23a3bd72": "异常",
"Ke039b9b5": "正常",
"K5c123bad": "角色名称",
"K76036e25": "HTTP 请求头",
"K8d4cbf50": "Cookie",
"K44607e3f": "全等匹配",
"Kc287500a": "前缀匹配",
"Kfc0b1147": "后缀匹配",
"Ka4a92043": "子串匹配",
"K30b2e44f": "非等匹配",
"Kb1587991": "空值匹配",
"K1e97dbd8": "存在匹配",
"Kc8ee3e62": "不存在匹配",
"K87c5a801": "区分大小写的正则匹配",
"K95f062f1": "不区分大小写的正则匹配",
"Kfbd230a5": "任意匹配",
"K413b9869": "服务名称",
"K1e84ad04": "服务 ID",
"K72b0c0b3": "API 数量",
"Kf7200cd9": "负责人",
"Kefaf9956": "创建时间",
"K98db2cb9": "申请状态",
"Ke792d01c": "所属服务",
"K61b62ace": "来源",
"Ke63767cf": "添加时间",
"K3a67ea90": "订阅方",
"K442937c4": "订阅时间",
"K34111022": "协议/方法",
"K62d10724": "URL",
"Ka9481f95": "创建者",
"Kf88d56fd": "上游 ID",
"K11a92fb2": "更新人",
"K2c2bc64f": "动态服务发现",
"Kc9a2a716": "HTTPS",
"Ka7f8266f": "带权轮询",
"Kd17edabd": "IP Hash",
"Kaeba0229": "透传客户端请求 Host",
"K6d7e2fd0": "使用上游服务 Host",
"K31332633": "重写 Host",
"Ke65db976": "权重",
"Kf966b12d": "内部服务:可通过网关访问,但不展示在服务广场",
"Kfc939e49": "公开服务:可通过网关访问,展示在服务广场,可被其他应用订阅",
"Ke96ccf45": " ",
"K5582ac8": "请求路径",
"K92485dd1": "所有 API",
"Ke64e43a": "隐藏鉴权信息",
"K1a78e6f0": "过期时间",
"K40bbb0a3": "服务ID",
"K9919285b": "服务类型",
"K63938137": "公开服务",
"Kfb20a12": "内部服务",
"Kedd64e4d": "停用",
"Ka29b346f": "地址(IP 端口或域名)",
"K63b1e0dc": "权重(0-999",
"K74ab00a3": "已审批",
"Kea996156": "发布申请记录",
"Kbea7d266": "所属系统",
"Ka36c13cc": "调用系统名称",
"Kd78d73a7": "调用服务名称",
"K73c144eb": "当前系统名称",
"K285bd65e": "被调用服务名称",
"K5cbab635": "被调用系统名称",
"K93c2696e": "上线结果",
"K43fcaf94": "成功",
"Kc71c6a9": "上线失败",
"K56c686f8": "失败",
"K3ba29a85": "API",
"Kda8d5ea1": "上游",
"Kdefa9caa": "使用说明",
"K36856e71": "发布",
"K6382bbfd": "订阅管理",
"K2eef4e4": "订阅审批",
"Ka97bd9e5": "订阅方管理",
"K5974bf24": "管理",
"K3fa5c4c3": "调用拓扑图",
"Kb5c7b82d": "设置",
"K2472615e": "服务数量",
"Kc02aa5f1": "API数量",
"Ke08ff808": "添加日期",
"Kd7d84192": "姓名",
"Kc88e03b6": "团队角色",
"Kdf35c48c": "所有成员",
"K3818f03d": "审批",
"K56b4254f": "发布申请",
"Kd2c1a316": "登录",
"Kf076f63c": "请输入账号",
"K80a560a1": "账号",
"K25c895d5": "请输入密码",
"K551b0348": "密码",
"K480045ce": "Version (0)-(1)",
"Kadee8e49": "日志配置",
"K3453272": "APIPark 提供详尽的 API 调用日志,帮助企业监控、分析和审计 API 的运行状况。",
"K33c76dbc": "部门名称",
"K84829ca9": "父部门 ID",
"K4d7fc74b": "子部门名称",
"Ka16e6c44": "未激活、已禁用的成员无法加入到部门",
"Ked03ba97": "请选择成员需要新加入的部门",
"K5e237e06": "name",
"K184d3473": "添加账号",
"K1ecb35f2": "编辑成员信息",
"Ke6f00b44": "加入部门",
"K501cb1e7": "确定删除成员?此操作无法恢复,确认操作?",
"Kf20863b5": "成员与部门",
"K5f27a546": "输入用户名、邮箱查找成员",
"K26c698bb": "添加部门",
"Kb9cf2a7d": "添加子部门",
"Kc83551f5": "重命名",
"K3f1077c9": "设置成员和对应的角色,成员只能够看到权限范围内的功能和数据。",
"Kdce62a6": "搜索部门",
"K8ef69ee2": "密钥",
"Kba3507d6": "上传密钥",
"K93ac0f23": "密钥文件的后缀名一般为 .key 的文件内容",
"K7cdd1331": "上传证书",
"K6d91905d": "证书文件的后缀名一般为 .crt 或 .pem 的文件内容",
"Kd0f6ded7": "添加证书",
"Ke5732d60": "修改证书",
"K3ca07a70": "证书管理",
"Kdb927f83": "通过为 API 服务配置和管理 SSL 证书,企业可以加密数据传输,防止敏感信息被窃取或篡改。",
"K877985b7": "修改配置",
"Kdf66a675": "设置访问 API 的集群,让 API 在分布式环境中稳定运行,并且能够根据业务需求进行灵活扩展和优化。",
"Kaf074220": "未配置",
"K5878440c": "集群地址",
"K5e9022f8": "下一步",
"K8e7a0f80": "资源配置",
"Kabfe9512": "保存",
"K95c3fd8b": "设置角色的权限范围。",
"K138facd3": "系统级别角色",
"K6eac768d": "添加角色",
"Kb9c2cf02": "团队级别角色",
"Kb4ceecea": "添加子分类",
"K67479e88": "修改分类名称",
"K2bc75e2c": "添加分类",
"Kab4aab44": "重命名分类",
"K8e0e6977": "设置服务可选择的分类,方便团队成员快速找到API。",
"Ke595a20a": "分类名称",
"K9679728f": "父分类 ID",
"K9b2d08fd": "子分类名称",
"Kf14e76e5": "副本",
"K2e050340": "API 基础信息",
"K90f3c02f": "转发规则设置",
"K6ea8d549": "编辑文档",
"Kff5c18ac": "最后编辑人",
"K2eb99415": "转发规则",
"Ke93388fd": "编辑 API",
"K1b1ae3b0": "复制 API",
"K84aabfd4": "添加 API",
"K6a662463": "输入名称、URL 查找 API",
"K59bc6280": "API 详情",
"K2a16c93b": "单位:ms,最小值:1",
"K469e475a": "重试次数",
"Kd568e15c": "发布结果",
"K35f990b0": "查看详情",
"Kdbc1f6cb": "申请发布",
"Kb6860a3f": "回滚",
"Ka3494f4b": "请确认是否回滚?",
"Kb397a99f": "撤销申请",
"K7d401c0f": "请确认是否撤销申请?",
"Ke1b79b93": "终止发布",
"Ka2449180": "请确认是否终止发布?",
"K2cb02f38": "新建版本",
"Ka9c08390": "只允许上传PNG、JPG或SVG格式的图片",
"Kcf756b7a": "API 调用前缀",
"K43d101a": "选填,作为服务内所有API的前缀,比如host/{service_name}/{api_path},一旦保存无法修改",
"Kdc840242": "图标",
"K427a5bd5": "仅支持 .png .jpg .jpeg .svg 格式的图片文件, 大于 1KB 的文件将被压缩",
"K44bc352d": "Logo",
"Kf52a584d": "所属服务分类",
"K72b21be5": "设置服务展示在服务市场中的哪个分类下",
"Kde6bae17": "删除服务",
"K885ea699": "删除操作不可恢复,请谨慎操作!",
"K617f34f1": "最近一次更新者",
"K6ebca204": "最近一次更新时间",
"K39ab0358": "新增订阅方",
"K2d6658ed": "添加服务",
"K7b8f623f": "输入名称、ID、所属团队、负责人查找服务",
"Kdd9b5008": "后端默认使用的IP地址",
"K6bc47edb": "请求协议",
"Kc9acdb25": "负载均衡",
"K632dba5c": "转发 Host",
"Kc1f08a63": "重写域名",
"K628f6851": "超时时间",
"Kaff62621": "超时重试次数",
"Kc41ca30e": "调用频率限制",
"K813e1c0a": "团队名称",
"K692f5aa6": "团队 ID",
"K5de0bc2": "团队 ID(team_id)可用于检索团队,一旦保存无法修改。",
"Ka63dd985": "团队负责人",
"Ka6bcd272": "负责人对团队内的团队、服务、成员有管理权限",
"Ka2012bdd": "删除团队",
"Kbde1f3d": "服务数据清除后,方可删除",
"K395acc14": "移除成员",
"Kec46a57f": "添加成员",
"K48724410": "输入姓名查找",
"Kb9052305": "搜索用户名、邮箱",
"K5ece3bac": "设置团队和成员,然后你可以在团队内创建服务和应用、订阅API,成员只能看到所属团队内的服务和应用。",
"K510cdd27": "添加团队",
"K9244ae14": "输入名称、ID、负责人查找团队",
"Kc7b24b4b": "配置团队",
"Kecb51e2c": "旧密码",
"K8266bcf2": "新密码",
"Ka9aef039": "确认密码",
"Kcf42dcda": "两次密码不一致",
"Kf876a42d": "修改密码",
"K8ed884f": "管理个人账号",
"K9be8e1d7": "API调用统计",
"K521ab28e": "选择服务",
"Kcc8265e1": "选择API",
"K8aefc1e4": "请输入请求路径进行搜索",
"K50d471b2": "重置",
"Kee8ae330": "查询",
"Ka2c794a2": "导出",
"Kaf70c3b": "退出全屏",
"Kd22841a4": "(0)调用详情",
"K1512e983": "应用调用统计",
"Kb4d2007f": "请选择应用",
"K8c7f2d2e": "调用趋势",
"K657c3452": "(0)-(1)调用趋势",
"Kc04efb87": "调用量统计",
"Keb98266e": "加入总体数据对比",
"K18c2ed46": "(0)调用量",
"Kc3741830": "(0)调用成功率",
"Ka6aa5863": "请求总数",
"K9eaef42": "请求成功率",
"K7082a4af": "转发总数",
"K1ce386fb": "转发成功率",
"K87d6877e": "状态码4xx数",
"K4c8a54db": "状态码5xx数",
"Kd566283e": "调用总体趋势",
"Kd23a0be6": "请求报文量",
"Kec3e8361": "响应报文量",
"Ke6250744": "状态码4XX数",
"K2d79d4e1": "状态码5XX数",
"Kcf6553c6": "服务调用统计",
"Kffcfe375": "请选择服务",
"Ka65f739c": "调用详情",
"K89b7ac79": "API 请求量 Top10",
"Kc0915603": "应用调用量 Top10",
"Kf90b54": "服务被调用量 Top10",
"Kfb26388": "暂无请求统计数据",
"Kc8cbd8f8": "请求统计",
"K8dece48": "暂无转发统计数据",
"K1ee32434": "转发统计",
"Kcd125e4d": "暂无调用量统计数据",
"Kaa114e8b": "暂无报文量统计数据",
"K3ad84406": "报文量统计",
"K19a3ebe0": "请求成功数",
"Kcaa8259": "转发成功数",
"K888f038f": "失败状态码数",
"K42d2bef2": "平均响应时间(ms)",
"K9197c994": "最大响应时间(ms)",
"K7c2f3fee": "最小响应时间(ms)",
"K3d85ea54": "平均请求流量(KB)",
"Keec09d32": "最大请求流量(KB)",
"K3786b48": "最小请求流量(KB)",
"K5168eb63": "应用名称",
"K546e46f": "应用 ID",
"K4a1a14": "监控总览",
"K69741ea7": "服务被调用统计",
"K9c8d9933": "API 调用统计",
"K28cf9613": "每分钟",
"K18f25019": "每5分钟",
"Kf00f01ca": "每小时",
"Kfcda87fc": "每天",
"K29ec75dc": "每周",
"K145e4941": "亿",
"Ke6a935d": "万",
"K8f7abcab": " 次",
"K146477a8": "服务标签",
"K4de0af74": "服务分类",
"Kcce1af60": "订阅的服务",
"Kb6e9328f": "访问授权",
"Kb7e869a4": "应用管理",
"Kd59290a2": "搜索分类或标签",
"K6b75bdbc": "暂无API数据",
"Kd8a7a689": "搜索或选择应用",
"K4b15d6f5": "申请理由",
"Kb71b5a13": "鉴权类型",
"K4d1465ee": "Iss",
"K5dcd7ed8": "签名算法",
"K5b0eedd3": "Secret",
"K44f4ffe1": "RSA 公钥",
"Kc5ecd7d9": "用户名 JsonPath",
"K417d85cf": "校验字段",
"K3b82fe1d": "是否 Base64 加密",
"K49b5f4a3": "AK",
"K31418470": "SK",
"Kbfeb5297": "Apikey",
"K95764d1d": "删除应用",
"K217cb125": "鉴权详情",
"K2bb63eca": "添加鉴权",
"Kd74d69b7": "编辑鉴权",
"K9cbe1e0": "修改",
"Kd23d1716": "添加授权",
"K9dfa2c97": "永不过期",
"Kfa920c0": "到期时间",
"Kbeb4e991": "审批详情",
"Ked811bb1": "请确认是否取消订阅?",
"K50c39a62": "取消订阅申请",
"K1856c229": "请确认是否取消订阅申请?",
"K66ea2f0": "搜索服务",
"Kfeb2559b": "审批中",
"Ka2b6d281": "API 文档",
"K667bbbe7": "添加应用",
"Ka4b45550": "暂无服务描述",
"K3c7b175f": "订阅的服务数量:已通过 (0) 个,申请中 (1) 个",
"K850b4b2d": "状态码",
"Kbe3e9335": "退出测试",
"K370a3eb2": "服务市场",
"Kf7ec36d": "服务详情",
"K59cdbec3": "介绍",
"K4aa9ed2c": "申请",
"K6c060779": "服务信息",
"K8723422e": "接入应用",
"Kb97544cb": "供应方",
"Kb32f0afe": "分类",
"K81634069": "版本",
"K96a2f1c8": "无标签",
"K93d5a66e": "接入应用数量",
"K3e770a75": "鉴权 Token",
"K96059c69": "关联标签",
"K32263abd": "添加 Open Api",
"K7829bb78": "配置 Open Api",
"Kcdf76005": "Open Api",
"Ke2601944": "调用服务",
"K8504bca8": "放大",
"K693c1b41": "缩小"
}
@@ -1,4 +1,3 @@
import { loader } from '@monaco-editor/react';
import * as monaco from 'monaco-editor';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
@@ -1,5 +1,7 @@
import { MenuProps } from "antd";
import { MenuItem } from "@common/components/aoplatform/Navigation";
export type MenuItem = Required<MenuProps>['items'][number];
export function getNavItem(
label: React.ReactNode,
@@ -251,7 +251,7 @@ import { TopologyProjectItem, TopologyServiceItem } from "@core/pages/systemRun
// },
// defaultEdge: {
// type: 'line-running',
// label: '详情',
// label: $t('详情',
// labelCfg: {
// style: {
// fill: '5B8FF9',
@@ -1,7 +1,8 @@
import { $t } from "@common/locales";
export const validateUrlSlash = (_, value) => {
if (value && value.includes('//')) {
return Promise.reject(new Error('暂不支持带有双斜杠//的url'));
return Promise.reject(new Error($t('暂不支持带有双斜杠//的url')));
}
return Promise.resolve();
};
+2 -2
View File
@@ -10,7 +10,7 @@
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script src="/frontend/iconpark_eolink.js"></script>
<script src="/frontend/iconpark_apinto.js"></script>
<script async src="/frontend/iconpark_eolink.js"></script>
<script async src="/frontend/iconpark_apinto.js"></script>
</body>
</html>
+7 -11
View File
@@ -1,5 +1,12 @@
@tailwind base;
@tailwind components;
@layer components {
.button-bottom-default {
@apply border-[0px] border-b-[1px] border-solid border-BORDER;
}
}
@tailwind utilities;
@@ -36,17 +43,6 @@
background-color:'transparent'
}
/* .ant-layout-sider-children{
ul.ant-menu.ant-menu-root.ant-menu-inline > li:not(.ant-menu-item-selected) {
border-radius: 10px;
background-color: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.15);
}
.ant-menu-item {
height:40px;
margin-block:10px;
}
} */
.apipark-layout-global-header-collapsed-button{
color:hsl(0, 0%, 100%);
+22 -2
View File
@@ -1,11 +1,22 @@
import './App.css'
import { ConfigProvider } from 'antd';
import { ConfigProvider, ConfigProviderProps, Radio, RadioChangeEvent } from 'antd';
import RenderRoutes from '@core/components/aoplatform/RenderRoutes';
import {BreadcrumbProvider} from "@common/contexts/BreadcrumbContext.tsx";
import { StyleProvider } from '@ant-design/cssinjs';
import zhCN from 'antd/locale/zh_CN';
import enUS from 'antd/locale/en_US';
import useInitializeMonaco from "@common/hooks/useInitializeMonaco";
import { useEffect, useState } from 'react';
import 'dayjs/locale/zh-cn';
import dayjs from 'dayjs';
import { useTranslation } from "react-i18next";
import { useGlobalContext } from '@common/contexts/GlobalStateContext';
type Locale = ConfigProviderProps['locale'];
dayjs.locale('en');
const antdComponentThemeToken = {
token: {
@@ -129,12 +140,21 @@ const antdComponentThemeToken = {
}
function App() {
const [locale, setLocal] = useState<Locale>(enUS);
useInitializeMonaco()
const { state} = useGlobalContext()
useEffect(() => {
console.log(state.language)
dayjs.locale(state.language);
setLocal(state.language === 'cn' ? zhCN : enUS);
},[state.language])
return (
<StyleProvider hashPriority={"high"}>
<ConfigProvider
locale={zhCN}
locale={locale}
wave={{disabled:true}}
theme={antdComponentThemeToken}>
<BreadcrumbProvider>
@@ -338,16 +338,6 @@ const PUBLIC_ROUTES:RouteConfig[] = [
}
]
},
{
path:'logretrieval',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/logRetrieval/LogRetrieval.tsx')),
key:uuidv4(),
},
{
path:'auditlog',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/auditLog/AuditLog.tsx')),
key:uuidv4(),
},
{
path:'assets',
component:<p></p>,
@@ -1,11 +1,12 @@
import { ProColumns } from "@ant-design/pro-components";
import { PageProColumns } from "@common/components/aoplatform/PageList";
import { MemberTableListItem } from "./type";
import { $t } from "@common/locales";
export const MEMBER_TABLE_COLUMNS: ProColumns<MemberTableListItem>[] = [
export const MEMBER_TABLE_COLUMNS: PageProColumns<MemberTableListItem>[] = [
{
title: '用户名',
title:$t('用户名'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -15,12 +16,12 @@ export const MEMBER_TABLE_COLUMNS: ProColumns<MemberTableListItem>[] = [
},
},
{
title: '邮箱',
title:$t('邮箱'),
dataIndex: 'email',
ellipsis:true,
},
{
title: '部门',
title:$t('部门'),
dataIndex: 'department',
ellipsis:true,
filterMode:'tree',
@@ -31,20 +32,20 @@ export const MEMBER_TABLE_COLUMNS: ProColumns<MemberTableListItem>[] = [
filterSearch: true,
},
{
title: '角色',
title:$t('角色'),
dataIndex: 'roles',
ellipsis:true,
width:200
},
{
title:'状态',
title:$t('状态'),
dataIndex:'enable',
valueType: 'select',
filters: true,
onFilter: true,
valueEnum:new Map([
[true,<span className="text-status_success"></span>],
[false,<span className="text-status_fail"></span>],
[true,<span className="text-status_success">{$t('启用')}</span>],
[false,<span className="text-status_fail">{$t('禁用')}</span>],
])
}
];
@@ -1,12 +1,13 @@
import { ProColumns } from "@ant-design/pro-components";
import { PartitionCertTableListItem, PartitionClusterNodeModalTableListItem, PartitionClusterNodeTableListItem, PartitionClusterTableListItem, PartitionTableListItem } from "./types";
import { ColumnType } from "antd/es/table";
import CopyAddrList from "@common/components/aoplatform/CopyAddrList";
import { $t } from "@common/locales";
import { PageProColumns } from "@common/components/aoplatform/PageList";
export const PARTITION_CERT_TABLE_COLUMNS: ProColumns<PartitionCertTableListItem>[] = [
export const PARTITION_CERT_TABLE_COLUMNS: PageProColumns<PartitionCertTableListItem>[] = [
{
title: '证书',
title:$t('证书'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -16,7 +17,7 @@ export const PARTITION_CERT_TABLE_COLUMNS: ProColumns<PartitionCertTableListItem
},
},
{
title: '绑定域名',
title:$t('绑定域名'),
dataIndex: 'domains',
renderText:(_,entity) =>(
entity.domains.join(',')
@@ -24,7 +25,7 @@ export const PARTITION_CERT_TABLE_COLUMNS: ProColumns<PartitionCertTableListItem
ellipsis:true
},
{
title: '过期日期',
title:$t('过期日期'),
ellipsis: true,
dataIndex: 'notAfter',
width:100,
@@ -34,7 +35,7 @@ export const PARTITION_CERT_TABLE_COLUMNS: ProColumns<PartitionCertTableListItem
},
},
{
title: '更新者',
title:$t('更新者'),
dataIndex: ['updater','name'],
ellipsis: true,
filters: true,
@@ -43,7 +44,7 @@ export const PARTITION_CERT_TABLE_COLUMNS: ProColumns<PartitionCertTableListItem
filterSearch: true
},
{
title: '更新时间',
title:$t('更新时间'),
key: 'updateTime',
dataIndex: 'updateTime',
ellipsis:true,
@@ -54,9 +55,9 @@ export const PARTITION_CERT_TABLE_COLUMNS: ProColumns<PartitionCertTableListItem
},
];
export const PARTITION_CLUSTER_TABLE_COLUMNS : ProColumns<PartitionClusterTableListItem>[] = [
export const PARTITION_CLUSTER_TABLE_COLUMNS : PageProColumns<PartitionClusterTableListItem>[] = [
{
title: '集群名称',
title:$t('集群名称'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -66,13 +67,13 @@ export const PARTITION_CLUSTER_TABLE_COLUMNS : ProColumns<PartitionClusterTableL
},
},
{
title: '集群 ID',
title:$t('集群 ID'),
dataIndex: 'id',
width: 140,
ellipsis:true
},
{
title: '状态',
title:$t('状态'),
dataIndex: 'status',
ellipsis:true,
valueType: 'select',
@@ -84,16 +85,16 @@ export const PARTITION_CLUSTER_TABLE_COLUMNS : ProColumns<PartitionClusterTableL
])
},
{
title: '描述',
title: $t('描述'),
dataIndex: 'description',
ellipsis:true
}
];
export const PARTITION_CLUSTER_NODE_COLUMNS: ProColumns<PartitionClusterNodeTableListItem>[] = [
export const PARTITION_CLUSTER_NODE_COLUMNS: PageProColumns<PartitionClusterNodeTableListItem>[] = [
{
title: '节点名称',
title:$t('节点名称'),
dataIndex: 'name',
ellipsis:true,
fixed:'left',
@@ -102,28 +103,28 @@ export const PARTITION_CLUSTER_NODE_COLUMNS: ProColumns<PartitionClusterNodeTabl
},
},
{
title: '管理地址',
title:$t('管理地址'),
dataIndex: 'managerAddress',
ellipsis:true,
width:200,
render:(_,entity)=>(<CopyAddrList keyName="managerAddress" addrItem={entity} />)
},
{
title: '服务地址',
title:$t('服务地址'),
dataIndex: 'serviceAddress',
ellipsis:true,
width:230,
render:(_,entity)=>(<CopyAddrList keyName="serviceAddress" addrItem={entity} />)
},
{
title: '集群同步地址',
title:$t('集群同步地址'),
dataIndex: 'peerAddress',
ellipsis:true,
width:230,
render:(_,entity)=>(<CopyAddrList keyName="peerAddress" addrItem={entity} />)
},
{
title: '状态',
title:$t('状态'),
dataIndex: 'status',
ellipsis:true,
width:86,
@@ -138,20 +139,20 @@ export const PARTITION_CLUSTER_NODE_COLUMNS: ProColumns<PartitionClusterNodeTabl
];
export const NODE_MODAL_COLUMNS:ColumnType<PartitionClusterNodeModalTableListItem>[] = [
{title:'名称', dataIndex:'name',width:200,
{title:$t('名称'), dataIndex:'name',width:200,
ellipsis:true,
fixed:'left'},
{title:'管理地址', dataIndex:'managerAddress',width:240,ellipsis:true,render:(_,entity)=>(<CopyAddrList keyName="managerAddress" addrItem={entity} />)},
{title:'服务地址', dataIndex:'serviceAddress',width:240,ellipsis:true,render:(_,entity)=>(<CopyAddrList keyName="serviceAddress" addrItem={entity} />)},
{title:'状态', dataIndex:'status',
{title:$t('管理地址'), dataIndex:'managerAddress',width:240,ellipsis:true,render:(_,entity)=>(<CopyAddrList keyName="managerAddress" addrItem={entity} />)},
{title:$t('服务地址'), dataIndex:'serviceAddress',width:240,ellipsis:true,render:(_,entity)=>(<CopyAddrList keyName="serviceAddress" addrItem={entity} />)},
{title:$t('状态'), dataIndex:'status',
render:(text)=>(
<span className={text === 0 ? 'text-status_fail' : 'text-status_success'}>{ClusterStatusEnum[text]}</span>
)}
]
export const PARTITION_LIST_COLUMNS: ProColumns<PartitionTableListItem>[] = [
export const PARTITION_LIST_COLUMNS: PageProColumns<PartitionTableListItem>[] = [
{
title: '环境名称',
title:$t('环境名称'),
dataIndex: 'name',
ellipsis:true,
fixed:'left',
@@ -160,20 +161,20 @@ export const PARTITION_LIST_COLUMNS: ProColumns<PartitionTableListItem>[] = [
},
},
{
title: 'ID',
title:$t('ID'),
dataIndex: 'id',
ellipsis:true,
width:140,
},
// {
// title: '集群数量',
// title:$t('集群数量',
// dataIndex: 'clusterNum',
// sorter: (a,b)=> {
// return a.clusterNum - b.clusterNum
// },
// },
{
title: '更新者',
title:$t('更新者'),
dataIndex: ['updater','name'],
ellipsis: true,
filters: true,
@@ -183,7 +184,7 @@ export const PARTITION_LIST_COLUMNS: ProColumns<PartitionTableListItem>[] = [
filterSearch: true
},
{
title: '更新时间',
title:$t('更新时间'),
dataIndex: 'updateTime',
ellipsis:true,
width:182,
@@ -193,9 +194,9 @@ export const PARTITION_LIST_COLUMNS: ProColumns<PartitionTableListItem>[] = [
},
];
export enum ClusterStatusEnum {
'异常',
'正常'
export const ClusterStatusEnum ={
0: $t('异常'),
1: $t('正常')
}
export const DASHBOARD_SETTING_DRIVER_OPTION_LIST = [
@@ -1,7 +1,8 @@
import { $t } from "@common/locales"
export const ROLE_TABLE_COLUMNS = [
{
title: '角色名称',
title:$t('角色名称'),
dataIndex: 'name',
ellipsis:true,
fixed:'left',
+181 -193
View File
@@ -1,4 +1,3 @@
import { ProColumns } from "@ant-design/pro-components";
import { GlobalNodeItem, MyServiceTableListItem, NodeItem, ProxyHeaderItem, ServiceApiTableListItem, SimpleApiItem, SystemApiTableListItem, SystemAuthorityTableListItem, SystemMemberTableListItem, SystemSubServiceTableListItem, SystemSubscriberTableListItem, SystemTableListItem, SystemUpstreamTableListItem } from "./type";
import { Input, InputNumber, MenuProps, Select, TabsProps, Tooltip } from "antd";
import { ColumnsType } from "antd/es/table";
@@ -6,21 +5,25 @@ import { getItem } from "@common/utils/navigation";
import { MatchItem, MemberItem } from "@common/const/type";
import { ConfigField } from "@common/components/aoplatform/EditableTableWithModal";
import { frontendTimeSorter } from "@common/utils/dataTransfer";
import moment from "moment";
import { STATUS_COLOR } from "@common/const/const";
import { COLUMNS_TITLE, STATUS_COLOR, VALIDATE_MESSAGE } from "@common/const/const";
import { LoadingOutlined } from "@ant-design/icons";
import { SystemInsidePublishOnlineItems } from "../../pages/system/publish/SystemInsidePublishOnline";
import dayjs from 'dayjs';
import { Link } from "react-router-dom";
import { $t } from "@common/locales";
import { PageProColumns } from "@common/components/aoplatform/PageList";
export enum SubscribeEnum{
'驳回' = 0,
'审核中' = 1,
'已订阅' = 2,
'取消订阅' = 3,
'取消申请' = 4
Rejected = 0,
Reviewing = 1,
Subscribed = 2,
Unsubscribed = 3,
CancelRequest = 4
}
export const SubscribeStatusColor= {
2: 'text-[#138913]', // 使用 Tailwind 的 Arbitrary Properties
1: 'text-[#03a9f4]',
@@ -30,29 +33,40 @@ export const SubscribeStatusColor= {
};
export enum SubscribeFromEnum {
'手动添加' = 0,
'订阅申请' = 1
manual = 0,
subscribe= 1
}
export enum MatchPositionEnum {
'header' = 'HTTP 请求头',
'query' = '请求参数',
'cookie' = 'Cookie'
export const MatchPositionEnum = {
'header' : $t('HTTP 请求头'),
'query': $t('请求参数'),
'cookie' : $t('Cookie')
}
export enum MatchTypeEnum{
'EQUAL' = '全等匹配',
'PREFIX' = '前缀匹配',
'SUFFIX' = '后缀匹配',
'SUBSTR' = '子串匹配',
'UNEQUAL' = '非等匹配',
'NULL' = '空值匹配',
'EXIST' = '存在匹配',
'UNEXIST'='不存在匹配',
'REGEXP'='区分大小写的正则匹配',
'REGEXPG'='不区分大小写的正则匹配',
'unknown'='任意匹配'
export const MatchTypeEnum = {
'EQUAL' : $t('全等匹配'),
'PREFIX' : $t('前缀匹配'),
'SUFFIX' :$t('后缀匹配'),
'SUBSTR' : $t('子串匹配'),
'UNEQUAL' : $t('非等匹配'),
'NULL' : $t('空值匹配'),
'EXIST' : $t('存在匹配'),
'UNEXIST':$t('不存在匹配'),
'REGEXP':$t('区分大小写的正则匹配'),
'REGEXPG':$t('不区分大小写的正则匹配'),
'unknown': $t('任意匹配')
}
export const SYSTEM_I18NEXT_FOR_ENUM = {
[SubscribeEnum.Rejected]:$t('驳回'),
[SubscribeEnum.Reviewing]:$t('审核中'),
[SubscribeEnum.Subscribed]:$t('已订阅'),
[SubscribeEnum.Unsubscribed]:$t('取消订阅'),
[SubscribeEnum.CancelRequest]:$t('取消申请'),
[SubscribeFromEnum.manual]:$t('手动添加'),
[SubscribeFromEnum.subscribe]:$t('订阅申请'),
}
export const HTTP_METHOD = ['GET','POST','PUT','DELETE','PATCH','HEAD']
@@ -70,9 +84,9 @@ export const ALGORITHM_ITEM = [
{label:'ES512',value:'ES512'},
]
export const SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
export const SYSTEM_TABLE_COLUMNS: PageProColumns<SystemTableListItem>[] = [
{
title: '服务名称',
title:$t('服务名称'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -82,13 +96,13 @@ export const SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
},
},
{
title: '服务 ID',
title:$t('服务 ID'),
dataIndex: 'id',
width: 140,
ellipsis:true,
},
{
title: '所属团队',
title:$t('所属团队'),
dataIndex: ['team','name'],
ellipsis:true,
// filters: true,
@@ -96,7 +110,7 @@ export const SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
// filterSearch: true,
},
{
title: 'API 数量',
title:$t('API 数量'),
dataIndex: 'apiNum',
ellipsis:true,
sorter: (a,b)=> {
@@ -104,12 +118,12 @@ export const SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
},
},
{
title: '描述',
title: $t('描述'),
dataIndex: 'description',
ellipsis:true,
},
{
title: '负责人',
title:$t('负责人'),
dataIndex: ['master','name'],
ellipsis: true,
width:108,
@@ -119,7 +133,7 @@ export const SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
filterSearch: true,
},
{
title: '创建时间',
title:$t('创建时间'),
dataIndex: 'createTime',
width:182,
ellipsis:true,
@@ -127,9 +141,9 @@ export const SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
}
];
export const SYSTEM_SUBSERVICE_TABLE_COLUMNS: ProColumns<SystemSubServiceTableListItem>[] = [
export const SYSTEM_SUBSERVICE_TABLE_COLUMNS: PageProColumns<SystemSubServiceTableListItem>[] = [
{
title: '服务名称',
title:$t('服务名称'),
dataIndex: ['service','name'],
ellipsis:true,
width:160,
@@ -139,13 +153,13 @@ export const SYSTEM_SUBSERVICE_TABLE_COLUMNS: ProColumns<SystemSubServiceTableLi
},
},
{
title: '服务 ID',
title:$t('服务 ID'),
dataIndex: ['service','name'],
width: 140,
ellipsis:true
},
{
title: '申请状态',
title:$t('申请状态'),
dataIndex: 'applyStatus',
ellipsis:{
showTitle:true
@@ -155,25 +169,25 @@ export const SYSTEM_SUBSERVICE_TABLE_COLUMNS: ProColumns<SystemSubServiceTableLi
onFilter: true,
valueType: 'select',
valueEnum:new Map([
[0,<span className={SubscribeStatusColor[0]}></span>],
[1,<span className={SubscribeStatusColor[1]}></span>],
[2,<span className={SubscribeStatusColor[2]}></span>],
[3,<span className={SubscribeStatusColor[3]}></span>],
[4,<span className={SubscribeStatusColor[4]}></span>],
[0,<span className={SubscribeStatusColor[0]}>{$t('驳回')}</span>],
[1,<span className={SubscribeStatusColor[1]}>{$t('审核中')}</span>],
[2,<span className={SubscribeStatusColor[2]}>{$t('已订阅')}</span>],
[3,<span className={SubscribeStatusColor[3]}>{$t('取消订阅')}</span>],
[4,<span className={SubscribeStatusColor[4]}>{$t('取消申请')}</span>],
])
},
{
title: '所属服务',
title:$t('所属服务'),
dataIndex: ['project','name'],
ellipsis:true
},
{
title: '所属团队',
title:$t('所属团队'),
dataIndex: ['team','name'],
ellipsis:true
},
{
title: '申请人',
title:$t('申请人'),
dataIndex: ['applier','name'],
ellipsis: true,
width:88,
@@ -183,19 +197,19 @@ export const SYSTEM_SUBSERVICE_TABLE_COLUMNS: ProColumns<SystemSubServiceTableLi
filterSearch: true,
},
{
title: '来源',
title:$t('来源'),
dataIndex: 'from',
ellipsis: true,
filters: true,
onFilter: true,
valueType: 'select',
valueEnum:new Map([
[0,<span></span>],
[1,<span></span>],
[0,<span>{$t('手动添加')}</span>],
[1,<span>{$t('订阅申请')}</span>],
])
},
{
title: '添加时间',
title:$t('添加时间'),
dataIndex: 'createTime',
ellipsis:true,
width:182,
@@ -206,9 +220,9 @@ export const SYSTEM_SUBSERVICE_TABLE_COLUMNS: ProColumns<SystemSubServiceTableLi
];
export const SYSTEM_SUBSCRIBER_TABLE_COLUMNS: ProColumns<SystemSubscriberTableListItem>[] = [
export const SYSTEM_SUBSCRIBER_TABLE_COLUMNS: PageProColumns<SystemSubscriberTableListItem>[] = [
{
title: '服务名称',
title:$t('服务名称'),
dataIndex: ['service','name'],
ellipsis:true,
width:160,
@@ -218,55 +232,35 @@ export const SYSTEM_SUBSCRIBER_TABLE_COLUMNS: ProColumns<SystemSubscriberTableLi
},
},
{
title: '服务 ID',
title:$t('服务 ID'),
dataIndex: 'id',
width: 140,
ellipsis:true
},
{
title: '订阅方',
title:$t('订阅方'),
dataIndex: ['subscriber','name'],
ellipsis:true
},
{
title: '所属团队',
title:$t('所属团队'),
dataIndex: ['team','name'],
ellipsis:true
},
// {
// title: '申请人',
// dataIndex: ['applier','name'],
// ellipsis:true,
// width:88,
// filters: true,
// onFilter: true,
// valueType: 'select',
// filterSearch: true,
// },
// {
// title: '审批人',
// dataIndex: ['approver','name'],
// ellipsis:true,
// width:88,
// filters: true,
// onFilter: true,
// valueType: 'select',
// filterSearch: true,
// },
{
title: '来源',
title:$t('来源'),
dataIndex: 'from',
ellipsis:true,
filters: true,
onFilter: true,
valueType: 'select',
valueEnum:new Map([
[0,<span></span>],
[1,<span></span>],
[0,<span>{$t('手动添加')}</span>],
[1,<span>{$t('订阅申请')}</span>],
])
},
{
title: '订阅时间',
title:$t('订阅时间'),
dataIndex: 'applyTime',
ellipsis:true,
width:182,
@@ -278,7 +272,7 @@ export const SYSTEM_SUBSCRIBER_TABLE_COLUMNS: ProColumns<SystemSubscriberTableLi
export const memberModalColumn:ColumnsType<MemberItem> = [
{title:'成员',
{title:$t('成员'),
render:(_,entity)=>{
return <>
<div>
@@ -292,9 +286,9 @@ export const memberModalColumn:ColumnsType<MemberItem> = [
}}
]
export const SYSTEM_MEMBER_TABLE_COLUMN: ProColumns<SystemMemberTableListItem>[] = [
export const SYSTEM_MEMBER_TABLE_COLUMN: PageProColumns<SystemMemberTableListItem>[] = [
{
title: '用户名',
title:$t('用户名'),
dataIndex: ['user','name'],
ellipsis:true,
width:160,
@@ -304,12 +298,12 @@ export const SYSTEM_MEMBER_TABLE_COLUMN: ProColumns<SystemMemberTableListItem>[]
},
},
{
title: '邮箱',
title:$t('邮箱'),
dataIndex: 'email',
ellipsis:true
},
{
title: '角色',
title:$t('角色'),
dataIndex: ['roles','name'],
ellipsis:true
@@ -318,7 +312,7 @@ export const SYSTEM_MEMBER_TABLE_COLUMN: ProColumns<SystemMemberTableListItem>[]
export const MATCH_CONFIG:ConfigField<MatchItem>[] = [
{
title: '参数位置',
title:$t('参数位置'),
key: 'position',
component: <Select className="w-INPUT_NORMAL" options={Object.entries(MatchPositionEnum)?.map(([key,value])=>{
return { label:value, value:key}
@@ -329,13 +323,13 @@ export const MATCH_CONFIG:ConfigField<MatchItem>[] = [
required: true,
ellipsis:true
}, {
title: '参数名',
title:$t('参数名'),
key: 'key',
component: <Input className="w-INPUT_NORMAL" />,
renderText: (value: unknown) => <>{value}</>,
required: true
}, {
title: '匹配类型',
title:$t('匹配类型'),
key: 'matchType',
component: <Select className="w-INPUT_NORMAL" options={Object.entries(MatchTypeEnum)?.map(([key,value])=>{
return { label:value, value:key}
@@ -345,7 +339,7 @@ export const MATCH_CONFIG:ConfigField<MatchItem>[] = [
},
required: true
}, {
title: '参数值',
title:$t('参数值'),
key: 'pattern',
component: <Input className="w-INPUT_NORMAL"/>,
renderText: (value: string) => {
@@ -356,9 +350,9 @@ export const MATCH_CONFIG:ConfigField<MatchItem>[] = [
]
export const SYSTEM_API_TABLE_COLUMNS: ProColumns<SystemApiTableListItem>[] = [
export const SYSTEM_API_TABLE_COLUMNS: PageProColumns<SystemApiTableListItem>[] = [
{
title: '名称',
title:$t('名称'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -369,7 +363,7 @@ export const SYSTEM_API_TABLE_COLUMNS: ProColumns<SystemApiTableListItem>[] = [
},
},
{
title: '协议/方法',
title:$t('协议/方法'),
dataIndex: 'method',
ellipsis:true,
filters: true,
@@ -384,12 +378,12 @@ export const SYSTEM_API_TABLE_COLUMNS: ProColumns<SystemApiTableListItem>[] = [
},
},
{
title: 'URL',
title:$t('URL'),
dataIndex: 'requestPath',
ellipsis:true
},
{
title: '创建者',
title:$t('创建者'),
dataIndex: ['creator','name'],
ellipsis: true,
filters: true,
@@ -398,7 +392,7 @@ export const SYSTEM_API_TABLE_COLUMNS: ProColumns<SystemApiTableListItem>[] = [
filterSearch: true,
},
{
title: '更新时间',
title:$t('更新时间'),
dataIndex: 'updateTime',
ellipsis:true,
hideInSearch: true,
@@ -409,9 +403,9 @@ export const SYSTEM_API_TABLE_COLUMNS: ProColumns<SystemApiTableListItem>[] = [
export const SYSTEM_UPSTREAM_TABLE_COLUMNS: ProColumns<SystemUpstreamTableListItem>[] = [
export const SYSTEM_UPSTREAM_TABLE_COLUMNS: PageProColumns<SystemUpstreamTableListItem>[] = [
{
title: '名称',
title:$t('名称'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -421,13 +415,13 @@ export const SYSTEM_UPSTREAM_TABLE_COLUMNS: ProColumns<SystemUpstreamTableListIt
},
},
{
title: '上游 ID',
title:$t('上游 ID'),
dataIndex: 'id',
width: 140,
ellipsis:true
},
{
title: '创建人',
title:$t('创建人'),
dataIndex: ['creator','name'],
ellipsis: true,
width:88,
@@ -437,7 +431,7 @@ export const SYSTEM_UPSTREAM_TABLE_COLUMNS: ProColumns<SystemUpstreamTableListIt
filterSearch: true,
},
{
title: '更新人',
title:$t('更新人'),
dataIndex: ['updater','name'],
ellipsis: true,
width:88,
@@ -447,7 +441,7 @@ export const SYSTEM_UPSTREAM_TABLE_COLUMNS: ProColumns<SystemUpstreamTableListIt
filterSearch: true,
},
{
title: '创建时间',
title:$t('创建时间'),
dataIndex: 'createTime',
width:182,
ellipsis:true,
@@ -456,7 +450,7 @@ export const SYSTEM_UPSTREAM_TABLE_COLUMNS: ProColumns<SystemUpstreamTableListIt
}
},
{
title: '更新时间',
title:$t('更新时间'),
dataIndex: 'updateTime',
width:182,
ellipsis:true,
@@ -466,47 +460,47 @@ export const SYSTEM_UPSTREAM_TABLE_COLUMNS: ProColumns<SystemUpstreamTableListIt
},
];
export enum UpstreamDriverEnum{
'static'='静态上游',
'discoveries'='动态服务发现',
export const UpstreamDriverEnum = {
'static':$t('静态上游'),
'discoveries':$t('动态服务发现'),
}
export const typeOptions = [
{ label: '静态上游', value: 'static' },
// { label: '动态服务发现', value: 'discoveries' },
{ label: $t('静态上游'), value: 'static' },
// { label: $t('动态服务发现', value: 'discoveries' },
];
export const schemeOptions = [
{ label:'HTTPS', value:'HTTPS'},
{ label:'HTTP', value:'HTTP'},
{ label:$t('HTTPS'), value:'HTTPS'},
{ label:$t('HTTP'), value:'HTTP'},
]
export const balanceOptions = [
{ label: '带权轮询', value: 'round-robin' },
{ label: 'IP Hash', value: 'ip-hash' },
{ label: $t('带权轮询'), value: 'round-robin' },
{ label: $t('IP Hash'), value: 'ip-hash' },
];
export const passHostOptions = [
{ label:'透传客户端请求 Host', value:'pass'},
{ label:'使用上游服务 Host', value:'node'},
{ label:'重写 Host', value:'rewrite'},
{ label:$t('透传客户端请求 Host'), value:'pass'},
{ label:$t('使用上游服务 Host'), value:'node'},
{ label:$t('重写 Host'), value:'rewrite'},
]
export const proxyHeaderTypeOptions =[
{label:'新增或修改', value: 'ADD' },
{ label: '删除', value: 'DELETE' }
{label:$t('新增或修改'), value: 'ADD' },
{ label: $t('删除'), value: 'DELETE' }
]
export const PROXY_HEADER_CONFIG:ConfigField<ProxyHeaderItem>[] = [
{
title: '操作类型',
title:$t('操作类型'),
key: 'optType',
component: <Select className="w-INPUT_NORMAL" options={proxyHeaderTypeOptions}/>,
renderText: (value: string) => {
return (<>{value === 'ADD' ? '新增或修改':'删除'}</>)
return (<>{value === 'ADD' ? $t('新增或修改'):$t('删除')}</>)
},
required: true
}, {
title: '参数名',
title:$t('参数名'),
key: 'key',
component: <Input className="w-INPUT_NORMAL"/>,
renderText: (value: string) => {
@@ -514,7 +508,7 @@ export const PROXY_HEADER_CONFIG:ConfigField<ProxyHeaderItem>[] = [
},
required: true
}, {
title: '参数值',
title:$t('参数值'),
key: 'value',
component: <Input className="w-INPUT_NORMAL" />,
renderText: (value: string) => {
@@ -526,12 +520,12 @@ export const PROXY_HEADER_CONFIG:ConfigField<ProxyHeaderItem>[] = [
export const NODE_CONFIG:ConfigField<NodeItem>[] = [
{
title: '集群',
title:$t('集群'),
key: 'cluster',
component: <Select className="w-INPUT_NORMAL" options={[]}/>,
required: true
}, {
title: '地址',
title:$t('地址'),
key: 'address',
component: <Input className="w-INPUT_NORMAL" />,
renderText: (value: string) => {
@@ -539,7 +533,7 @@ export const NODE_CONFIG:ConfigField<NodeItem>[] = [
},
required: true
}, {
title: '权重',
title:$t('权重'),
key: 'weight',
component: <InputNumber className="w-INPUT_NORMAL"/>,
renderText: (value: string) => {
@@ -550,37 +544,37 @@ export const NODE_CONFIG:ConfigField<NodeItem>[] = [
]
export const visualizations = [
{label:'内部服务:可通过网关访问,但不展示在服务广场',value:'inner'},
{label:'公开服务:可通过网关访问,展示在服务广场,可被其他应用订阅',value:'public'}];
{label:$t('内部服务:可通过网关访问,但不展示在服务广场'),value:'inner'},
{label:$t('公开服务:可通过网关访问,展示在服务广场,可被其他应用订阅'),value:'public'}];
export const SYSTEM_MYSERVICE_API_TABLE_COLUMNS: ProColumns<ServiceApiTableListItem>[] = [
export const SYSTEM_MYSERVICE_API_TABLE_COLUMNS: PageProColumns<ServiceApiTableListItem>[] = [
{
title: ' ',
title:$t(' '),
dataIndex: 'id',
width:'40px',
fixed:'left'
},
{
title: '名称',
title:$t('名称'),
dataIndex: 'name',
width:160,
fixed:'left',
ellipsis:true
},
{
title: '请求方式',
title:$t('请求方式'),
dataIndex: 'method',
ellipsis:true
},
{
title: '请求路径',
title:$t('请求路径'),
dataIndex: 'path',
ellipsis:true
},
{
title: '描述',
title: $t('描述'),
dataIndex: 'description',
ellipsis:true
}
@@ -589,7 +583,7 @@ export const SYSTEM_MYSERVICE_API_TABLE_COLUMNS: ProColumns<ServiceApiTableListI
export const apiModalColumn:ColumnsType<SimpleApiItem> = [
{
title:'所有 API',
title:$t('所有 API'),
dataIndex:'method',
},
{
@@ -600,9 +594,9 @@ export const apiModalColumn:ColumnsType<SimpleApiItem> = [
]
export const SYSTEM_AUTHORITY_TABLE_COLUMNS: ProColumns<SystemAuthorityTableListItem>[] = [
export const SYSTEM_AUTHORITY_TABLE_COLUMNS: PageProColumns<SystemAuthorityTableListItem>[] = [
{
title: '名称',
title:$t('名称'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -612,7 +606,7 @@ export const SYSTEM_AUTHORITY_TABLE_COLUMNS: ProColumns<SystemAuthorityTableList
},
},
{
title: '类型',
title:$t('类型'),
dataIndex: 'driver',
ellipsis:true,
filters: true,
@@ -628,7 +622,7 @@ export const SYSTEM_AUTHORITY_TABLE_COLUMNS: ProColumns<SystemAuthorityTableList
}
},
{
title: '隐藏鉴权信息',
title:$t('隐藏鉴权信息'),
dataIndex: 'hideCredential',
ellipsis:{
showTitle:true
@@ -642,19 +636,19 @@ export const SYSTEM_AUTHORITY_TABLE_COLUMNS: ProColumns<SystemAuthorityTableList
])
},
{
title: '过期时间',
title:$t('过期时间'),
dataIndex: 'expireTime',
ellipsis:true,
width:182,
render:(_: React.ReactNode, entity: SystemAuthorityTableListItem) => (
<span className={entity.expireTime !== 0 && moment.now() - (entity.expireTime * 1000) > 0 ? 'text-status_fail' : ''}>{entity.expireTime === 0 ? '永不过期' :moment(entity.expireTime * 1000).format('YYYY-MM-DD hh:mm:ss')}</span>
<span className={entity.expireTime !== 0 && dayjs().valueOf() - (entity.expireTime * 1000) > 0 ? 'text-status_fail' : ''}>{entity.expireTime === 0 ? '永不过期' :dayjs(entity.expireTime * 1000).format('YYYY-MM-DD hh:mm:ss')}</span>
),
sorter: (a,b)=> {
return a.expireTime - b.expireTime
},
},
{
title: '更新者',
title:$t('更新者'),
dataIndex: ['updater','name'],
ellipsis: true,
width:88,
@@ -664,7 +658,7 @@ export const SYSTEM_AUTHORITY_TABLE_COLUMNS: ProColumns<SystemAuthorityTableList
filterSearch: true,
},
{
title: '创建时间',
title:$t('创建时间'),
key: 'createTime',
dataIndex: 'createTime',
width:182,
@@ -676,9 +670,9 @@ export const SYSTEM_AUTHORITY_TABLE_COLUMNS: ProColumns<SystemAuthorityTableList
];
export const SYSTEM_MYSERVICE_TABLE_COLUMNS: ProColumns<MyServiceTableListItem>[] = [
export const SYSTEM_MYSERVICE_TABLE_COLUMNS: PageProColumns<MyServiceTableListItem>[] = [
{
title: '服务名称',
title:$t('服务名称'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -688,13 +682,13 @@ export const SYSTEM_MYSERVICE_TABLE_COLUMNS: ProColumns<MyServiceTableListItem>[
},
},
{
title: '服务ID',
title:$t('服务ID'),
dataIndex: 'id',
width: 140,
ellipsis:true
},
{
title: '服务类型',
title:$t('服务类型'),
dataIndex: 'serviceType',
ellipsis:{
showTitle:true
@@ -704,22 +698,22 @@ export const SYSTEM_MYSERVICE_TABLE_COLUMNS: ProColumns<MyServiceTableListItem>[
valueType: 'select',
valueEnum:{
'public':{
text:'公开服务'
text:$t('公开服务')
},
'inner':{
text:'内部服务'
text:$t('内部服务')
}
}
},
{
title: 'API 数量',
title:$t('API 数量'),
dataIndex: 'apiNum',
sorter: (a,b)=> {
return a.apiNum - b.apiNum
},
},
{
title: '状态',
title:$t('状态'),
dataIndex: 'status',
ellipsis:{
showTitle:true
@@ -728,12 +722,12 @@ export const SYSTEM_MYSERVICE_TABLE_COLUMNS: ProColumns<MyServiceTableListItem>[
onFilter: true,
valueType: 'select',
valueEnum:{
'on':<span className="text-status_success"></span> ,
'off':<span className="text-status_fail"></span>
'on':<span className="text-status_success">{$t('启用')}</span> ,
'off':<span className="text-status_fail">{$t('停用')}</span>
}
},
{
title: '更新时间',
title:$t('更新时间'),
key: 'updateTime',
dataIndex: 'updateTime',
ellipsis:true,
@@ -743,7 +737,7 @@ export const SYSTEM_MYSERVICE_TABLE_COLUMNS: ProColumns<MyServiceTableListItem>[
},
},
{
title: '创建时间',
title:$t('创建时间'),
key: 'createTime',
dataIndex: 'createTime',
width:182,
@@ -756,9 +750,9 @@ export const SYSTEM_MYSERVICE_TABLE_COLUMNS: ProColumns<MyServiceTableListItem>[
export const SYSTEM_UPSTREAM_GLOBAL_CONFIG_TABLE_COLUMNS: ProColumns<GlobalNodeItem & {_id:string}>[] = [
export const SYSTEM_UPSTREAM_GLOBAL_CONFIG_TABLE_COLUMNS: PageProColumns<GlobalNodeItem & {_id:string}>[] = [
{
title: '地址(IP 端口或域名)',
title:$t('地址(IP 端口或域名)'),
dataIndex: 'address',
width: '50%',
formItemProps: {
@@ -768,23 +762,23 @@ export const SYSTEM_UPSTREAM_GLOBAL_CONFIG_TABLE_COLUMNS: ProColumns<GlobalNodeI
{
required: true,
whitespace: true,
message: '此项是必填项',
message: VALIDATE_MESSAGE.required,
},
],
},
ellipsis:true
},
{
title: '权重(0-999',
title:$t('权重(0-999'),
dataIndex: 'weight',
valueType:'digit',
formItemProps: {
className:'p-0 bg-transparent border-none'}
},
{
title: '操作',
title: COLUMNS_TITLE.operate,
valueType: 'option',
width: 90,
btnNums:2,
render: ()=>null
},
];
@@ -793,11 +787,11 @@ export const SYSTEM_UPSTREAM_GLOBAL_CONFIG_TABLE_COLUMNS: ProColumns<GlobalNodeI
export const SYSTEM_INSIDE_APPROVAL_TAB_ITEMS: TabsProps['items'] = [
{
key: '0',
label: '待审批',
label:$t('待审批'),
},
{
key: '1',
label: '已审批',
label: $t('已审批'),
}
];
@@ -806,70 +800,64 @@ export const SYSTEM_INSIDE_APPROVAL_TAB_ITEMS: TabsProps['items'] = [
export const SYSTEM_PUBLISH_TAB_ITEMS: TabsProps['items'] = [
{
key: '0',
label: '发布版本',
label: $t('发布版本'),
},
{
key: '1',
label: '发布申请记录',
label: $t('发布申请记录'),
}
];
export const SYSTEM_SUBSCRIBE_APPROVAL_DETAIL_LIST = [
{
title:'服务名称',key:'service',nested:'name'
title:$t('服务名称'),key:'service',nested:'name'
},
{
title:'服务 ID',key:'applyTeam',nested:'id'
title:$t('服务 ID'),key:'applyTeam',nested:'id'
},
{
title:'所属团队',key:'team',nested:'name'
title:$t('所属团队'),key:'team',nested:'name'
},
{
title:'所属系统',key:'project',nested:'name'
title:$t('所属系统'),key:'project',nested:'name'
},
{
title:'申请状态',key:'status',renderText:()=>{}
title:$t('申请状态'),key:'status',renderText:()=>{}
},
{
title:'申请人',key:'applier',nested:'name'
title:$t('申请人'),key:'applier',nested:'name'
},
// {
// title:'审批人',key:'team',nested:'name'
// },
{
title:'申请时间',key:'applyTime'
title:$t('申请时间'),key:'applyTime'
},
// {
// title:'审批时间',key:'team'
// }
]
export const SYSTEM_TOPOLOGY_NODE_TYPE_COLOR_MAP = {
subscriberProject:{
stroke:'#3291F8FF',
fill: '#3291F8FF',
name:'调用系统名称'
name:$t('调用系统名称')
},
subscriberService:{
stroke:'#3D46F2',
fill: '#7371FC33',
name:'调用服务名称'
name:$t('调用服务名称')
},
curProject:{
stroke:'#7371FCFF',
fill: '#7371FCFF',
name:'当前系统名称'
name:$t('当前系统名称')
},
invokeService:{
stroke:'#3D46F2',
fill: '#7371FC33',
name:'被调用服务名称'
name:$t('被调用服务名称')
},
invokeProject:{
stroke:'#19C56BFF',
fill: '#19C56BFF',
name:'被调用系统名称'
name:$t('被调用系统名称')
},
application:{
stroke:'#ffa940',
@@ -881,7 +869,7 @@ export const SYSTEM_TOPOLOGY_NODE_TYPE_COLOR_MAP = {
export const SYSTEM_PUBLISH_ONLINE_COLUMNS = [
{
title: '上线结果',
title:$t('上线结果'),
dataIndex: 'status',
ellipsis:{
showTitle:true
@@ -889,9 +877,9 @@ export const SYSTEM_TOPOLOGY_NODE_TYPE_COLOR_MAP = {
render:(_:unknown,entity:SystemInsidePublishOnlineItems)=>{
switch(entity.status){
case 'done':
return <span className={STATUS_COLOR[entity.status as keyof typeof STATUS_COLOR]}></span>
return <span className={STATUS_COLOR[entity.status as keyof typeof STATUS_COLOR]}>{$t('成功')}</span>
case 'error':
return <Tooltip title={entity.error || '上线失败'}><span className={`${STATUS_COLOR[entity.status as keyof typeof STATUS_COLOR]} truncate block`}> {entity.error}</span></Tooltip>
return <Tooltip title={entity.error || $t('上线失败')}><span className={`${STATUS_COLOR[entity.status as keyof typeof STATUS_COLOR]} truncate block`}>{$t('失败')} {entity.error}</span></Tooltip>
default:
return <LoadingOutlined className="text-theme" spin />
}
@@ -903,23 +891,23 @@ const APP_MODE = import.meta.env.VITE_APP_MODE;
export const SYSTEM_PAGE_MENU_ITEMS: MenuProps['items'] = [
getItem('服务', 'assets', null,
getItem($t('服务'), 'assets', null,
[
getItem(<Link to="./api">API</Link>, 'api',undefined,undefined,undefined,'team.service.api.view'),
getItem(<Link to="./upstream"></Link>, 'upstream',undefined,undefined,undefined,'team.service.upstream.view'),
getItem(<Link to="./document">使</Link>, 'document',undefined,undefined,undefined,''),
getItem(<Link to="./publish"></Link>, 'publish',undefined,undefined,undefined,'team.service.release.view'),
getItem(<Link to="./api">{$t('API')}</Link>, 'api',undefined,undefined,undefined,'team.service.api.view'),
getItem(<Link to="./upstream">{$t('上游')}</Link>, 'upstream',undefined,undefined,undefined,'team.service.upstream.view'),
getItem(<Link to="./document">{$t('使用说明')}</Link>, 'document',undefined,undefined,undefined,''),
getItem(<Link to="./publish">{$t('发布')}</Link>, 'publish',undefined,undefined,undefined,'team.service.release.view'),
],
'group'),
getItem('订阅管理', 'provideSer', null,
getItem($t('订阅管理'), 'provideSer', null,
[
getItem(<Link to="./approval"></Link>, 'approval',undefined,undefined,undefined,'team.service.subscription.view'),
getItem(<Link to="./subscriber"></Link>, 'subscriber',undefined,undefined,undefined,'team.service.subscription.view'),
getItem(<Link to="./approval">{$t('订阅审批')}</Link>, 'approval',undefined,undefined,undefined,'team.service.subscription.view'),
getItem(<Link to="./subscriber">{$t('订阅方管理')}</Link>, 'subscriber',undefined,undefined,undefined,'team.service.subscription.view'),
],
'group'),
getItem('管理', 'mng', null,
getItem($t('管理'), 'mng', null,
[
APP_MODE === 'pro' ? getItem(<Link to="./topology"></Link>, 'topology',undefined,undefined,undefined,'project.mySystem.topology.view'):null,
getItem(<Link to="./setting"></Link>, 'setting',undefined,undefined,undefined,'')],
APP_MODE === 'pro' ? getItem(<Link to="./topology">{$t('调用拓扑图')}</Link>, 'topology',undefined,undefined,undefined,'project.mySystem.topology.view'):null,
getItem(<Link to="./setting">{$t('设置')}</Link>, 'setting',undefined,undefined,undefined,'')],
'group'),
];
@@ -32,7 +32,7 @@ export type SystemConfigFieldType = {
export type SystemSubServiceTableListItem = {
id:string;
applyStatus:SubscribeEnum;
applyStatus:typeof SubscribeEnum;
project:EntityItem;
team:EntityItem
service:EntityItem
@@ -46,7 +46,7 @@ export type SystemSubServiceTableListItem = {
export type SystemSubscriberTableListItem = {
id:string
service:EntityItem
applyStatus:SubscribeEnum
applyStatus:typeof SubscribeEnum
project:EntityItem
team:EntityItem;
applier:EntityItem
+27 -26
View File
@@ -1,16 +1,17 @@
import { ProColumns } from "@ant-design/pro-components";
import { TeamMemberTableListItem, TeamTableListItem } from "./type";
import { ColumnsType } from "antd/es/table";
import { MemberItem } from "@common/const/type";
import { getItem, getTabItem } from "@common/utils/navigation";
import { getItem } from "@common/utils/navigation";
import { SystemTableListItem } from "../system/type";
import { MenuProps, TabsProps } from "antd/lib";
import { MenuProps } from "antd/lib";
import { Link } from "react-router-dom";
import { $t } from "@common/locales";
import { PageProColumns } from "@common/components/aoplatform/PageList";
export const TEAM_TABLE_COLUMNS: ProColumns<TeamTableListItem>[] = [
export const TEAM_TABLE_COLUMNS: PageProColumns<TeamTableListItem>[] = [
{
title: '名称',
title:$t('名称'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -20,18 +21,18 @@ export const TEAM_TABLE_COLUMNS: ProColumns<TeamTableListItem>[] = [
},
},
{
title: 'ID',
title:$t('ID'),
dataIndex: 'id',
width: 140,
ellipsis:true
},
{
title: '描述',
title: $t('描述'),
dataIndex: 'description',
ellipsis:true
},
{
title: '服务数量',
title:$t('服务数量'),
dataIndex: 'serviceNum',
ellipsis:true,
sorter: (a,b)=> {
@@ -39,7 +40,7 @@ export const TEAM_TABLE_COLUMNS: ProColumns<TeamTableListItem>[] = [
},
},
{
title: '负责人',
title:$t('负责人'),
dataIndex: ['master','name'],
ellipsis: true,
width:108,
@@ -49,7 +50,7 @@ export const TEAM_TABLE_COLUMNS: ProColumns<TeamTableListItem>[] = [
filterSearch: true,
},
{
title: '创建时间',
title:$t('创建时间'),
dataIndex: 'createTime',
ellipsis:true,
width:182,
@@ -60,9 +61,9 @@ export const TEAM_TABLE_COLUMNS: ProColumns<TeamTableListItem>[] = [
];
export const TEAM_SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
export const TEAM_SYSTEM_TABLE_COLUMNS: PageProColumns<SystemTableListItem>[] = [
{
title: '服务名称',
title:$t('服务名称'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -72,18 +73,18 @@ export const TEAM_SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
},
},
{
title: '服务 ID',
title:$t('服务 ID'),
dataIndex: 'id',
width: 140,
ellipsis:true
},
{
title: '所属团队',
title:$t('所属团队'),
dataIndex: ['team','name'],
ellipsis:true
},
{
title: 'API数量',
title:$t('API数量'),
dataIndex: 'apiNum',
ellipsis:true,
sorter: (a,b)=> {
@@ -91,7 +92,7 @@ export const TEAM_SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
},
},
{
title: '服务数量',
title:$t('服务数量'),
dataIndex: 'serviceNum',
ellipsis:true,
sorter: (a,b)=> {
@@ -99,7 +100,7 @@ export const TEAM_SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
},
},
{
title: '负责人',
title:$t('负责人'),
dataIndex: ['master','name'],
ellipsis: true,
width:108,
@@ -109,7 +110,7 @@ export const TEAM_SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
filterSearch: true
},
{
title: '添加日期',
title:$t('添加日期'),
dataIndex: 'createTime',
ellipsis: true,
sorter: (a,b)=> {
@@ -118,9 +119,9 @@ export const TEAM_SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
},
];
export const TEAM_MEMBER_TABLE_COLUMNS: ProColumns<TeamMemberTableListItem>[] = [
export const TEAM_MEMBER_TABLE_COLUMNS: PageProColumns<TeamMemberTableListItem>[] = [
{
title: '姓名',
title:$t('姓名'),
dataIndex: ['user','name'],
ellipsis:true,
width:160,
@@ -130,12 +131,12 @@ export const TEAM_MEMBER_TABLE_COLUMNS: ProColumns<TeamMemberTableListItem>[] =
},
},
{
title: '团队角色',
title:$t('团队角色'),
dataIndex: 'roles',
ellipsis:true,
},
{
title: '添加日期',
title:$t('添加日期'),
dataIndex: 'attachTime',
ellipsis:true,
sorter: (a,b)=> {
@@ -146,7 +147,7 @@ export const TEAM_MEMBER_TABLE_COLUMNS: ProColumns<TeamMemberTableListItem>[] =
export const TEAM_MEMBER_MODAL_TABLE_COLUMNS:ColumnsType<MemberItem> = [
{title:'成员',
{title:$t('成员'),
render:(_,entity)=>{
return <>
<div>
@@ -161,10 +162,10 @@ export const TEAM_MEMBER_MODAL_TABLE_COLUMNS:ColumnsType<MemberItem> = [
]
export const TEAM_INSIDE_MENU_ITEMS: MenuProps['items'] = [
getItem('管理', 'grp', null,
getItem($t('管理'), 'grp', null,
[
getItem(<Link to="member"></Link>, 'member',undefined, undefined, undefined,'team.team.member.view'),
getItem(<Link to="setting"></Link>, 'setting',undefined,undefined,undefined,'team.team.team.edit')],
getItem(<Link to="member">{$t('成员')}</Link>, 'member',undefined, undefined, undefined,'team.team.member.view'),
getItem(<Link to="setting">{$t('设置')}</Link>, 'setting',undefined,undefined,undefined,'team.team.team.edit')],
'group'),
];
@@ -1,12 +1,13 @@
import { ProColumns } from "@ant-design/pro-components";
import { ColumnsType } from "antd/es/table";
import { MemberItem } from "@common/const/type";
import { Tooltip } from "antd";
import { $t } from "@common/locales";
import { PageProColumns } from "@common/components/aoplatform/PageList";
export const USER_LIST_COLUMNS: ProColumns<MemberItem>[]= [
export const USER_LIST_COLUMNS: PageProColumns<MemberItem>[]= [
{
title: '用户名',
title:$t('用户名'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -16,12 +17,12 @@ export const USER_LIST_COLUMNS: ProColumns<MemberItem>[]= [
},
},
{
title: '邮箱',
title:$t('邮箱'),
dataIndex: 'email',
ellipsis:true,
},
{
title: '部门',
title:$t('部门'),
dataIndex: 'department',
ellipsis:{
showTitle:true
@@ -36,7 +37,7 @@ export const USER_LIST_COLUMNS: ProColumns<MemberItem>[]= [
];
export const MEMBER_MODAL_COLUMNS:ColumnsType<MemberItem> = [
{title:'所有成员',
{title:$t('所有成员'),
width:215,
render:(_,entity)=>{
+7 -4
View File
@@ -353,17 +353,20 @@ p{
color:var(--primary-color) !important;
}
button.ant-btn.ant-btn-text{
button.ant-btn.ant-btn-text.ant-btn-icon-only{
height:22px !important;
width:18px !important;
}
button.ant-btn:not(:disabled):not(.text-table_text){
/* button.ant-btn:not(:disabled):not(.text-table_text){
color:var(--primary-color);
}
button.ant-btn:not(:disabled):not(.text-table_text):hover{
color:var(--button-primary-hover-background-color) !important;
}
} */
}
.ant-popover .ant-popover-inner{
@@ -1224,4 +1227,4 @@ div.preview-document{
}
.ant-table-wrapper .ant-table-thead th.ant-table-column-has-sorters{
transition:none !important;
}
}
+11 -10
View File
@@ -2,10 +2,11 @@ import {FC, useCallback, useEffect, useRef, useState} from "react";
import {App, Button, Form, FormInstance, Input} from "antd";
import {useGlobalContext} from "@common/contexts/GlobalStateContext.tsx";
import {useFetch} from "@common/hooks/http.ts";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {BasicResponse, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import {useNavigate} from "react-router-dom";
// import {useCrypto} from "../hooks/crypto.ts";
import Logo from '@common/assets/logo.png'
import { $t } from "@common/locales";
const Login:FC = ()=> {
const {state, dispatch} = useGlobalContext()
@@ -61,7 +62,7 @@ const Login:FC = ()=> {
if (code === STATUS_CODE.SUCCESS) {
dispatch({type:'LOGIN'})
message.success('登录成功');
message.success(RESPONSE_TIPS.loginSuccess);
const callbackUrl = new URLSearchParams(window.location.search).get('callbackUrl');
if (callbackUrl && callbackUrl !== 'null') {
navigate(callbackUrl);
@@ -102,7 +103,7 @@ const Login:FC = ()=> {
<div className="h-full">
<div>
<div className="flex justify-center items-center">
<span className="text-[24px] text-[#101010]"></span>
<span className="text-[24px] text-[#101010]">{$t('登录')}</span>
</div>
<Form onFinish={login} className="w-[350px] pt-[28px]"
@@ -110,11 +111,11 @@ const Login:FC = ()=> {
<Form.Item
className="p-0 bg-transparent rounded border-none"
name="username"
rules={[{ required: true, message: '请输入账号' ,whitespace:true }]}
rules={[{ required: true, message: $t('请输入账号') ,whitespace:true }]}
>
<Input
className="w-[350px] h-[40px]"
placeholder="账号"
placeholder={$t("账号")}
autoComplete="on"
autoFocus
/>
@@ -123,11 +124,11 @@ const Login:FC = ()=> {
<Form.Item
className="p-0 bg-transparent rounded border-none"
name="password"
rules={[{ required: true, message: '请输入密码' }]}
rules={[{ required: true, message: $t('请输入密码') }]}
>
<Input.Password
className="w-[350px] h-[40px]"
placeholder="密码"
placeholder={$t("密码")}
autoComplete="off"
/>
</Form.Item>
@@ -137,7 +138,7 @@ const Login:FC = ()=> {
className="p-0 bg-transparent rounded border-none mb-0"
>
<Button loading={loading} className="h-[40px] mt-mbase w-full inline-flex justify-center items-center" type="primary" htmlType="submit">
{$t('登录')}
</Button>
</Form.Item>
</div>
@@ -147,8 +148,8 @@ const Login:FC = ()=> {
</section>
<section className="flex flex-col items-center mt-[46px] text-SECOND_TEXT">
<p className="leading-[28px]">Version {state.version}-{state.updateDate}</p>
<p className="leading-[28px]">{state.powered}</p>
<p className="leading-[28px]">{$t('Version (0)-(1)',[state.version,state.updateDate])}</p>
<p className="leading-[28px]">{$t(state.powered)}</p>
</section>
</div>
</div>
@@ -1,7 +1,7 @@
import {ActionType, ProColumns} from "@ant-design/pro-components";
import {ActionType} from "@ant-design/pro-components";
import {App, Button} from "antd";
import {useEffect, useMemo, useRef, useState} from "react";
import PageList from "@common/components/aoplatform/PageList.tsx";
import PageList, { PageProColumns } from "@common/components/aoplatform/PageList.tsx";
import {
PUBLISH_APPROVAL_TABLE_COLUMN,
SUBSCRIBE_APPROVAL_TABLE_COLUMN,
@@ -10,9 +10,10 @@ import {
import {
ApprovalTableListItem,
PublishApprovalInfoType,
PublishApprovalModalHandle,
SubscribeApprovalInfoType,
} from "@common/const/approval/type.tsx";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {BasicResponse, COLUMNS_TITLE, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts";
import {
SubscribeApprovalModalContent,
@@ -20,11 +21,12 @@ import {
} from "@common/components/aoplatform/SubscribeApprovalModalContent.tsx";
import {
PublishApprovalModalContent,
PublishApprovalModalHandle
} from "@common/components/aoplatform/PublishApprovalModalContent.tsx";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { SimpleMemberItem } from "@common/const/type.ts";
import TableBtnWithPermission from "@common/components/aoplatform/TableBtnWithPermission.tsx";
import { $t } from "@common/locales";
export default function ApprovalList({pageType,pageStatus}:{pageType:'subscribe'|'release',pageStatus:0|1}){
const { modal,message } = App.useApp()
@@ -52,11 +54,11 @@ export default function ApprovalList({pageType,pageStatus}:{pageType:'subscribe'
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setTableListDataSource(data.approvals)
!init && message.success(msg || '操作成功')
!init && message.success(msg || RESPONSE_TIPS.success)
setInit((prev)=>prev ? false : prev)
return {data:data.approvals, success: true}
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
return {data:[], success:false}
}
}).catch(() => {
@@ -73,12 +75,12 @@ export default function ApprovalList({pageType,pageStatus}:{pageType:'subscribe'
}, [pageType,pageStatus]);
const openModal = async(type:'approval'|'view',entity:ApprovalTableListItem)=>{
message.loading('正在加载数据')
message.loading(RESPONSE_TIPS.loading)
const {code,data,msg} = await fetchData<BasicResponse<{approval:PublishApprovalInfoType|SubscribeApprovalInfoType}>>(`approval/${pageType}`,{method:'GET',eoParams:{id:entity!.id},eoTransformKeys:['apply_project','apply_team','apply_time','approval_time']})
message.destroy()
if(code === STATUS_CODE.SUCCESS){
const modalInst = modal.confirm({
title:type === 'approval' ? '审批' : '查看',
title:type === 'approval' ? $t('审批') : $t('查看'),
content:pageType === 'subscribe' ?
<SubscribeApprovalModalContent ref={subscribeRef} data={data.approval as SubscribeApprovalInfoType} type={type}/>
:<PublishApprovalModalContent ref={publishRef} data={data.approval as PublishApprovalInfoType} type={type} systemId={''}/>,
@@ -90,8 +92,8 @@ export default function ApprovalList({pageType,pageStatus}:{pageType:'subscribe'
}
},
width:600,
okText:type === 'approval' ? '通过' :'确认',
cancelText:'取消',
okText:type === 'approval' ? $t('通过') :$t('确认'),
cancelText:$t('取消'),
closable:true,
onCancel:()=>{setApprovalBtnLoading(false)},
icon:<></>,
@@ -112,7 +114,7 @@ export default function ApprovalList({pageType,pageStatus}:{pageType:'subscribe'
},
})
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
return
}
}
@@ -128,21 +130,21 @@ export default function ApprovalList({pageType,pageStatus}:{pageType:'subscribe'
})
setMemberValueEnum(tmpValueEnum)
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
}
const operation:ProColumns<ApprovalTableListItem>[] =[
const operation:PageProColumns<ApprovalTableListItem>[] =[
{
title: '操作',
title: COLUMNS_TITLE.operate,
key: 'option',
width: 62,
btnNums:1,
fixed:'right',
valueType: 'option',
render: (_: React.ReactNode, entity: ApprovalTableListItem) => [
pageStatus === 0 ?
<TableBtnWithPermission access="" key="approval" onClick={()=>{openModal('approval',entity)}} btnTitle="审批"/>
:<TableBtnWithPermission access="" key="view" onClick={()=>{openModal('view',entity)}} btnTitle="查看"/>,
<TableBtnWithPermission access="" key="approval" btnType="approval" onClick={()=>{openModal('approval',entity)}} btnTitle="审批"/>
:<TableBtnWithPermission access="" key="view" btnType="publish" onClick={()=>{openModal('view',entity)}} btnTitle="查看"/>,
]
}
]
@@ -4,24 +4,25 @@ import {Link, useLocation, useNavigate} from "react-router-dom";
import {useEffect, useState} from "react";
import ApprovalList from "./ApprovalList.tsx";
import { getItem } from "@common/utils/navigation.tsx";
import { $t } from "@common/locales/index.ts";
const menuItems: MenuProps['items'] = [
getItem('管理', 'mng', null,
getItem($t('管理'), 'mng', null,
[
getItem(<Link to="/approval?type=subscribe"></Link>, 'subscribe'),
getItem(<Link to="/approval?type=release"></Link>, 'release')],
getItem(<Link to="/approval?type=subscribe">{$t('订阅申请')}</Link>, 'subscribe'),
getItem(<Link to="/approval?type=release">{$t('发布申请')}</Link>, 'release')],
'group'),
];
const items: TabsProps['items'] = [
{
key: '0',
label: '待审批',
label: $t('待审批'),
},
{
key: '1',
label: '已审批',
label: $t('已审批'),
}
];

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