mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
feat: Complete static pages for Phase 1 of V1.3
This commit is contained in:
@@ -75,21 +75,20 @@ const themeToken = {
|
||||
if (filteredRoutes.length === 0) {
|
||||
return false
|
||||
}
|
||||
return { ...item, routes: filteredRoutes };
|
||||
return { ...item,routes: filteredRoutes,name:$t(item.name) };
|
||||
}
|
||||
// 处理没有 routes 的菜单项
|
||||
if (item.access) {
|
||||
return (item.access === 'all' || hasAccess(item.access)) ? item : null;
|
||||
return (item.access === 'all' || hasAccess(item.access)) ? {...item,name:$t(item.name)} : null;
|
||||
}
|
||||
|
||||
// 如果没有 access 和 routes,则保留
|
||||
return item;
|
||||
return {...item,name:$t(item.name) };
|
||||
})
|
||||
.filter(x => x); // 过滤掉处理后为 null 的项
|
||||
};
|
||||
|
||||
// 初始过滤操作
|
||||
const res = [...(menuItems || [])]!.filter(x => x).map((x: any) => (x.routes ? { ...x, routes: filterMenu(x.routes) } : x));
|
||||
const res = [...(menuItems || [])]!.filter(x => x).map((x: any) => (x.routes ? { ...x,name:$t(x.name), routes: filterMenu(x.routes) } : {...x,name:$t(x.name)}));
|
||||
// 返回处理后的数据
|
||||
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, state.language,menuItems]);
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import {App, Form, Input, Row, Table} from "antd";
|
||||
import {forwardRef, useEffect, useImperativeHandle, useMemo} from "react";
|
||||
import {useFetch} from "@common/hooks/http.ts";
|
||||
import {BasicResponse, PLACEHOLDER, PolicyPublishColumns, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
|
||||
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
|
||||
import { $t } from "@common/locales";
|
||||
import { useGlobalContext } from "@common/contexts/GlobalStateContext";
|
||||
import { PolicyPublishModalHandle, PolicyPublishModalProps } from "@common/const/type";
|
||||
|
||||
|
||||
export const PolicyPublishModalContent = forwardRef<PolicyPublishModalHandle,PolicyPublishModalProps>((props, ref) => {
|
||||
const { message } = App.useApp()
|
||||
const { data} = props
|
||||
const [form] = Form.useForm();
|
||||
const {fetchData} = useFetch()
|
||||
const {state} = useGlobalContext()
|
||||
|
||||
const publish:()=>Promise<boolean | string | Record<string, unknown>> = ()=>{
|
||||
return new Promise((resolve, reject)=>{
|
||||
form.validateFields().then((value)=>{
|
||||
const body = {...value, source:data.source}
|
||||
fetchData<BasicResponse<null>>('strategy/global/data-masking/publish',{method: 'POST',eoBody:body,eoTransformKeys:['versionName']}).then(response=>{
|
||||
const {code,msg} = response
|
||||
if(code === STATUS_CODE.SUCCESS){
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
resolve(response)
|
||||
}else{
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
reject(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
}).catch((errorInfo)=> reject(errorInfo))
|
||||
}).catch((errorInfo)=> reject(errorInfo))
|
||||
})
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, ()=>({
|
||||
publish,
|
||||
})
|
||||
)
|
||||
|
||||
useEffect(()=>{
|
||||
form.setFieldsValue(data)
|
||||
},[data])
|
||||
|
||||
const translatedPolicyColumns = useMemo(()=>PolicyPublishColumns.map((x)=>({
|
||||
...x,
|
||||
title: typeof x.title === 'string' ? $t(x.title) : x.title,
|
||||
})),[state.language])
|
||||
|
||||
console.log(translatedPolicyColumns,data.strategies)
|
||||
|
||||
return (
|
||||
<>
|
||||
<WithPermission access=""><Form
|
||||
className=" mx-auto"
|
||||
form={form}
|
||||
labelAlign='left'
|
||||
layout='vertical'
|
||||
scrollToFirstError
|
||||
name="publishApprovalModalContent"
|
||||
// labelCol={{span: 3}}
|
||||
// wrapperCol={{span: 21}}
|
||||
autoComplete="off"
|
||||
>
|
||||
|
||||
<Form.Item
|
||||
label={$t("发布名称")}
|
||||
name='versionName'
|
||||
rules={[{required: true,whitespace:true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={$t("描述")}
|
||||
name="desc"
|
||||
>
|
||||
<Input.TextArea className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
<Row className="mt-mbase pb-[8px] h-[32px] font-bold" ><span >{$t('策略列表')}:</span></Row>
|
||||
<Row className="mb-mbase ">
|
||||
<Table
|
||||
columns={translatedPolicyColumns}
|
||||
bordered={true}
|
||||
rowKey="name"
|
||||
size="small"
|
||||
dataSource={data.strategies || []}
|
||||
pagination={false}
|
||||
/>
|
||||
{!data?.isPublish&& data?.unpublishMsg&& <p className="text-status_fail mt-[4px]">{data.unpublishMsg}</p>}
|
||||
</Row>
|
||||
|
||||
</Form>
|
||||
</WithPermission>
|
||||
</>)
|
||||
})
|
||||
@@ -163,6 +163,22 @@ export const TranslateWord = ()=>{
|
||||
{$t('优先级')}
|
||||
{$t('筛选条件')}
|
||||
{$t('处理数')}
|
||||
{$t('数据格式')}
|
||||
{$t('关键字')}
|
||||
{$t('正则表达式')}
|
||||
{$t('手机号')}
|
||||
{$t('身份证号')}
|
||||
{$t('银行卡号')}
|
||||
{$t('金额')}
|
||||
{$t('日期')}
|
||||
{$t('局部显示')}
|
||||
{$t('局部遮蔽')}
|
||||
{$t('截取')}
|
||||
{$t('替换')}
|
||||
{$t('乱序')}
|
||||
{$t('随机字符串')}
|
||||
{$t('自定义字符串')}
|
||||
{$t('请输入IP地址或CIDR范围,每条以换行分割')}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,5 @@
|
||||
import { ProtectedRoute } from '@core/components/aoplatform/RenderRoutes'
|
||||
import { AiServiceProvider } from '@core/contexts/AiServiceContext'
|
||||
import { SystemProvider } from '@core/contexts/SystemContext'
|
||||
import { TeamProvider } from '@core/contexts/TeamContext'
|
||||
import { TenantManagementProvider } from '@market/contexts/TenantManagementContext'
|
||||
import { lazy } from 'react'
|
||||
import { Outlet, Navigate } from 'react-router-dom'
|
||||
import { $t } from '@common/locales'
|
||||
import { StrategyStatusColorClass, StrategyStatusEnum } from './policy/consts'
|
||||
|
||||
export type BasicResponse<T> = {
|
||||
code:number
|
||||
@@ -24,7 +19,7 @@ export const STATUS_COLOR = {
|
||||
}
|
||||
|
||||
|
||||
// avoid changing route within ths same category
|
||||
// TODO should be generated dynamically
|
||||
export const routerKeyMap = new Map<string, string[]|string>([
|
||||
['workspace',['consumer','service','team','guide']],
|
||||
['my',['consumer','service','team']],
|
||||
@@ -52,6 +47,7 @@ export const routerKeyMap = new Map<string, string[]|string>([
|
||||
startWithAlphabet:('英文数字下划线任意一种,首字母必须为英文'),
|
||||
specialStartWithAlphabet:('支持字母开头、英文数字中横线下划线组合'),
|
||||
onlyAlphabet:('字符非法,仅支持英文'),
|
||||
ipAndCidr:'请输入IP地址或CIDR范围,每条以换行分割'
|
||||
}
|
||||
|
||||
export const FORM_ERROR_TIPS = {
|
||||
@@ -81,3 +77,32 @@ export const routerKeyMap = new Map<string, string[]|string>([
|
||||
{label:'列表', value:'list'},
|
||||
{label:'块', value:'block'},
|
||||
]
|
||||
|
||||
|
||||
export const PolicyPublishColumns = [
|
||||
|
||||
{
|
||||
title: ('策略名称'),
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
width: 160
|
||||
},
|
||||
{
|
||||
title: ('优先级'),
|
||||
dataIndex: 'priority',
|
||||
width: 140,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: ('状态'),
|
||||
dataIndex: 'status',
|
||||
width: 140,
|
||||
render:(text:string)=> <span className={StrategyStatusColorClass[text as keyof typeof StrategyStatusColorClass]}>{$t(StrategyStatusEnum[text as keyof typeof StrategyStatusEnum])}</span>,
|
||||
},
|
||||
{
|
||||
title: ('更新时间'),
|
||||
dataIndex: 'optTime',
|
||||
width: 182,
|
||||
ellipsis: true,
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
export const MatchRules = [
|
||||
{ value: 'inner', label: '数据格式' },
|
||||
{ value: 'keyword', label: '关键字' },
|
||||
{ value: 'regex', label: '正则表达式' },
|
||||
{ value: 'json_path', label: 'JSON Path' }
|
||||
];
|
||||
|
||||
|
||||
export const DataFormatOptions = [
|
||||
{ label: '姓名', value: 'name' },
|
||||
{ label: '手机号', value: 'phone' },
|
||||
{ label: '身份证号', value: 'id-card' },
|
||||
{ label: '银行卡号', value: 'bank-card' },
|
||||
{ label: '日期', value: 'date' },
|
||||
{ label: '金额', value: 'amount' }
|
||||
];
|
||||
|
||||
|
||||
export const DataMaskBaseOptionOptions = [
|
||||
{ value: 'partial-display', label: '局部显示' },
|
||||
{ value: 'partial-masking', label: '局部遮蔽' },
|
||||
{ value: 'truncation', label: '截取' },
|
||||
{ value: 'replacement', label: '替换' },
|
||||
];
|
||||
|
||||
|
||||
export const DataMaskOrderOptions = [
|
||||
...DataMaskBaseOptionOptions,
|
||||
{ label: '乱序', value: 'shuffling' }
|
||||
]
|
||||
|
||||
|
||||
export const DataMaskReplaceStrOptions = [
|
||||
{ value: 'random', label: '随机字符串' },
|
||||
{ value: 'custom', label: '自定义字符串' }
|
||||
];
|
||||
|
||||
|
||||
export const PolicyOptions = [
|
||||
{label:'数据脱敏',value:'data-masking'},
|
||||
]
|
||||
|
||||
export const StrategyStatusEnum = {
|
||||
'update':'待更新',
|
||||
'online':'已发布',
|
||||
'offline':'未发布',
|
||||
"delete":'待删除',
|
||||
}
|
||||
|
||||
export const StrategyStatusColorClass = {
|
||||
"online":'text-status_success',
|
||||
"update":'text-status_pending',
|
||||
"offline":'text-status_fail',
|
||||
"delete":'text-status_offline',
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import { DefaultOptionType } from "antd/es/select";
|
||||
import { StrategyStatusEnum } from "./consts";
|
||||
|
||||
export type DataMaskRuleTableProps = {
|
||||
disabled?: boolean;
|
||||
value?: MaskRuleData[];
|
||||
onChange?: (value: MaskRuleData[]) => void;
|
||||
}
|
||||
|
||||
|
||||
export type MaskRuleData = {
|
||||
match: {
|
||||
type: string;
|
||||
value: string;
|
||||
};
|
||||
mask: {
|
||||
type: string;
|
||||
begin?: number;
|
||||
length?: number;
|
||||
replace?: {
|
||||
type: string;
|
||||
value: string;
|
||||
};
|
||||
};
|
||||
eoKey?: string;
|
||||
}
|
||||
|
||||
export type DataMaskRuleFormProps = {
|
||||
editData?: MaskRuleData;
|
||||
ruleList: MaskRuleData[];
|
||||
onSave: (ruleList: MaskRuleData[]) => void;
|
||||
onClose: () => void;
|
||||
modalVisible:boolean
|
||||
}
|
||||
|
||||
|
||||
export type DataMaskingConfigHandle = {
|
||||
save: (values: any) => void
|
||||
}
|
||||
|
||||
export type PolicyMatchType = {name:string, values:string[], label?:string, title?:string, type?:string}
|
||||
|
||||
export type DataMaskingRulesType = {}
|
||||
|
||||
export type DataMaskingConfigFieldType = {
|
||||
id:string
|
||||
name:string
|
||||
priority:number
|
||||
description:string
|
||||
filters:PolicyMatchType[]
|
||||
config:{
|
||||
rules:DataMaskingRulesType
|
||||
}
|
||||
}
|
||||
|
||||
export type DataMaskStrategyItem = {
|
||||
id:string
|
||||
name:string
|
||||
priority:number
|
||||
isStop:boolean
|
||||
isDeleted:boolean
|
||||
publishStatus:keyof typeof StrategyStatusEnum
|
||||
filters:string
|
||||
conf:string
|
||||
operator:string
|
||||
updateTime:string
|
||||
}
|
||||
|
||||
|
||||
export type FilterFormField= {
|
||||
name: string;
|
||||
value:string[] |string;
|
||||
label:string
|
||||
title:string
|
||||
}
|
||||
|
||||
export type FilterOptionType = {
|
||||
name:string
|
||||
pattern:string
|
||||
title:string
|
||||
type:'remote'|'pattern'|'static'
|
||||
options:string[]
|
||||
}
|
||||
|
||||
|
||||
export type FilterTableProps = {
|
||||
disabled?: boolean;
|
||||
drawerTitle?: string;
|
||||
value?:FilterFormField[];
|
||||
onChange?:(val:FilterFormField[])=>void
|
||||
}
|
||||
|
||||
export type FilterFormType = {
|
||||
name:string
|
||||
value:unknown
|
||||
}
|
||||
|
||||
export type FilterFormProps = {
|
||||
filterForm: FilterFormType;
|
||||
filterOptions:DefaultOptionType[];
|
||||
selectedOptionNameSet: Set<string>;
|
||||
disabled: boolean;
|
||||
onFilterFormChange: (form: FilterFormType) => void;
|
||||
setFormCanSubmit:(canSubmit:boolean)=>void
|
||||
}
|
||||
|
||||
export type FilterFormHandle = {
|
||||
clear:()=>void
|
||||
save:()=>Promise<FilterFormField>
|
||||
}
|
||||
|
||||
export type FilterFormItemProps = {
|
||||
value?: string[];
|
||||
onChange?: (value: string[]) => void;
|
||||
disabled:boolean
|
||||
option:unknown
|
||||
onShowValueChange?:(value:string)=>void
|
||||
}
|
||||
|
||||
export type RemoteTitleType = {
|
||||
title:string
|
||||
field:string
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { PERMISSION_DEFINITION } from "./permissions"
|
||||
import { MatchPositionEnum, MatchTypeEnum } from "@core/const/system/const"
|
||||
import usePluginLoader from "@common/hooks/pluginLoader"
|
||||
import { useGlobalContext } from "@common/contexts/GlobalStateContext"
|
||||
import { StrategyStatusEnum } from "./policy/consts"
|
||||
|
||||
export type UserInfoType = {
|
||||
username: string
|
||||
@@ -141,6 +142,7 @@ export type RouterParams = {
|
||||
roleType:string
|
||||
roleId:string
|
||||
routeId:string
|
||||
policyId:string
|
||||
}
|
||||
|
||||
|
||||
@@ -185,4 +187,28 @@ export type RouterMapConfig = {
|
||||
data?:Record<string, string>
|
||||
pathMatch?:string
|
||||
}
|
||||
|
||||
|
||||
|
||||
export type PolichPublishItemType = {
|
||||
name:string
|
||||
priority:number
|
||||
status:keyof typeof StrategyStatusEnum
|
||||
optTime:string
|
||||
}
|
||||
|
||||
// 发布详情(版本)
|
||||
export type PolicyPublishInfoType = {
|
||||
source:string
|
||||
strategies:Array<PolichPublishItemType>
|
||||
isPublish:boolean
|
||||
versionName:string
|
||||
unpublishMsg:string
|
||||
};
|
||||
|
||||
export type PolicyPublishModalProps = {
|
||||
data:PolicyPublishInfoType
|
||||
}
|
||||
|
||||
export type PolicyPublishModalHandle = {
|
||||
publish:()=>Promise<boolean|string|Record<string, unknown>>
|
||||
}
|
||||
@@ -91,14 +91,14 @@ export type GlobalAction =
|
||||
},
|
||||
{
|
||||
"name": "仪表盘",
|
||||
"key": "mainPage",
|
||||
"key": "analytics",
|
||||
"path": "/analytics",
|
||||
"icon": "ic:baseline-bar-chart",
|
||||
"children": [
|
||||
{
|
||||
"name": "运行视图",
|
||||
"key": "analytics",
|
||||
"path": "/analytics/total",
|
||||
"path": "/analytics",
|
||||
"icon": "ic:baseline-bar-chart",
|
||||
"access": "system.analysis.run_view.view"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { createContext, useContext, useState, useEffect, useMemo } from 'react';
|
||||
import React, { createContext, useContext, useState, useEffect, useMemo, FC, ReactNode } from 'react';
|
||||
import { ConfigProviderProps } from 'antd';
|
||||
import zhCN from 'antd/locale/zh_CN';
|
||||
import enUS from 'antd/locale/en_US';
|
||||
@@ -21,12 +21,9 @@ const languageMap: Record<string, Locale> = {
|
||||
const LocaleContext = createContext<{
|
||||
locale: Locale;
|
||||
setLocale: (locale: string) => void;
|
||||
}>({
|
||||
locale: zhCN,
|
||||
setLocale: () => {},
|
||||
});
|
||||
}|undefined>(undefined);
|
||||
|
||||
export const LocaleProvider: React.FC = ({ children }) => {
|
||||
export const LocaleProvider: FC<{children:ReactNode}> = ({ children }) => {
|
||||
const [locale, setLocaleState] = useState<Locale>(zhCN);
|
||||
|
||||
const setLocale = (language: string) => {
|
||||
@@ -41,4 +38,10 @@ export const LocaleProvider: React.FC = ({ children }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const useLocaleContext = () => useContext(LocaleContext);
|
||||
export const useLocaleContext = () => {
|
||||
const context = useContext(LocaleContext);
|
||||
if (!context) {
|
||||
throw new Error('useLocaleContext must be used within a LocaleContext');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,32 +1,8 @@
|
||||
{
|
||||
"工作空间": "Kc0e5ef9f",
|
||||
"首页": "K4de11e23",
|
||||
"服务": "Kb58e0c3f",
|
||||
"消费者": "K7acfcfad",
|
||||
"团队": "Kc9e489f5",
|
||||
"API 市场": "K61c89f5f",
|
||||
"仪表盘": "K16d71239",
|
||||
"运行视图": "K714c192d",
|
||||
"系统拓扑图": "Kd57dfe97",
|
||||
"系统设置": "K3fe97dcc",
|
||||
"系统": "Kecbb0e45",
|
||||
"常规": "Ka358e23d",
|
||||
"API 网关": "K449058e9",
|
||||
"AI 模型": "K99935e6f",
|
||||
"用户": "K1deaa2dd",
|
||||
"账号": "K80a560a1",
|
||||
"角色": "Kf644225f",
|
||||
"集成": "K4057391a",
|
||||
"数据源": "K8fa58214",
|
||||
"全局策略": "Ke8cbb878",
|
||||
"证书": "K481e8a05",
|
||||
"日志": "Kca53edd0",
|
||||
"资源": "Kb283e720",
|
||||
"Open API": "K631d646f",
|
||||
"账号设置": "K6535ff9c",
|
||||
"退出登录": "Kf15499b4",
|
||||
"文档": "Kabbd6e6",
|
||||
"APIPark - 企业API数据开放平台": "K1196b104",
|
||||
"APIPark": "K630c9e6d",
|
||||
"HTTP 状态码": "K1f42de3",
|
||||
"系统状态码": "K4770dff4",
|
||||
"描述": "Kf89e58f1",
|
||||
@@ -44,6 +20,8 @@
|
||||
"ID": "K11d3633a",
|
||||
"名称": "Kbff43de3",
|
||||
"Driver": "K16ca79ef",
|
||||
"资源": "Kb283e720",
|
||||
"日志": "Kca53edd0",
|
||||
"已发布": "K7a369eef",
|
||||
"下线": "Kcfa1a4d2",
|
||||
"上线": "K771dc3b7",
|
||||
@@ -51,6 +29,8 @@
|
||||
"删除": "Kecbd7449",
|
||||
"确认": "K1cbe2507",
|
||||
"搜索(0)名称": "K48325b6",
|
||||
"发布名称": "Ka3e9f580",
|
||||
"策略列表": "Kb2480682",
|
||||
"上下文": "Kc6340091",
|
||||
"查询内容": "K74ecb1fa",
|
||||
"会话历史": "K79f2e2f9",
|
||||
@@ -235,6 +215,26 @@
|
||||
"地址": "K78b1ca25",
|
||||
"新增": "K1644b775",
|
||||
"申请方消费者": "Kec91f0db",
|
||||
"策略名称": "K931615d7",
|
||||
"优先级": "K31faa2a1",
|
||||
"筛选条件": "Kbdec9fa",
|
||||
"处理数": "Kbcbb7391",
|
||||
"数据格式": "K118d8d74",
|
||||
"关键字": "Kfe7c7d2d",
|
||||
"正则表达式": "K2f57a694",
|
||||
"手机号": "K8953e0a6",
|
||||
"身份证号": "K6f86a038",
|
||||
"银行卡号": "K7954e7c8",
|
||||
"金额": "K320fdb17",
|
||||
"日期": "K7867acda",
|
||||
"局部显示": "K7d327ae8",
|
||||
"局部遮蔽": "Kfbf38e3c",
|
||||
"截取": "Kd8c1fbb0",
|
||||
"替换": "K89829921",
|
||||
"乱序": "K480a7165",
|
||||
"随机字符串": "Kea0d69df",
|
||||
"自定义字符串": "Ke7c84d1d",
|
||||
"请输入IP地址或CIDR范围,每条以换行分割": "K49731763",
|
||||
"暂无操作权限,请联系管理员分配。": "K23fda291",
|
||||
"微信小程序": "K4618cb0a",
|
||||
"获取文件,需填路径": "Ka854f511",
|
||||
@@ -322,11 +322,14 @@
|
||||
"至": "K7e1ab4b0",
|
||||
"详情": "Kf1b166e7",
|
||||
"暂不支持带有双斜杠//的url": "K28555332",
|
||||
"输入的IP或CIDR不符合格式": "K83237c89",
|
||||
"请正确输入路径,如/usr/*或*/usr/*": "K5ae2c87a",
|
||||
"必填项": "K71661ee8",
|
||||
"不是有效邮箱地址": "Kcbee3f8",
|
||||
"最近一次更新者": "K617f34f1",
|
||||
"最近一次更新时间": "K6ebca204",
|
||||
"保存": "Kabfe9512",
|
||||
"服务": "Kb58e0c3f",
|
||||
"API 路由": "K51d1eb5d",
|
||||
"API 文档": "Ka2b6d281",
|
||||
"使用说明": "Kdefa9caa",
|
||||
@@ -402,11 +405,13 @@
|
||||
"现在你可以通过 Token 来调用这些 API。": "Kd6d7ca1f",
|
||||
"快速接入 REST API": "K86cf95f",
|
||||
"创建 REST 服务和 API": "K7a3a8417",
|
||||
"仪表盘": "K16d71239",
|
||||
"统计 API 调用情况": "K4a84214e",
|
||||
"仪表盘中提供了多种统计图表,帮助我们了解 API 的运行情况。": "K297d8563",
|
||||
"核心功能": "K2cdbb773",
|
||||
"账号与角色": "K3378c50d",
|
||||
"邀请你的团队成员加入 APIPark,共同管理和调用 API。": "Kda5bb930",
|
||||
"团队": "Kc9e489f5",
|
||||
"团队中包含了人员、消费者和服务,不同团队之间的消费者和服务数据是隔离的,可用于管理企业内部不同的部门/项目组/团队。": "Keee27105",
|
||||
"服务内包含一组 API,并且可以发布到 API 市场被其他团队使用。": "Kd5be0cd7",
|
||||
"权限管理": "K62e89ee7",
|
||||
@@ -414,6 +419,7 @@
|
||||
"如果需要调用某个服务的 API,需要先订阅该服务,并且等待提供服务的团队审核后才可发起 API 请求。": "Kf2410413",
|
||||
"审核订阅申请": "K6c2e44b8",
|
||||
"提供服务的团队可以审核来自其他团队的订阅申请,审核通过后的消费者才可发起 API 请求。": "Kaa717866",
|
||||
"集成": "K4057391a",
|
||||
"APIPark 提供详尽的 API 调用日志,帮助企业监控、分析和审计 API 的运行状况。": "K3453272",
|
||||
"Hello!欢迎使用 APIPark": "Kd518ba3e",
|
||||
"你能通过 APIPark 快速在企业内部构建 API 开放门户/市场,享受极致的转发性能、API 可观测、服务治理、多租户管理、订阅审核流程等诸多好处。": "K7e04ea16",
|
||||
@@ -425,6 +431,7 @@
|
||||
"了解更多功能": "K48f7e21f",
|
||||
"隐藏该教程": "K698296e2",
|
||||
"请输入账号": "Kf076f63c",
|
||||
"账号": "K80a560a1",
|
||||
"请输入密码": "K25c895d5",
|
||||
"密码": "K551b0348",
|
||||
"登录": "Kd2c1a316",
|
||||
@@ -467,6 +474,7 @@
|
||||
"密钥": "K8ef69ee2",
|
||||
"上传密钥": "Kba3507d6",
|
||||
"密钥文件的后缀名一般为 .key 的文件内容": "K93ac0f23",
|
||||
"证书": "K481e8a05",
|
||||
"上传证书": "K7cdd1331",
|
||||
"证书文件的后缀名一般为 .crt 或 .pem 的文件内容": "K6d91905d",
|
||||
"添加证书": "Kd0f6ded7",
|
||||
@@ -485,19 +493,39 @@
|
||||
"同步地址": "K2a49373f",
|
||||
"集群地址": "K5878440c",
|
||||
"下一步": "K5e9022f8",
|
||||
"数据源": "K8fa58214",
|
||||
"设置监控报表的数据来源,设置完成之后即可获得详细的API调用统计图表。": "Kdbafd6f9",
|
||||
"统计图表": "K1358acf",
|
||||
"地址(IP:端口)": "K62dabdf6",
|
||||
"组织(Organization)": "K2db12335",
|
||||
"添加策略": "K34d0d409",
|
||||
"输入名称、筛选条件查找": "Kbb4298ac",
|
||||
"策略名称": "K931615d7",
|
||||
"优先级": "K31faa2a1",
|
||||
"筛选条件": "Kbdec9fa",
|
||||
"处理数": "Kbcbb7391",
|
||||
"编辑策略": "Kc82b8374",
|
||||
"策略类型": "K4b34a5e5",
|
||||
"匹配条件": "K57f0fee8",
|
||||
"数据脱敏规则": "K10650c58",
|
||||
"配置脱敏规则": "K1b34a9ab",
|
||||
"匹配值": "K26d22405",
|
||||
"脱敏类型": "K1546e1fe",
|
||||
"起始位置": "K9b9b0629",
|
||||
"长度": "K52c84fe1",
|
||||
"替换类型": "Kde84409c",
|
||||
"替换值": "K338653b4",
|
||||
"JSON Path": "Kbaeed3b7",
|
||||
"脱敏规则": "K4cd91d61",
|
||||
"自定义字符串; 值:": "K8dcad979",
|
||||
"起始位置:(0)位;长度:(1)位": "K82e3f7b7",
|
||||
"编辑": "Kad207008",
|
||||
"已选择(0)项(1)数据": "K49dfc123",
|
||||
"所有(0)": "K8457ea34",
|
||||
"属性名称": "K7ca9a795",
|
||||
"属性值": "Kc4391744",
|
||||
"配置(0)": "K678e13fc",
|
||||
"数据脱敏": "Kabac9caf",
|
||||
"全局策略": "Ke8cbb878",
|
||||
"支持对系统全局进行统一的策略配置,从而简化管理并确保一致性。全局策略的优先级比服务策略略低。": "Kc975cd5a",
|
||||
"资源配置": "K8e7a0f80",
|
||||
"角色": "Kf644225f",
|
||||
"设置角色的权限范围。": "K95c3fd8b",
|
||||
"系统级别角色": "K138facd3",
|
||||
"添加角色": "K6eac768d",
|
||||
@@ -578,6 +606,7 @@
|
||||
"退出全屏": "Kaf70c3b",
|
||||
"(0)调用详情": "Kd22841a4",
|
||||
"消费者调用统计": "K61cca533",
|
||||
"消费者": "K7acfcfad",
|
||||
"请选择消费者": "Kdfff59d4",
|
||||
"调用趋势": "K8c7f2d2e",
|
||||
"(0)-(1)调用趋势": "K657c3452",
|
||||
@@ -610,6 +639,7 @@
|
||||
"暂无调用量统计数据": "Kcd125e4d",
|
||||
"暂无报文量统计数据": "Kaa114e8b",
|
||||
"报文量统计": "K3ad84406",
|
||||
"运行视图": "K714c192d",
|
||||
"集群配置并开启监控": "Kfa088d49",
|
||||
"监控功能用于辅助管理集群内信息,请配置集群、设置监控信息后查看当前集群监控情况;": "K3da3b9a0",
|
||||
"集群配置": "Kaddacfb",
|
||||
@@ -699,6 +729,7 @@
|
||||
"配置 Open Api": "K7829bb78",
|
||||
"Open Api": "Kcdf76005",
|
||||
"调用服务": "Ke2601944",
|
||||
"系统拓扑图": "Kd57dfe97",
|
||||
"放大": "K8504bca8",
|
||||
"缩小": "K693c1b41"
|
||||
}
|
||||
@@ -703,5 +703,46 @@
|
||||
"K31faa2a1": "Priority",
|
||||
"Kbdec9fa": "Filter Criteria",
|
||||
"Kbcbb7391": "Processed Count",
|
||||
"Kad207008": "Edit"
|
||||
"Kad207008": "Edit",
|
||||
"K630c9e6d": "APIPark",
|
||||
"Ka3e9f580": "Release Name",
|
||||
"Kb2480682": "Policy List",
|
||||
"K118d8d74": "Data Format",
|
||||
"Kfe7c7d2d": "Keyword",
|
||||
"K2f57a694": "Regular Expression",
|
||||
"K8953e0a6": "Mobile Number",
|
||||
"K6f86a038": "ID Card Number",
|
||||
"K7954e7c8": "Bank Card Number",
|
||||
"K320fdb17": "Amount",
|
||||
"K7867acda": "Date",
|
||||
"K7d327ae8": "Partial Display",
|
||||
"Kfbf38e3c": "Partial Masking",
|
||||
"Kd8c1fbb0": "Truncate",
|
||||
"K89829921": "Replace",
|
||||
"K480a7165": "Shuffle",
|
||||
"Kea0d69df": "Random String",
|
||||
"Ke7c84d1d": "Custom String",
|
||||
"K49731763": "Please enter IP address or CIDR range, each separated by a newline.",
|
||||
"K83237c89": "The entered IP or CIDR does not meet the format.",
|
||||
"K5ae2c87a": "Please enter the correct path, such as /usr/* or */usr/*.",
|
||||
"Kc82b8374": "Edit Policy",
|
||||
"K4b34a5e5": "Policy Type",
|
||||
"K57f0fee8": "Match Conditions",
|
||||
"K10650c58": "Data Masking Rules",
|
||||
"K1b34a9ab": "Configure Masking Rules",
|
||||
"K26d22405": "Match Value",
|
||||
"K1546e1fe": "Masking Type",
|
||||
"K9b9b0629": "Starting Position",
|
||||
"K52c84fe1": "Length",
|
||||
"Kde84409c": "Replace Type",
|
||||
"K338653b4": "Replace Value",
|
||||
"Kbaeed3b7": "JSON Path",
|
||||
"K4cd91d61": "Masking Rules",
|
||||
"K8dcad979": "Custom String; Value:",
|
||||
"K82e3f7b7": "Starting Position: (0); Length: (1)",
|
||||
"K49dfc123": "Selected (0) items (1) data",
|
||||
"K8457ea34": "All (0)",
|
||||
"K7ca9a795": "Attribute Name",
|
||||
"Kc4391744": "Attribute Value",
|
||||
"K678e13fc": "Configure (0)"
|
||||
}
|
||||
|
||||
@@ -725,5 +725,46 @@
|
||||
"K31faa2a1": "優先度",
|
||||
"Kbdec9fa": "フィルタ条件",
|
||||
"Kbcbb7391": "処理数",
|
||||
"Kad207008": "編集"
|
||||
"Kad207008": "編集",
|
||||
"K630c9e6d": "APIPark",
|
||||
"Ka3e9f580": "リリース名",
|
||||
"Kb2480682": "ポリシーリスト",
|
||||
"K118d8d74": "データフォーマット",
|
||||
"Kfe7c7d2d": "キーワード",
|
||||
"K2f57a694": "正規表現",
|
||||
"K8953e0a6": "携帯番号",
|
||||
"K6f86a038": "IDカード番号",
|
||||
"K7954e7c8": "銀行カード番号",
|
||||
"K320fdb17": "金額",
|
||||
"K7867acda": "日付",
|
||||
"K7d327ae8": "部分表示",
|
||||
"Kfbf38e3c": "部分マスキング",
|
||||
"Kd8c1fbb0": "切り取り",
|
||||
"K89829921": "置換",
|
||||
"K480a7165": "シャッフル",
|
||||
"Kea0d69df": "ランダム文字列",
|
||||
"Ke7c84d1d": "カスタム文字列",
|
||||
"K49731763": "IPアドレスまたはCIDR範囲を入力してください。各行で改行してください。",
|
||||
"K83237c89": "入力されたIPまたはCIDRがフォーマットに一致しません。",
|
||||
"K5ae2c87a": "正しいパスを入力してください。例: /usr/* または */usr/*。",
|
||||
"Kc82b8374": "ポリシーを編集",
|
||||
"K4b34a5e5": "ポリシータイプ",
|
||||
"K57f0fee8": "一致条件",
|
||||
"K10650c58": "データマスキングルール",
|
||||
"K1b34a9ab": "マスキングルールを設定",
|
||||
"K26d22405": "一致値",
|
||||
"K1546e1fe": "マスキングタイプ",
|
||||
"K9b9b0629": "開始位置",
|
||||
"K52c84fe1": "長さ",
|
||||
"Kde84409c": "置換タイプ",
|
||||
"K338653b4": "置換値",
|
||||
"Kbaeed3b7": "JSON パス",
|
||||
"K4cd91d61": "マスキングルール",
|
||||
"K8dcad979": "カスタム文字列; 値:",
|
||||
"K82e3f7b7": "開始位置: (0); 長さ: (1)",
|
||||
"K49dfc123": "選択された (0) アイテム (1) データ",
|
||||
"K8457ea34": "すべて (0)",
|
||||
"K7ca9a795": "属性名",
|
||||
"Kc4391744": "属性値",
|
||||
"K678e13fc": "設定 (0)"
|
||||
}
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
{
|
||||
"Kf5fd27ed": "输入名称查找用户"
|
||||
}
|
||||
{}
|
||||
@@ -1,3 +1 @@
|
||||
{
|
||||
"Kf5fd27ed": "输入名称查找用户"
|
||||
}
|
||||
{}
|
||||
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"K630c9e6d": "APIPark",
|
||||
"Ka3e9f580": "发布名称",
|
||||
"Kb2480682": "策略列表",
|
||||
"K76036e25": "HTTP 请求头",
|
||||
"K44607e3f": "全等匹配",
|
||||
"Kc287500a": "前缀匹配",
|
||||
@@ -21,7 +24,43 @@
|
||||
"K78b1ca25": "地址",
|
||||
"K1644b775": "新增",
|
||||
"Kec91f0db": "申请方消费者",
|
||||
"Kf5fd27ed": "输入名称查找用户",
|
||||
"Kc3b7bfa8": "暂无消费者描述",
|
||||
"K3a6f905d": "输入名称、ID 查找消费者"
|
||||
"K118d8d74": "数据格式",
|
||||
"Kfe7c7d2d": "关键字",
|
||||
"K2f57a694": "正则表达式",
|
||||
"K8953e0a6": "手机号",
|
||||
"K6f86a038": "身份证号",
|
||||
"K7954e7c8": "银行卡号",
|
||||
"K320fdb17": "金额",
|
||||
"K7867acda": "日期",
|
||||
"K7d327ae8": "局部显示",
|
||||
"Kfbf38e3c": "局部遮蔽",
|
||||
"Kd8c1fbb0": "截取",
|
||||
"K89829921": "替换",
|
||||
"K480a7165": "乱序",
|
||||
"Kea0d69df": "随机字符串",
|
||||
"Ke7c84d1d": "自定义字符串",
|
||||
"K49731763": "请输入IP地址或CIDR范围,每条以换行分割",
|
||||
"K83237c89": "输入的IP或CIDR不符合格式",
|
||||
"K5ae2c87a": "请正确输入路径,如/usr/*或*/usr/*",
|
||||
"Kc82b8374": "编辑策略",
|
||||
"K4b34a5e5": "策略类型",
|
||||
"K57f0fee8": "匹配条件",
|
||||
"K10650c58": "数据脱敏规则",
|
||||
"K1b34a9ab": "配置脱敏规则",
|
||||
"K26d22405": "匹配值",
|
||||
"K1546e1fe": "脱敏类型",
|
||||
"K9b9b0629": "起始位置",
|
||||
"K52c84fe1": "长度",
|
||||
"Kde84409c": "替换类型",
|
||||
"K338653b4": "替换值",
|
||||
"Kbaeed3b7": "JSON Path",
|
||||
"K4cd91d61": "脱敏规则",
|
||||
"K8dcad979": "自定义字符串; 值:",
|
||||
"K82e3f7b7": "起始位置:(0)位;长度:(1)位",
|
||||
"K49dfc123": "已选择(0)项(1)数据",
|
||||
"K8457ea34": "所有(0)",
|
||||
"K7ca9a795": "属性名称",
|
||||
"Kc4391744": "属性值",
|
||||
"K678e13fc": "配置(0)",
|
||||
"Kf5fd27ed": "输入名称查找用户"
|
||||
}
|
||||
@@ -1,3 +1 @@
|
||||
{
|
||||
"Kf5fd27ed": "输入名称查找用户"
|
||||
}
|
||||
{}
|
||||
@@ -1,4 +1,15 @@
|
||||
{
|
||||
"Kc0e5ef9f": "Workspace",
|
||||
"K4de11e23": "Home",
|
||||
"K61c89f5f": "API Portal",
|
||||
"K3fe97dcc": "System Settings",
|
||||
"Kecbb0e45": "System",
|
||||
"Ka358e23d": "General",
|
||||
"K449058e9": "API Gateway",
|
||||
"K99935e6f": "AI Model",
|
||||
"K1deaa2dd": "User",
|
||||
"K631d646f": "Open API",
|
||||
"K1196b104": "APIPark",
|
||||
"Kb9052305": "Search Username, Email",
|
||||
"K40a89bd8": "Enter Name, ID to Search Member"
|
||||
}
|
||||
@@ -1,5 +1,16 @@
|
||||
{
|
||||
"Kc0e5ef9f": "Workspace",
|
||||
"K4de11e23": "Home",
|
||||
"Kfe93ef35": "Application",
|
||||
"K61c89f5f": "API Portal",
|
||||
"K3fe97dcc": "設定",
|
||||
"Kecbb0e45": "システム",
|
||||
"Ka358e23d": "一般",
|
||||
"K449058e9": "API ゲートウェイ",
|
||||
"K99935e6f": "AI モデル",
|
||||
"K1deaa2dd": "ユーザー",
|
||||
"K631d646f": "Open API",
|
||||
"K1196b104": "APIPark",
|
||||
"Kffd7e274": "無審査:すべてのアプリケーションがこのサービスにサブスクライブできます",
|
||||
"K8a8b13e4": "手動審査:承認されたアプリケーションのみがこのサービスにサブスクライブできます",
|
||||
"K9bdd8403": "API を安全に呼び出すためには、アプリケーションとトークンを作成する必要があります。",
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
{
|
||||
"Kc0e5ef9f": "工作空间",
|
||||
"K4de11e23": "首页",
|
||||
"Kfe93ef35": "消费者",
|
||||
"K61c89f5f": "API 门户",
|
||||
"K3fe97dcc": "系统设置",
|
||||
"Kecbb0e45": "系统",
|
||||
"Ka358e23d": "常规",
|
||||
"K449058e9": "API 网关",
|
||||
"K99935e6f": "AI 模型",
|
||||
"K1deaa2dd": "用户",
|
||||
"K631d646f": "Open API",
|
||||
"K1196b104": "APIPark",
|
||||
"Kffd7e274": "无审核:允许所有消费者订阅该服务",
|
||||
"K8a8b13e4": "人工审核:仅允许审核通过的消费者订阅该服务",
|
||||
"K9bdd8403": "为了安全地调用 API,你需要创建一个消费者以及Token。",
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
{
|
||||
"Kc0e5ef9f": "工作區",
|
||||
"K4de11e23": "主頁",
|
||||
"Kfe93ef35": "應用程式",
|
||||
"K61c89f5f": "API 門戶",
|
||||
"K3fe97dcc": "系統設置",
|
||||
"Kecbb0e45": "系統",
|
||||
"Ka358e23d": "常規",
|
||||
"K449058e9": "API 網關",
|
||||
"K99935e6f": "AI 模型",
|
||||
"K1deaa2dd": "用戶",
|
||||
"K631d646f": "Open API",
|
||||
"K1196b104": "APIPark",
|
||||
"Kffd7e274": "無審核:允許所有應用程式訂閱該服務",
|
||||
"K8a8b13e4": "人工審核:僅允許審核通過的應用程式訂閱該服務",
|
||||
"K9bdd8403": "為了安全地調用 API,你需要創建一個應用以及Token。",
|
||||
|
||||
@@ -725,5 +725,46 @@
|
||||
"K31faa2a1": "優先級",
|
||||
"Kbdec9fa": "篩選條件",
|
||||
"Kbcbb7391": "處理數",
|
||||
"Kad207008": "編輯"
|
||||
"Kad207008": "編輯",
|
||||
"K630c9e6d": "APIPark",
|
||||
"Ka3e9f580": "發布名稱",
|
||||
"Kb2480682": "策略列表",
|
||||
"K118d8d74": "數據格式",
|
||||
"Kfe7c7d2d": "關鍵字",
|
||||
"K2f57a694": "正則表達式",
|
||||
"K8953e0a6": "手機號碼",
|
||||
"K6f86a038": "身份證號碼",
|
||||
"K7954e7c8": "銀行卡號碼",
|
||||
"K320fdb17": "金額",
|
||||
"K7867acda": "日期",
|
||||
"K7d327ae8": "局部顯示",
|
||||
"Kfbf38e3c": "局部遮蔽",
|
||||
"Kd8c1fbb0": "截取",
|
||||
"K89829921": "替換",
|
||||
"K480a7165": "亂序",
|
||||
"Kea0d69df": "隨機字串",
|
||||
"Ke7c84d1d": "自訂字串",
|
||||
"K49731763": "請輸入IP地址或CIDR範圍,每行以換行符分隔",
|
||||
"K83237c89": "輸入的IP或CIDR格式不正確",
|
||||
"K5ae2c87a": "請正確輸入路徑,例如/usr/*或*/usr/*",
|
||||
"Kc82b8374": "編輯策略",
|
||||
"K4b34a5e5": "策略類型",
|
||||
"K57f0fee8": "匹配條件",
|
||||
"K10650c58": "數據脫敏規則",
|
||||
"K1b34a9ab": "配置脫敏規則",
|
||||
"K26d22405": "匹配值",
|
||||
"K1546e1fe": "脫敏類型",
|
||||
"K9b9b0629": "起始位置",
|
||||
"K52c84fe1": "長度",
|
||||
"Kde84409c": "替換類型",
|
||||
"K338653b4": "替換值",
|
||||
"Kbaeed3b7": "JSON路徑",
|
||||
"K4cd91d61": "脫敏規則",
|
||||
"K8dcad979": "自訂字串; 值:",
|
||||
"K82e3f7b7": "起始位置:(0)位;長度:(1)位",
|
||||
"K49dfc123": "已選擇(0)項(1)數據",
|
||||
"K8457ea34": "所有(0)",
|
||||
"K7ca9a795": "屬性名稱",
|
||||
"Kc4391744": "屬性值",
|
||||
"K678e13fc": "配置(0)"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,38 @@ export const validateUrlSlash = (_, value) => {
|
||||
if (value && value.includes('//')) {
|
||||
return Promise.reject(new Error($t('暂不支持带有双斜杠//的url')));
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
export const validateIPorCIDR = (rule, value) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const lines = value.split('\n');
|
||||
const ipCidrRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$/;
|
||||
|
||||
for (const line of lines) {
|
||||
if (line && !ipCidrRegex.test(line.trim())) {
|
||||
return Promise.reject($t('输入的IP或CIDR不符合格式'));
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
|
||||
export const validateApiPath = (rule, value) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const invalidCharsRegex = /[^a-zA-Z0-9\-\/\*]/;
|
||||
const validPathRegex = /^(\/?\*?\/?[a-zA-Z0-9\-\/\*]*)$/;
|
||||
|
||||
if (value && (invalidCharsRegex.test(value.trim()) || !validPathRegex.test(value.trim()))) {
|
||||
return Promise.reject($t('请正确输入路径,如/usr/*或*/usr/*'));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
@@ -299,4 +299,11 @@ a{
|
||||
a[disabled]:hover {
|
||||
color: #BBB;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.ant-input-group-addon{
|
||||
height:32px !important;
|
||||
.ant-btn.ant-btn-default{
|
||||
height:32px !important;
|
||||
}
|
||||
}
|
||||
@@ -172,7 +172,7 @@ import { Outlet, Navigate } from 'react-router-dom';
|
||||
{
|
||||
path:'servicepolicy',
|
||||
key: 'servicePolicy',
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/servicePolicy')),
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/ServicePolicy')),
|
||||
children:[
|
||||
|
||||
]
|
||||
@@ -266,7 +266,7 @@ import { Outlet, Navigate } from 'react-router-dom';
|
||||
{
|
||||
path:'servicepolicy',
|
||||
key: 'servicePolicy',
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/servicePolicy')),
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/ServicePolicy')),
|
||||
children:[
|
||||
|
||||
]
|
||||
@@ -413,6 +413,23 @@ import { Outlet, Navigate } from 'react-router-dom';
|
||||
key:'analytics2',
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardTotal.tsx')),
|
||||
},
|
||||
{
|
||||
path:':dashboardType',
|
||||
key:'analytics3',
|
||||
component:<Outlet />,
|
||||
children:[
|
||||
{
|
||||
path:'list',
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardList.tsx')),
|
||||
key:'analyticsList'
|
||||
},
|
||||
{
|
||||
path:'detail/:dashboardDetailId',
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardDetail.tsx')),
|
||||
key:'analyticsDetail'
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}],
|
||||
['template', { type: 'module',
|
||||
@@ -449,6 +466,28 @@ import { Outlet, Navigate } from 'react-router-dom';
|
||||
key:'changePsw'
|
||||
}]}],
|
||||
['globalPolicy', { type: 'module',
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/globalPolicy.tsx')),
|
||||
key:'globalPolicy'}]
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/GlobalPolicyLayout')),
|
||||
key:'globalPolicy',
|
||||
children:[{
|
||||
path:'datamasking',
|
||||
component:<Outlet />,
|
||||
key:'dataMasking',
|
||||
children:[
|
||||
{
|
||||
path:'list',
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/GlobalPolicy')),
|
||||
key:'dataMaskingList'
|
||||
},
|
||||
{
|
||||
path:'create',
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/dataMasking/DataMaskingConfig')),
|
||||
key:'dataMaskingAdd'
|
||||
},
|
||||
{
|
||||
path:':policyId',
|
||||
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/dataMasking/DataMaskingConfig')),
|
||||
key:'dataMaskingAdd'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
])
|
||||
@@ -3,9 +3,10 @@ import { Input, TabsProps } from "antd";
|
||||
import { MatchItem } from "@common/const/type";
|
||||
import { ConfigField } from "@common/components/aoplatform/EditableTableWithModal";
|
||||
import { frontendTimeSorter } from "@common/utils/dataTransfer";
|
||||
import { COLUMNS_TITLE } from "@common/const/const";
|
||||
import { COLUMNS_TITLE, PLACEHOLDER } from "@common/const/const";
|
||||
|
||||
import { PageProColumns } from "@common/components/aoplatform/PageList";
|
||||
import { $t } from "@common/locales";
|
||||
|
||||
export enum SubscribeEnum{
|
||||
Rejected = 0,
|
||||
@@ -222,7 +223,7 @@ export const MATCH_CONFIG:ConfigField<MatchItem>[] = [
|
||||
}, {
|
||||
title:('参数名'),
|
||||
key: 'key',
|
||||
component: <Input className="w-INPUT_NORMAL" />,
|
||||
component: <Input className="w-INPUT_NORMAL"/>,
|
||||
renderText: (value: unknown) => value,
|
||||
required: true
|
||||
}, {
|
||||
@@ -236,7 +237,7 @@ export const MATCH_CONFIG:ConfigField<MatchItem>[] = [
|
||||
title:('参数值'),
|
||||
key: 'pattern',
|
||||
unRender:(formValue)=>{return formValue?.matchType === 'NULL' || formValue?.matchType==='EXIST' || formValue?.matchType === 'UNEXIST'},
|
||||
component: <Input className="w-INPUT_NORMAL"/>,
|
||||
component: <Input className="w-INPUT_NORMAL" />,
|
||||
renderText: (value: string) => {
|
||||
return value
|
||||
},
|
||||
|
||||
@@ -321,6 +321,11 @@ p{
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-body {
|
||||
scrollbar-width: auto;
|
||||
scrollbar-color: auto;
|
||||
}
|
||||
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
@@ -664,7 +669,7 @@ p{
|
||||
.ant-pro-table-list-toolbar-setting-items{
|
||||
position:absolute;
|
||||
top:18px;
|
||||
right:16px;
|
||||
right:22px;
|
||||
z-index:9;
|
||||
.ant-pro-table-list-toolbar-setting-item{
|
||||
font-size:14px;
|
||||
|
||||
@@ -3,6 +3,7 @@ import {StrictMode} from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import './index.css'
|
||||
import { LocaleProvider } from '@common/contexts/LocaleContext.tsx';
|
||||
async function initializeApp() {
|
||||
try {
|
||||
// 初始化行为
|
||||
@@ -11,7 +12,9 @@ async function initializeApp() {
|
||||
// 异步操作完成后,渲染React消费者
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<LocaleProvider>
|
||||
<App />
|
||||
</LocaleProvider>
|
||||
</StrictMode>,
|
||||
);
|
||||
} catch (error) {
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from 'react';
|
||||
import { Form, Input, Select, Checkbox, Table, Spin, TableColumnsType, message } from 'antd';
|
||||
import { useFetch } from '@common/hooks/http';
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
import { validateApiPath, validateIPorCIDR } from '@common/utils/validate';
|
||||
import { $t } from '@common/locales';
|
||||
import { FilterFormItemProps, RemoteTitleType, FilterFormHandle, FilterFormProps } from '@common/const/policy/type';
|
||||
import { BasicResponse, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const';
|
||||
|
||||
const RemoteFormItem: React.FC<FilterFormItemProps> = (props) =>{
|
||||
const {value, onChange, disabled,option, onShowValueChange} = props
|
||||
const [remoteList, setRemoteList] = useState<unknown[]>([])
|
||||
const [remoteTableColumns, setRemoteTableColumns] = useState<TableColumnsType>([])
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [rowKey, setRowKey] = useState<string>('')
|
||||
const title = useMemo(()=>option?.title,[option])
|
||||
const [remoteCounts, setRemoteCounts] = useState<number>(0)
|
||||
const [originRemoteList, setOriginRemoteList] = useState<unknown[]>([])
|
||||
const {fetchData} = useFetch()
|
||||
|
||||
|
||||
const getRemoteDetail = (searchWord?:string)=>{
|
||||
setLoading(true)
|
||||
fetchData<BasicResponse<{
|
||||
key:string,
|
||||
list:Record<string,unknown>[],
|
||||
target:string,
|
||||
titles:Array<RemoteTitleType>,
|
||||
total:number
|
||||
value:string
|
||||
}>>(`strategy/filter-REMOTE/${option?.name}`,{method:'GET', eoParams:{keyword:searchWord}}).then(response=>{
|
||||
const {code,data, msg} = response
|
||||
if(code === STATUS_CODE.SUCCESS){
|
||||
setRemoteList(data[data.target as string] as unknown[])
|
||||
setRowKey(data.key as string)
|
||||
setRemoteTableColumns(data.titles.map((x:RemoteTitleType)=>({
|
||||
title: x.title,dataIndex:x.field,key:x.field,ellipsis:true
|
||||
})))
|
||||
setRemoteCounts(data.total)
|
||||
if(!searchWord){
|
||||
setOriginRemoteList(data[data.target as string])
|
||||
if(value?.length === 1 && value[0] === 'ALL'){
|
||||
const totalDataArr = data[data.target as string]?.map((x:Record<string,unknown>)=>x[data.key as string])
|
||||
onChange?.(totalDataArr)
|
||||
onShowValueChange?.(totalDataArr.join(','))
|
||||
}
|
||||
}
|
||||
}else{
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
}).finally(()=>{
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(()=>{getRemoteDetail()},[option])
|
||||
return (
|
||||
<div className="w-full transfer-section rounded-DEFAULT" style={{ border: '1px solid var(--border-color)', borderBottom: 'none' }}>
|
||||
<Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin/>} spinning={loading}>
|
||||
<p className="flex items-center mt-[12px] text-[16px] font-bold px-btnbase">
|
||||
{$t('已选择(0)项(1)数据', [value?.length || 0, title])}
|
||||
</p>
|
||||
<div className="flex items-center justify-between py-btnybase px-btnbase">
|
||||
<div></div>
|
||||
<Input.Search
|
||||
className="w-[224px] h-[32px]"
|
||||
placeholder={$t(PLACEHOLDER.input)}
|
||||
onSearch={(value)=>getRemoteDetail(value)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
columns={remoteTableColumns}
|
||||
dataSource={remoteList}
|
||||
pagination={false}
|
||||
scroll={{ y: 316 }}
|
||||
rowClassName={() => (disabled ? '' : 'clickable-row')}
|
||||
rowKey={rowKey}
|
||||
size='small'
|
||||
rowSelection={{
|
||||
type: 'checkbox',
|
||||
onChange: (selectedRowKeys: React.Key[]) => {
|
||||
onChange?.(selectedRowKeys as string[]);
|
||||
onShowValueChange?.(selectedRowKeys.length === remoteCounts? $t('所有(0)',[title]) : originRemoteList.filter(x=>selectedRowKeys?.indexOf(x[option.target]))?.map(x=>x.title).join(' , '))
|
||||
},
|
||||
selectedRowKeys: value,
|
||||
// getCheckboxProps: (record: unknown) => ({
|
||||
// disabled: record.name === 'Disabled User', // Column configuration not to be checked
|
||||
// name: record.name,
|
||||
// })
|
||||
}}
|
||||
onRow={(record)=>({
|
||||
onClick:()=>{
|
||||
if(value === undefined){
|
||||
onChange?.([record[rowKey]])
|
||||
onShowValueChange?.(record.title)
|
||||
}else if(value?.indexOf(record[rowKey])!== -1){
|
||||
const newSelectedKeys = value?.filter(x=>x!==record[rowKey])
|
||||
onChange?.(newSelectedKeys!)
|
||||
onShowValueChange?.(newSelectedKeys.length === remoteCounts? $t('所有(0)',[option?.title]) : originRemoteList.filter(x=>newSelectedKeys.indexOf(x[rowKey]) !== -1)?.map(x=>x.title)?.join(' , '))
|
||||
}else{
|
||||
const newSelectedKeys = [...value,record[rowKey]]
|
||||
onChange?.(newSelectedKeys)
|
||||
onShowValueChange?.(newSelectedKeys.length === remoteCounts? $t('所有(0)',[option?.title]) : originRemoteList.filter(x=>newSelectedKeys.indexOf(x[rowKey]) !== -1)?.map(x=>x.title)?.join(' , '))
|
||||
}
|
||||
}
|
||||
})}
|
||||
/>
|
||||
</Spin>
|
||||
</div>)
|
||||
}
|
||||
|
||||
const StaticFormItem: React.FC<FilterFormItemProps> = (props) => {
|
||||
const {value, onChange, disabled,option,onShowValueChange} = props
|
||||
const showAll = useMemo(()=>option.options.indexOf('ALL') !== -1,[option])
|
||||
const allChecked = useMemo(()=>value?.filter(x=>x!== 'ALL').length === option.options.filter(x=>x!== 'ALL').length,[value,option])
|
||||
|
||||
useEffect(()=>{
|
||||
if(value?.length === 1 && value[0] === 'ALL'){
|
||||
onChange?.(option.options.filter(x=>x!== 'ALL'))
|
||||
onShowValueChange?.($t('所有(0)',[option?.title]))
|
||||
}
|
||||
},[])
|
||||
|
||||
return (
|
||||
<div className="w-auto">
|
||||
{showAll && (
|
||||
<Checkbox
|
||||
className='mr-[8px]'
|
||||
checked={allChecked}
|
||||
onChange={(e) =>{
|
||||
onChange?.(e.target.checked ? option.options : [])
|
||||
onShowValueChange?.(e.target.checked ? $t('所有(0)',[option?.title]) : '-')
|
||||
}}
|
||||
disabled={disabled}
|
||||
indeterminate={!allChecked && value?.length > 0}
|
||||
>
|
||||
ALL
|
||||
</Checkbox>
|
||||
)}
|
||||
<Checkbox.Group
|
||||
value={value}
|
||||
options={option?.options.filter(x=>x!== 'ALL')}
|
||||
onChange={(checkedValues) => {
|
||||
onChange?.(checkedValues)
|
||||
onShowValueChange?.(checkedValues.join(','))
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>)
|
||||
}
|
||||
|
||||
const FilterForm = forwardRef<FilterFormHandle,FilterFormProps>(({
|
||||
filterForm,
|
||||
filterOptions,
|
||||
selectedOptionNameSet,
|
||||
disabled,
|
||||
setFormCanSubmit},ref)=> {
|
||||
const [form] = Form.useForm();
|
||||
const [filterType, setFilterType] = useState<'remote'|'static'|'pattern'>();
|
||||
const [curOption, setCurOption] = useState<unknown>()
|
||||
const [label,setLabel] = useState<string>('')
|
||||
|
||||
useImperativeHandle(ref, ()=>({
|
||||
clear:()=>{
|
||||
form.resetFields()
|
||||
},
|
||||
save:()=>form.validateFields().then((res)=>{
|
||||
const selectedOption = filterOptions.filter(x=>x.name === res.name)[0]
|
||||
return Promise.resolve({
|
||||
...res,
|
||||
label:filterType === 'pattern' ? res.value : label,
|
||||
title:selectedOption.label
|
||||
})
|
||||
}).catch((errorInfo)=>Promise.reject(errorInfo))
|
||||
})
|
||||
)
|
||||
|
||||
const handleValuesChange = (changedValues: any, allValues: any) => {
|
||||
if(!allValues){
|
||||
setFormCanSubmit(false)
|
||||
return
|
||||
}
|
||||
if(allValues.value instanceof Array){
|
||||
setFormCanSubmit(allValues.value.length > 0)
|
||||
return
|
||||
}
|
||||
setFormCanSubmit(true)
|
||||
};
|
||||
|
||||
|
||||
const handleTypeChange = (value:string)=>{
|
||||
form.setFieldValue('value',filterForm?.name === value ? filterForm.value : undefined)
|
||||
const selectedOption = filterOptions?.filter(item=>item.name === value)[0]
|
||||
setFilterType(selectedOption?.type)
|
||||
setCurOption(selectedOption)
|
||||
setFormCanSubmit(filterForm?.name === value )
|
||||
}
|
||||
|
||||
const handleIPChange = (e) => {
|
||||
const inputValue = e.target.value;
|
||||
const formattedValue = inputValue.replace(/,/g, '\n');
|
||||
form.setFieldsValue({ value: formattedValue });
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
if(filterForm?.name){
|
||||
form.setFieldsValue(filterForm)
|
||||
const selectedOption = filterOptions.filter(x=>x.name === filterForm?.name)[0]
|
||||
setFilterType(selectedOption?.type )
|
||||
setCurOption(selectedOption)
|
||||
}else{
|
||||
const firstOption = filterOptions.filter(x=>!selectedOptionNameSet.has(x.name))[0]
|
||||
form.setFieldValue('name',firstOption?.name)
|
||||
setFilterType(firstOption?.type)
|
||||
setCurOption(firstOption)
|
||||
}
|
||||
},[filterForm])
|
||||
|
||||
const filterOptionsList = useMemo(() => {
|
||||
return filterOptions.filter(x=>{
|
||||
return !!(filterForm?.name && x.name === filterForm?.name )|| !selectedOptionNameSet.has(x.name)}).map((item) => ({label:item.title, value:item.name}));
|
||||
}, [filterOptions,filterForm,selectedOptionNameSet]);
|
||||
|
||||
return (
|
||||
<Form form={form} layout="vertical" onValuesChange={handleValuesChange}>
|
||||
<Form.Item name="name" label={$t('属性名称')} rules={[{ required: true }]}>
|
||||
<Select disabled={disabled} onChange={handleTypeChange} options={filterOptionsList} />
|
||||
</Form.Item>
|
||||
<Form.Item name="value" label={$t('属性值')} rules={
|
||||
(filterType === 'pattern' ? ( [{validator:form.getFieldValue('name') === 'ip' ? validateIPorCIDR : validateApiPath}]):[])
|
||||
}>
|
||||
{filterType === 'remote' && <RemoteFormItem option={curOption} disabled={disabled} onShowValueChange={setLabel}/>}
|
||||
|
||||
{filterType === 'pattern' && form.getFieldValue('name') !== 'ip' && (
|
||||
<Input
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.input)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{filterType === 'pattern' && form.getFieldValue('name') === 'ip' && (
|
||||
<Input.TextArea
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.ipAndCidr)}
|
||||
onChange={handleIPChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{filterType === 'static' && <StaticFormItem option={curOption} disabled={disabled} onShowValueChange={setLabel}/>}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
})
|
||||
|
||||
export default FilterForm;
|
||||
@@ -0,0 +1,144 @@
|
||||
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
||||
import { Button, Table, Modal, App, Divider } from 'antd';
|
||||
import { ColumnsType } from 'antd/es/table';
|
||||
import { $t } from '@common/locales';
|
||||
import { useFetch } from '@common/hooks/http';
|
||||
import { useGlobalContext } from '@common/contexts/GlobalStateContext';
|
||||
import TableBtnWithPermission from '@common/components/aoplatform/TableBtnWithPermission';
|
||||
import { FilterFormField, FilterTableProps, FilterOptionType, FilterFormHandle } from '@common/const/policy/type.ts';
|
||||
import FilterForm from './FilterForm';
|
||||
import { BasicResponse, COLUMNS_TITLE, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const';
|
||||
|
||||
|
||||
const FilterTable: React.FC<FilterTableProps> = ({
|
||||
disabled = false,
|
||||
drawerTitle = '筛选条件',
|
||||
value,
|
||||
onChange
|
||||
}) => {
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [filterForm, setFilterForm] = useState<FilterFormField>();
|
||||
const [filterOptions, setFilterOptions] = useState<FilterOptionType[]>([]);
|
||||
const {message} = App.useApp()
|
||||
const {state} = useGlobalContext()
|
||||
const {fetchData} = useFetch()
|
||||
const formRef = useRef<FilterFormHandle>(null);
|
||||
const [formCanSubmit,setFormCanSubmit] = useState(false)
|
||||
const [selectedOptionNameSet, setSelectedOptionNameSet] = useState<Set<string>>(new Set());
|
||||
const openDrawer = (type: string, data?: FilterFormField) => {
|
||||
switch (type) {
|
||||
case 'addFilter':
|
||||
setFilterForm(undefined)
|
||||
break;
|
||||
case 'editFilter':
|
||||
console.log(data)
|
||||
setFilterForm(data)
|
||||
}
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
const closeDrawer = () => {
|
||||
setIsModalVisible(false);
|
||||
cleanFilterForm();
|
||||
};
|
||||
|
||||
const cleanFilterForm = () => {
|
||||
setFilterForm(undefined);
|
||||
};
|
||||
|
||||
const handleSaveFilter = () => {
|
||||
const formPromise = formRef.current?.save();
|
||||
formPromise?.then?.((res)=>{
|
||||
const newFilterForm = {
|
||||
name:res.name,
|
||||
value:res.value instanceof Array ? res.value : [res.value],
|
||||
label:res.label,
|
||||
title:res.title
|
||||
}
|
||||
onChange?.([newFilterForm, ...(value?.filter(x=>!filterForm?.name|| x.name!==filterForm.name) || [])])
|
||||
setSelectedOptionNameSet(prev=>{filterForm?.name &&prev.delete(filterForm?.name); prev.add(res.name); return prev})
|
||||
closeDrawer()
|
||||
})
|
||||
};
|
||||
|
||||
const handleDeleteFilter = (item: FilterFormField) => {
|
||||
setSelectedOptionNameSet(prev=>{prev.delete(item?.name); return prev})
|
||||
const newFilterShowList = value.filter((filter) => filter !== item);
|
||||
onChange?.(newFilterShowList);
|
||||
};
|
||||
|
||||
const getFilterOptions = ()=>{
|
||||
fetchData<BasicResponse<{options:FilterOptionType[]}>>('strategy/filter-options',{method:'GET'}).then(response=>{
|
||||
const {code,data,msg} = response
|
||||
if(code === STATUS_CODE.SUCCESS){
|
||||
setFilterOptions(data.options)
|
||||
}else{
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
}).catch((errorInfo)=> console.error(errorInfo))
|
||||
}
|
||||
|
||||
const columns: ColumnsType<FilterFormField> =useMemo(()=>[
|
||||
{
|
||||
title: $t('属性名称'),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: $t('属性值'),
|
||||
dataIndex: 'label',
|
||||
key: 'label',
|
||||
},
|
||||
{
|
||||
title: COLUMNS_TITLE.operate,
|
||||
key: 'action',
|
||||
width:100,
|
||||
render: (_, record) => (
|
||||
<div className='flex items-center gap-[8px]'>
|
||||
<TableBtnWithPermission key="edit" btnType="edit" onClick={()=>{openDrawer('editFilter', record)}} btnTitle={$t("编辑")}/>
|
||||
<Divider type="vertical" className="mx-0" key="div2"/>
|
||||
<TableBtnWithPermission key="delete" btnType="delete" onClick={()=>{handleDeleteFilter(record)}} btnTitle={$t("删除")}/></div>)
|
||||
}
|
||||
],[state.language])
|
||||
|
||||
useEffect(()=>{
|
||||
getFilterOptions()
|
||||
},[state.language])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
!disabled &&<Button onClick={() => openDrawer('addFilter')}>
|
||||
{$t('添加配置')}
|
||||
</Button>
|
||||
}
|
||||
{value && value.length >0 && <Table
|
||||
className={`mt-btnybase border-solid border-[1px] border-BORDER border-b-0 rounded ${disabled ? '' : 'mt-btnbase'}`}
|
||||
pagination={false}
|
||||
size='small'
|
||||
columns={columns} dataSource={value} rowKey='id' /> }
|
||||
<div role="alert" className="ant-form-item-explain-error">
|
||||
</div>
|
||||
<Modal
|
||||
title={filterForm?.name ? $t('编辑(0)',[$t(drawerTitle)]) :$t('配置(0)',[$t(drawerTitle)])}
|
||||
visible={isModalVisible}
|
||||
onCancel={closeDrawer}
|
||||
width={900}
|
||||
okButtonProps={{ disabled:!formCanSubmit }}
|
||||
onOk={()=>handleSaveFilter()}
|
||||
destroyOnClose={true}
|
||||
>
|
||||
<FilterForm
|
||||
ref={formRef}
|
||||
filterForm={filterForm}
|
||||
filterOptions={filterOptions}
|
||||
selectedOptionNameSet={selectedOptionNameSet}
|
||||
disabled={disabled}
|
||||
setFormCanSubmit={setFormCanSubmit}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FilterTable;
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from "react";
|
||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
export default function GlobalPolicyLayout(){
|
||||
const location = useLocation()
|
||||
const pathName = location.pathname
|
||||
const navigator = useNavigate()
|
||||
useEffect(()=>{
|
||||
if(pathName === '/globalpolicy'){
|
||||
navigator('/globalpolicy/datamasking/list')
|
||||
}
|
||||
},[pathName])
|
||||
return (<Outlet></Outlet>)
|
||||
}
|
||||
@@ -1,463 +0,0 @@
|
||||
import { ActionType } from "@ant-design/pro-components";
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { Button, message, Switch } from 'antd'
|
||||
import PageList, { PageProColumns } from "@common/components/aoplatform/PageList";
|
||||
import { DATA_MASSKING_TABLE_COLUMNS } from './dataMaskingColumn'
|
||||
import { $t } from "@common/locales";
|
||||
import { useGlobalContext } from "@common/contexts/GlobalStateContext";
|
||||
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const.tsx";
|
||||
import { useFetch } from "@common/hooks/http";
|
||||
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
|
||||
import TableBtnWithPermission from "@common/components/aoplatform/TableBtnWithPermission";
|
||||
|
||||
|
||||
const DataMasking = (props: any) => {
|
||||
|
||||
const {
|
||||
// 是否显示发布按钮
|
||||
publishBtn = false,
|
||||
// 行操作
|
||||
rowOperation = []
|
||||
} = props;
|
||||
const { checkPermission, getGlobalAccessData, accessInit, state } = useGlobalContext()
|
||||
/**
|
||||
* 列表ref
|
||||
*/
|
||||
const pageListRef = useRef<ActionType>(null);
|
||||
|
||||
/**
|
||||
* 表格数据重新加载
|
||||
*/
|
||||
const [tableHttpReload, setTableHttpReload] = useState(true);
|
||||
|
||||
/**
|
||||
* 表格数据
|
||||
*/
|
||||
const [tableListDataSource, setTableListDataSource] = useState<ServiceHubAppListItem[]>([]);
|
||||
|
||||
/**
|
||||
* 请求数据
|
||||
*/
|
||||
const { fetchData } = useFetch()
|
||||
|
||||
/**
|
||||
* 搜索关键字
|
||||
*/
|
||||
const [searchWord, setSearchWord] = useState<string>('')
|
||||
|
||||
/**
|
||||
* 获取列数据,国际化变化时重新获取
|
||||
*/
|
||||
const columns = useMemo(() => {
|
||||
const res = DATA_MASSKING_TABLE_COLUMNS.map(x => {
|
||||
// 启动列渲染
|
||||
if (x.dataIndex === 'enabled') {
|
||||
x.render = (text: any, record: any) => <Switch checked={record.enabled} onChange={(e) => { changeOpenApiStatus(e, record) }} />
|
||||
}
|
||||
// 处理数列渲染
|
||||
if (x.dataIndex === 'treatmentNumber') {
|
||||
x.render = (text: any, record: any) => <span className="w-full block cursor-pointer [&>.ant-typography]:text-theme" onClick={(e) => { openLogsModal(record) }} >{ text }</span>
|
||||
}
|
||||
// 名称筛选,这里是全量返回时候的,分页的话应该要接口返回对应的筛选数据
|
||||
if (x.dataIndex === 'name') {
|
||||
const nameList = tableListDataSource.map(item => item.name)
|
||||
const valueEnum: any = {}
|
||||
nameList.forEach(item => {
|
||||
valueEnum[item] = { text: item }
|
||||
})
|
||||
x.valueEnum = valueEnum
|
||||
}
|
||||
return {
|
||||
...x,
|
||||
title: typeof x.title === 'string' ? $t(x.title as string) : x.title
|
||||
}
|
||||
})
|
||||
return res
|
||||
}, [tableListDataSource, state.language])
|
||||
|
||||
/**
|
||||
* 操作列
|
||||
*/
|
||||
const operation: PageProColumns<any>[] = rowOperation.length ? [
|
||||
{
|
||||
title: '',
|
||||
key: 'option',
|
||||
btnNums: rowOperation.length,
|
||||
fixed: 'right',
|
||||
valueType: 'option',
|
||||
render: (_: React.ReactNode, entity: any) => [
|
||||
...(rowOperation.length && rowOperation.find((item: string) => item === 'edit') ? [<TableBtnWithPermission access="system.organization.member.edit" key="edit" btnType="edit" onClick={() => { openEditModal(entity) }} btnTitle="编辑" />] : []),
|
||||
...(rowOperation.length && rowOperation.find((item: string) => item === 'logs') ? [<TableBtnWithPermission access="system.organization.member.edit" key="logs" btnType="logs" onClick={() => { openLogsModal(entity) }} btnTitle="详情" />] : []),
|
||||
...(rowOperation.length && rowOperation.find((item: string) => item === 'delete') ? [<TableBtnWithPermission access="system.organization.member.edit" key="delete" btnType="delete" onClick={() => { deletePolicy(entity) }} btnTitle="删除" />] : []),
|
||||
],
|
||||
}
|
||||
] : []
|
||||
|
||||
/**
|
||||
* 手动刷新表格数据
|
||||
*/
|
||||
const manualReloadTable = () => {
|
||||
setTableHttpReload(true)
|
||||
pageListRef.current?.reload()
|
||||
};
|
||||
|
||||
/**
|
||||
* 更改启动状态
|
||||
* @param enabled 状态
|
||||
* @param entity 行数据
|
||||
*/
|
||||
const changeOpenApiStatus = (enabled: boolean, entity: any) => {
|
||||
console.log('更改启动状态', enabled, entity);
|
||||
|
||||
manualReloadTable()
|
||||
// fetchData<BasicResponse<null>>(
|
||||
// `external-app/${enabled ? 'disable' : 'enable'}`,
|
||||
// {
|
||||
// method: 'PUT',
|
||||
// eoParams: {
|
||||
// id: entity.id
|
||||
// }
|
||||
// }
|
||||
// ).then(response => {
|
||||
// const { code, msg } = response
|
||||
// if (code === STATUS_CODE.SUCCESS) {
|
||||
// message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
// manualReloadTable()
|
||||
// } else {
|
||||
// message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列表数据
|
||||
* @param dataType
|
||||
* @returns
|
||||
*/
|
||||
const getServiceList = () => {
|
||||
if (!accessInit) {
|
||||
getGlobalAccessData()?.then?.(() => { getServiceList() })
|
||||
return Promise.resolve({ data: [], success: false })
|
||||
}
|
||||
|
||||
if (!tableHttpReload) {
|
||||
setTableHttpReload(true)
|
||||
return Promise.resolve({
|
||||
data: tableListDataSource,
|
||||
success: true,
|
||||
});
|
||||
}
|
||||
return fetchData<BasicResponse<any>>(
|
||||
!checkPermission('system.workspace.team.view_all') ? 'teams' : 'manager/teams',
|
||||
{
|
||||
method: 'GET',
|
||||
eoParams: { keyword: searchWord },
|
||||
eoTransformKeys: ['create_time', 'service_num', 'can_delete']
|
||||
}
|
||||
).then(response => {
|
||||
const { code, data, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
const data = [
|
||||
{
|
||||
name: 'test',
|
||||
priority: 1,
|
||||
status: true,
|
||||
enabled: true,
|
||||
condition: 'test',
|
||||
treatmentNumber: 1,
|
||||
updater: 'test',
|
||||
createTime: '2021-10-01'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
priority: 2,
|
||||
status: false,
|
||||
enabled: false,
|
||||
condition: 'test2',
|
||||
treatmentNumber: 2,
|
||||
updater: 'test2',
|
||||
createTime: '2021-10-02'
|
||||
}
|
||||
]
|
||||
// 保存数据
|
||||
setTableListDataSource(data)
|
||||
setTableHttpReload(false)
|
||||
return {
|
||||
data,
|
||||
success: true
|
||||
}
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
return { data: [], success: false }
|
||||
}
|
||||
}).catch(() => {
|
||||
return { data: [], success: false }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加策略
|
||||
* @param type
|
||||
*/
|
||||
const addPolicy = () => {
|
||||
console.log('添加策略');
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布策略
|
||||
*/
|
||||
const publish = () => {
|
||||
console.log('发布策略');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
const openEditModal = (entity: any) => {
|
||||
console.log('编辑', entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志
|
||||
* @param entity
|
||||
*/
|
||||
const openLogsModal = (entity: any) => {
|
||||
console.log('日志', entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param entity
|
||||
*/
|
||||
const deletePolicy = (entity: any) => {
|
||||
console.log('删除', entity);
|
||||
manualReloadTable()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageList
|
||||
id="data_masking_list"
|
||||
ref={pageListRef}
|
||||
columns={[...columns, ...operation]}
|
||||
request={() => getServiceList()}
|
||||
addNewBtnTitle={$t("添加策略")}
|
||||
onAddNewBtnClick={() => { addPolicy() }}
|
||||
searchPlaceholder={$t("输入名称、筛选条件查找")}
|
||||
afterNewBtn={
|
||||
publishBtn && [<WithPermission key="removeFromDepPermission" access="system.organization.member.edit"><Button className="mr-btnbase" key="removeFromDep" onClick={() => publish()}>{$t('发布')}</Button></WithPermission>]
|
||||
}
|
||||
onSearchWordChange={(e) => {
|
||||
setSearchWord(e.target.value)
|
||||
}}
|
||||
manualReloadTable={manualReloadTable}
|
||||
onChange={() => {
|
||||
setTableHttpReload(false)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default DataMasking;
|
||||
@@ -0,0 +1,331 @@
|
||||
import { ActionType } from "@ant-design/pro-components";
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { Button, message, Switch } from 'antd'
|
||||
import PageList, { PageProColumns } from "@common/components/aoplatform/PageList";
|
||||
import { $t } from "@common/locales";
|
||||
import { useGlobalContext } from "@common/contexts/GlobalStateContext";
|
||||
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const.tsx";
|
||||
import { useFetch } from "@common/hooks/http";
|
||||
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
|
||||
import TableBtnWithPermission from "@common/components/aoplatform/TableBtnWithPermission";
|
||||
import { DATA_MASSKING_TABLE_COLUMNS } from "./DataMaskingColumn";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { PolicyPublishInfoType, PolicyPublishModalHandle, RouterParams } from "@common/const/type";
|
||||
import { DrawerWithFooter } from "@common/components/aoplatform/DrawerWithFooter";
|
||||
import { DataMaskStrategyItem } from "@common/const/policy/type";
|
||||
import {PolicyPublishModalContent} from '@common/components/aoplatform/PolicyPublishModalContent'
|
||||
|
||||
const DataMasking = (props: any) => {
|
||||
|
||||
const {
|
||||
// 是否显示发布按钮
|
||||
publishBtn = false,
|
||||
// 行操作
|
||||
rowOperation = []
|
||||
} = props;
|
||||
const { serviceId, teamId } = useParams<RouterParams>()
|
||||
const { state } = useGlobalContext()
|
||||
const navigator = useNavigate()
|
||||
const [drawerVisible, setDrawerVisible] = useState<boolean>(false)
|
||||
const [drawerData, setDrawerData] = useState<PolicyPublishInfoType >()
|
||||
const [isOkToPublish, setIsOkToPublish] = useState<boolean>(false)
|
||||
const drawerRef = useRef<PolicyPublishModalHandle>(null)
|
||||
/**
|
||||
* 列表ref
|
||||
*/
|
||||
const pageListRef = useRef<ActionType>(null);
|
||||
|
||||
/**
|
||||
* 请求数据
|
||||
*/
|
||||
const { fetchData } = useFetch()
|
||||
|
||||
/**
|
||||
* 搜索关键字
|
||||
*/
|
||||
const [searchWord, setSearchWord] = useState<string>('')
|
||||
|
||||
/**
|
||||
* 获取列数据,国际化变化时重新获取
|
||||
*/
|
||||
const columns = useMemo(() => {
|
||||
const res = DATA_MASSKING_TABLE_COLUMNS.map(x => {
|
||||
// 启动列渲染
|
||||
if (x.dataIndex === 'isStop') {
|
||||
x.render = (text: any, record: any) => <Switch checked={!record.isStop} onChange={(e) => { changeOpenApiStatus(e, record) }} />
|
||||
}
|
||||
// 处理数列渲染
|
||||
if (x.dataIndex === 'treatmentNumber') {
|
||||
x.render = (text: any, record: any) => <span className="w-full block cursor-pointer [&>.ant-typography]:text-theme" onClick={(e) => { openLogsModal(record) }} >{ text }</span>
|
||||
}
|
||||
return {
|
||||
...x,
|
||||
title: typeof x.title === 'string' ? $t(x.title as string) : x.title
|
||||
}
|
||||
})
|
||||
return res
|
||||
}, [ state.language])
|
||||
|
||||
/**
|
||||
* 操作列
|
||||
*/
|
||||
const operation: PageProColumns<any>[] = rowOperation.length ? [
|
||||
{
|
||||
title: '',
|
||||
key: 'option',
|
||||
btnNums: rowOperation.length,
|
||||
fixed: 'right',
|
||||
valueType: 'option',
|
||||
render: (_: React.ReactNode, entity: any) => [
|
||||
...(rowOperation.length && rowOperation.find((item: string) => item === 'edit') ? [<TableBtnWithPermission access="system.organization.member.edit" key="edit" btnType="edit" onClick={() => { openEditModal(entity) }} btnTitle="编辑" />] : []),
|
||||
// ...(rowOperation.length && rowOperation.find((item: string) => item === 'logs') ? [<TableBtnWithPermission access="system.organization.member.edit" key="logs" btnType="logs" onClick={() => { openLogsModal(entity) }} btnTitle="详情" />] : []),
|
||||
...(rowOperation.length && rowOperation.find((item: string) => item === 'delete') ? [
|
||||
entity.isDeleted ? <TableBtnWithPermission access="system.organization.member.edit" key="refresh" btnType="refresh" onClick={() => { restorePolicy(entity) }} btnTitle="恢复" /> :
|
||||
<TableBtnWithPermission access="system.organization.member.edit" key="delete" btnType="delete" onClick={() => { deletePolicy(entity) }} btnTitle="删除" />
|
||||
] : []),
|
||||
],
|
||||
}
|
||||
] : []
|
||||
|
||||
/**
|
||||
* 手动刷新表格数据
|
||||
*/
|
||||
const manualReloadTable = () => {
|
||||
pageListRef.current?.reload()
|
||||
};
|
||||
|
||||
/**
|
||||
* 更改启动状态
|
||||
* @param enabled 状态
|
||||
* @param entity 行数据
|
||||
*/
|
||||
const changeOpenApiStatus = (enabled: boolean, entity: any) => {
|
||||
fetchData<BasicResponse<null>>(
|
||||
`strategy/${serviceId === undefined? 'global':'service'}/data-masking/${enabled ? 'disable' : 'enable'}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
eoParams: {
|
||||
service:serviceId,
|
||||
team:teamId,
|
||||
strategy: entity.id
|
||||
}
|
||||
}
|
||||
).then(response => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
manualReloadTable()
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列表数据
|
||||
* @param dataType
|
||||
* @returns
|
||||
*/
|
||||
const getPolicyList = (params: DataMaskStrategyItem & {
|
||||
pageSize: number;
|
||||
current: number;
|
||||
},
|
||||
sort:Record<string, string>,
|
||||
filter:Record<string, string>) => {
|
||||
let filters
|
||||
if(filter){
|
||||
filters = []
|
||||
if(filter.isStop){
|
||||
if(filter.isStop.indexOf('true')!== -1){
|
||||
filters.push('enable')
|
||||
}
|
||||
if(filter.isStop.indexOf('false')!== -1){
|
||||
filters.push('disable')
|
||||
}
|
||||
if(filter.publishStatus?.length > 0){
|
||||
filters = [...filters, ...filter.publishStatus]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fetchData<BasicResponse<{list:DataMaskStrategyItem[], total:number}>>(
|
||||
`strategy/${serviceId === undefined? 'global':'service'}/data-masking/list`,
|
||||
{
|
||||
method: 'GET',
|
||||
eoParams: {
|
||||
order:Object.keys(sort)?.[0],
|
||||
sort:Object.keys(sort)?.length > 0 ? Object.values(sort)?.[0] === 'descend' ? 'desc' : 'asc' : undefined,
|
||||
filters:JSON.stringify(filters),
|
||||
keyword: searchWord,
|
||||
service:serviceId,
|
||||
team:teamId,},
|
||||
eoTransformKeys: ['is_stop', 'is_deleted', 'update_time','publish_status','processed_total']
|
||||
}
|
||||
).then(response => {
|
||||
const { code,data, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
// 保存数据
|
||||
return {
|
||||
data:data.strategies,
|
||||
total:data.total,
|
||||
success: true
|
||||
}
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
return { data: [], success: false }
|
||||
}
|
||||
}).catch(() => {
|
||||
return { data: [], success: false }
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加策略
|
||||
* @param type
|
||||
*/
|
||||
const addPolicy = () => {
|
||||
navigator('/globalpolicy/datamasking/create')
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布策略
|
||||
*/
|
||||
const publish = async () => {
|
||||
message.loading($t(RESPONSE_TIPS.loading));
|
||||
const { code, data, msg } = await fetchData<BasicResponse<PolicyPublishInfoType>>(
|
||||
'strategy/global/data-masking/to-publishs',
|
||||
{ method: 'GET',eoTransformKeys:['opt_time','is_publish','version_name','unpublish_msg'] }
|
||||
);
|
||||
message.destroy();
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
setDrawerVisible(true)
|
||||
setDrawerData(data)
|
||||
setIsOkToPublish(data.isPublish??true)
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error));
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
const openEditModal = (entity: any) => {
|
||||
navigator(`/globalpolicy/datamasking/${entity.id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志
|
||||
* @param entity
|
||||
*/
|
||||
const openLogsModal = (entity: any) => {
|
||||
console.log('日志', entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param entity
|
||||
*/
|
||||
const deletePolicy = (entity: DataMaskStrategyItem) => {
|
||||
fetchData<BasicResponse<null>>(
|
||||
`strategy/${serviceId === undefined? 'global':'service'}/data-masking`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
eoParams: {
|
||||
service:serviceId,
|
||||
team:teamId,
|
||||
strategy:entity.id},
|
||||
}
|
||||
).then(response => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
manualReloadTable()
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复
|
||||
* @param entity
|
||||
*/
|
||||
const restorePolicy = (entity: any) => {
|
||||
fetchData<BasicResponse<null>>(
|
||||
`strategy/${serviceId === undefined? 'global':'service'}/data-masking/restore`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
eoParams: {
|
||||
service:serviceId,
|
||||
team:teamId,
|
||||
strategy:entity.id},
|
||||
}
|
||||
).then(response => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
manualReloadTable()
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const onSubmit = () => {
|
||||
return drawerRef.current?.publish()?.then((res) => {
|
||||
manualReloadTable();
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageList<DataMaskStrategyItem>
|
||||
id="data_masking_list"
|
||||
ref={pageListRef}
|
||||
columns={[...columns, ...operation]}
|
||||
request={async (params: DataMaskStrategyItem & {
|
||||
pageSize: number;
|
||||
current: number;
|
||||
},
|
||||
sort:Record<string, string>,
|
||||
filter:Record<string, string>) => getPolicyList(params,sort, filter)}
|
||||
addNewBtnTitle={$t("添加策略")}
|
||||
addNewBtnAccess="system.organization.member.edit"
|
||||
onAddNewBtnClick={() => { addPolicy() }}
|
||||
searchPlaceholder={$t("输入名称、筛选条件查找")}
|
||||
afterNewBtn={
|
||||
publishBtn && [<WithPermission key="removeFromDepPermission" access="system.organization.member.edit">
|
||||
<Button className="mr-btnbase" key="removeFromDep" onClick={() => publish()}>{$t('发布')}</Button>
|
||||
</WithPermission>]
|
||||
}
|
||||
onSearchWordChange={(e) => {
|
||||
setSearchWord(e.target.value)
|
||||
}}
|
||||
manualReloadTable={manualReloadTable}
|
||||
/>
|
||||
<DrawerWithFooter
|
||||
destroyOnClose={true}
|
||||
title={$t('申请发布')}
|
||||
width={'60%'}
|
||||
onClose={()=>{setDrawerVisible(false)}}
|
||||
okBtnTitle={$t('发布')}
|
||||
open={drawerVisible}
|
||||
submitDisabled={!isOkToPublish}
|
||||
submitAccess={`team.service.release.add`}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<PolicyPublishModalContent
|
||||
ref={drawerRef}
|
||||
data={drawerData! }
|
||||
/>
|
||||
</DrawerWithFooter>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default DataMasking;
|
||||
+18
-19
@@ -2,16 +2,14 @@
|
||||
import { PageProColumns } from "@common/components/aoplatform/PageList";
|
||||
import { frontendTimeSorter } from "@common/utils/dataTransfer";
|
||||
import { $t } from "@common/locales";
|
||||
import { StrategyStatusEnum, StrategyStatusColorClass } from "@common/const/policy/consts";
|
||||
|
||||
|
||||
export const DATA_MASSKING_TABLE_COLUMNS: PageProColumns<any>[] = [
|
||||
{
|
||||
title: ('策略名称'),
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
filters: true,
|
||||
onFilter: true,
|
||||
valueType: 'select',
|
||||
filterSearch: true,
|
||||
width: 160
|
||||
},
|
||||
{
|
||||
@@ -25,46 +23,47 @@ export const DATA_MASSKING_TABLE_COLUMNS: PageProColumns<any>[] = [
|
||||
},
|
||||
{
|
||||
title: ('发布状态'),
|
||||
dataIndex: 'status',
|
||||
dataIndex: 'publishStatus',
|
||||
filters: true,
|
||||
onFilter: true,
|
||||
onFilter: false ,
|
||||
width: 140,
|
||||
valueEnum: new Map([
|
||||
[true, <span className="text-status_success">{$t('已发布')}</span>],
|
||||
[false, <span className="text-status_fail">{$t('未发布')}</span>]
|
||||
])
|
||||
valueEnum: new Map(
|
||||
Object.keys(StrategyStatusEnum).map(key=>
|
||||
[key,
|
||||
<span className={StrategyStatusColorClass[key as keyof typeof StrategyStatusColorClass]}>{$t(StrategyStatusEnum[key as keyof typeof StrategyStatusEnum])}</span>
|
||||
]))
|
||||
},
|
||||
{
|
||||
title: ('启用'),
|
||||
dataIndex: 'enabled',
|
||||
dataIndex: 'isStop',
|
||||
filters: true,
|
||||
onFilter: true,
|
||||
onFilter: false ,
|
||||
valueEnum: {
|
||||
true: { text: <span className="text-status_success">{$t('启用')}</span> },
|
||||
false: { text: <span className="text-status_fail">{$t('禁用')}</span> }
|
||||
false: { text: <span className="text-status_success">{$t('启用')}</span> },
|
||||
true: { text: <span className="text-status_fail">{$t('禁用')}</span> }
|
||||
}
|
||||
},
|
||||
{
|
||||
title: ('筛选条件'),
|
||||
dataIndex: 'condition',
|
||||
dataIndex: 'filters',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: ('处理数'),
|
||||
dataIndex: 'treatmentNumber',
|
||||
dataIndex: 'processedTotal',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: ('更新者'),
|
||||
dataIndex: 'updater',
|
||||
dataIndex: 'operator',
|
||||
width: 140,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: ('更新时间'),
|
||||
dataIndex: 'createTime',
|
||||
dataIndex: 'updateTime',
|
||||
width: 182,
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => frontendTimeSorter(a, b, 'createTime')
|
||||
sorter: (a, b) => frontendTimeSorter(a, b, 'updateTime')
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,171 @@
|
||||
import { LoadingOutlined } from "@ant-design/icons"
|
||||
import InsidePage from "@common/components/aoplatform/InsidePage"
|
||||
import WithPermission from "@common/components/aoplatform/WithPermission"
|
||||
import { BasicResponse, STATUS_CODE, RESPONSE_TIPS, PLACEHOLDER } from "@common/const/const"
|
||||
import { RouterParams } from "@common/const/type"
|
||||
import { useGlobalContext } from "@common/contexts/GlobalStateContext"
|
||||
import { useFetch } from "@common/hooks/http"
|
||||
import { $t } from "@common/locales"
|
||||
import { App, Button, Form, Input, InputNumber, Row, Select, Spin } from "antd"
|
||||
import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react"
|
||||
import { useParams, useNavigate } from "react-router-dom"
|
||||
import DataMaskRuleTable from "./DataMaskingRuleTable"
|
||||
import FilterTable from "../FilterTable"
|
||||
import { DataMaskingConfigHandle ,DataMaskingConfigFieldType} from "@common/const/policy/type"
|
||||
import {PolicyOptions} from '@common/const/policy/consts'
|
||||
|
||||
const DataMaskingConfig = forwardRef<DataMaskingConfigHandle>((_,ref) => {
|
||||
const { message,modal } = App.useApp()
|
||||
const { teamId, serviceId, policyId } = useParams<RouterParams>();
|
||||
const [onEdit, setOnEdit] = useState<boolean>(!!teamId)
|
||||
const [form] = Form.useForm();
|
||||
const {fetchData} = useFetch()
|
||||
const { state } = useGlobalContext()
|
||||
const [ loading, setLoading ] = useState<boolean>(false)
|
||||
const navigator = useNavigate()
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
save:onFinish
|
||||
}));
|
||||
|
||||
// 获取表单默认值
|
||||
const getPolicyInfo = () => {
|
||||
setLoading(true)
|
||||
fetchData<BasicResponse<{ strategy: DataMaskingConfigFieldType }>>( `strategy/${serviceId === undefined? 'global':'service'}/data-masking`,{method:'GET',eoParams:{team:teamId, service:serviceId, strategy:policyId}}).then(response=>{
|
||||
const {code,data,msg} = response
|
||||
if(code === STATUS_CODE.SUCCESS){
|
||||
setTimeout(()=>{
|
||||
form.setFieldsValue({
|
||||
...data.strategy,
|
||||
type:'data-masking'
|
||||
})
|
||||
},0)
|
||||
}else{
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
}).finally(()=>setLoading(false))
|
||||
};
|
||||
|
||||
const onFinish:()=>Promise<boolean|string> = () => {
|
||||
return form.validateFields().then((value)=>{
|
||||
return fetchData<BasicResponse<{service:{id:string}}>>(
|
||||
`strategy/${serviceId === undefined? 'global':'service'}/data-masking`,
|
||||
{
|
||||
method:policyId === undefined? 'POST' : 'PUT',
|
||||
eoParams: {service:serviceId,team:teamId, policyId:policyId},
|
||||
eoBody:({...value})
|
||||
}).then(response=>{
|
||||
const {code,data,msg} = response
|
||||
if(code === STATUS_CODE.SUCCESS){
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
return Promise.resolve(true)
|
||||
}else{
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
return Promise.reject(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
}).catch((errorInfo)=>{
|
||||
return Promise.reject(errorInfo)
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (policyId !== undefined) {
|
||||
setOnEdit(true);
|
||||
getPolicyInfo();
|
||||
} else {
|
||||
setOnEdit(false);
|
||||
form.setFieldValue('type','data-masking')
|
||||
}
|
||||
return (form.setFieldsValue({}))
|
||||
}, [policyId]);
|
||||
|
||||
const policyOptions = useMemo(()=>PolicyOptions.map((x)=>({...x, label:$t(x.label)})),[state.language])
|
||||
|
||||
return (
|
||||
|
||||
<InsidePage pageTitle={ $t('编辑策略')|| '-'}
|
||||
showBorder={false}
|
||||
scrollPage={false}
|
||||
className="overflow-y-auto"
|
||||
>
|
||||
<Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={loading} wrapperClassName=' pb-PAGE_INSIDE_B pr-PAGE_INSIDE_X'>
|
||||
|
||||
<WithPermission access={onEdit ? ['team.service.service.edit'] :''}>
|
||||
<Form
|
||||
layout='vertical'
|
||||
labelAlign='left'
|
||||
scrollToFirstError
|
||||
form={form}
|
||||
className="w-full "
|
||||
name="systemConfig"
|
||||
onFinish={onFinish}
|
||||
autoComplete="off"
|
||||
>
|
||||
<div>
|
||||
<Form.Item<DataMaskingConfigFieldType>
|
||||
label={$t("策略名称")}
|
||||
name="name"
|
||||
rules={[{ required: true ,whitespace:true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<DataMaskingConfigFieldType>
|
||||
label={$t("策略类型")}
|
||||
name="type"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} options={policyOptions} >
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
|
||||
<Form.Item<DataMaskingConfigFieldType>
|
||||
label={$t("优先级")}
|
||||
name={'priority'}
|
||||
rules={[{required: true}]}
|
||||
>
|
||||
<InputNumber className="w-INPUT_NORMAL" min={1} placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<DataMaskingConfigFieldType>
|
||||
label={$t("描述")}
|
||||
name="description"
|
||||
>
|
||||
<Input.TextArea className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<DataMaskingConfigFieldType>
|
||||
label={$t("匹配条件")}
|
||||
name="filters"
|
||||
>
|
||||
<FilterTable />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<DataMaskingConfigFieldType>
|
||||
label={$t("数据脱敏规则")}
|
||||
name="rules"
|
||||
rules={[{required: true}]}
|
||||
>
|
||||
<DataMaskRuleTable />
|
||||
</Form.Item>
|
||||
|
||||
<Row className="mb-[10px]">
|
||||
<WithPermission access={onEdit ? ['team.service.service.edit'] :''}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
{$t('保存')}
|
||||
</Button>
|
||||
</WithPermission>
|
||||
<Button className="ml-btnrbase" type="default" onClick={() => navigator('/globalpolicy/datamasking/list')}>
|
||||
{$t('取消')}
|
||||
</Button>
|
||||
</Row>
|
||||
</div>
|
||||
</Form>
|
||||
</WithPermission>
|
||||
</Spin>
|
||||
</InsidePage>
|
||||
)
|
||||
})
|
||||
export default DataMaskingConfig
|
||||
@@ -0,0 +1,158 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Form, Input, Select, Modal } from 'antd';
|
||||
import { useGlobalContext } from '@common/contexts/GlobalStateContext';
|
||||
import { $t } from '@common/locales';
|
||||
import { PLACEHOLDER } from '@common/const/const';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { DataMaskRuleFormProps } from '@common/const/policy/type';
|
||||
import { MatchRules, DataFormatOptions, DataMaskReplaceStrOptions, DataMaskBaseOptionOptions, DataMaskOrderOptions } from '@common/const/policy/consts';
|
||||
const DataMaskRuleForm: React.FC<DataMaskRuleFormProps> = ({ editData, ruleList, onSave, onClose,modalVisible }) => {
|
||||
const [form] = Form.useForm();
|
||||
const [matchType, setMatchType] = useState<string>('');
|
||||
const [matchValue, setMatchValue] = useState<string>('');
|
||||
const [maskType, setMaskType] = useState<string>('');
|
||||
const [replaceType, setReplaceType] = useState<string>('');
|
||||
const {state} = useGlobalContext()
|
||||
useEffect(() => {
|
||||
if (editData) {
|
||||
form.setFieldsValue(editData);
|
||||
}
|
||||
}, [editData, form]);
|
||||
|
||||
const handleSave = () => {
|
||||
form.validateFields().then((values) => {
|
||||
const submitData = prepareSubmitData(values);
|
||||
const newRuleList =ruleList ? [...ruleList] : [];
|
||||
if (editData) {
|
||||
const index = newRuleList.findIndex((rule) => rule.eoKey === editData.eoKey);
|
||||
if (index !== -1) {
|
||||
newRuleList.splice(index, 1);
|
||||
}
|
||||
}
|
||||
newRuleList.unshift({ ...submitData, eoKey: editData?.eoKey || uuidv4() });
|
||||
onSave?.(newRuleList);
|
||||
onClose?.();
|
||||
clearData()
|
||||
});
|
||||
};
|
||||
|
||||
const clearData = ()=>{
|
||||
form.resetFields()
|
||||
setMatchType('');
|
||||
setMaskType('');
|
||||
setMatchValue('');
|
||||
setReplaceType('');
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (editData) {
|
||||
form.setFieldsValue(editData);
|
||||
editData?.match?.type && setMatchType(editData.match.type);
|
||||
editData?.mask?.type && setMaskType(editData.mask.type);
|
||||
editData?.match?.value && setMatchValue(editData.match.value);
|
||||
editData?.mask?.replace?.type && setReplaceType(editData.mask.replace.type);
|
||||
}
|
||||
}, [editData, form]);
|
||||
|
||||
const handleMatchTypeChange = (value: string) => {
|
||||
setMatchType(value);
|
||||
form.resetFields(['match.value','mask.begin', 'mask.length', 'mask.replace.type', 'mask.replace.value']);
|
||||
};
|
||||
|
||||
const handleMatchValueChange = (value: string) => {
|
||||
setMatchValue(value);
|
||||
form.resetFields(['mask.begin', 'mask.length', 'mask.replace.type', 'mask.replace.value']);
|
||||
};
|
||||
|
||||
const handleMaskTypeChange = (value: string) => {
|
||||
setMaskType(value);
|
||||
form.resetFields(['mask.begin', 'mask.length', 'mask.replace.type', 'mask.replace.value']);
|
||||
};
|
||||
|
||||
const handleReplaceTypeChange = (value: string) => {
|
||||
setReplaceType(value);
|
||||
form.resetFields(['mask.replace.value']);
|
||||
};
|
||||
|
||||
const prepareSubmitData = (formData: any) => {
|
||||
const submitData: any = {
|
||||
match: {
|
||||
type: formData.match.type,
|
||||
value: formData.match.value
|
||||
},
|
||||
mask: {
|
||||
type: formData.mask.type
|
||||
}
|
||||
};
|
||||
|
||||
switch (formData.mask.type) {
|
||||
case 'replacement':
|
||||
submitData.mask = {
|
||||
...submitData.mask,
|
||||
replace: formData.mask.replace
|
||||
};
|
||||
break;
|
||||
case 'shuffling':
|
||||
break;
|
||||
default:
|
||||
submitData.mask.begin = formData.mask.begin;
|
||||
submitData.mask.length = formData.mask.length;
|
||||
break;
|
||||
}
|
||||
return submitData;
|
||||
};
|
||||
|
||||
const matchRuleOptions = useMemo(()=>MatchRules.map(rule => ({ label: $t(rule.label), value: rule.value })),[state.language])
|
||||
const dataFormatOptions = useMemo(()=>DataFormatOptions.map(rule => ({ label: $t(rule.label), value: rule.value })),[state.language])
|
||||
const dataMaskBaseOptions = useMemo(()=>DataMaskBaseOptionOptions.map(rule => ({ label: $t(rule.label), value: rule.value })),[state.language])
|
||||
const dataMaskOrderOptions = useMemo(()=>DataMaskOrderOptions.map(rule => ({ label: $t(rule.label), value: rule.value })),[state.language])
|
||||
const dataMaskReplaceStrOptions = useMemo(()=>DataMaskReplaceStrOptions.map(rule => ({ label: $t(rule.label), value: rule.value })),[state.language])
|
||||
|
||||
return (
|
||||
<Modal open={modalVisible} onCancel={onClose} onOk={handleSave} title={$t("配置脱敏规则")}>
|
||||
<Form form={form} layout="vertical" className="p-4">
|
||||
<Form.Item name={['match', 'type']} label={$t("匹配类型")} rules={[{ required: true }]}>
|
||||
<Select placeholder={$t(PLACEHOLDER.select)} onChange={handleMatchTypeChange} options={matchRuleOptions}/>
|
||||
</Form.Item>
|
||||
|
||||
{ matchType && <Form.Item name={['match', 'value']} label={$t("匹配值")} rules={[{ required: true }]}>{
|
||||
matchType === 'inner' ?
|
||||
<Select placeholder={$t(PLACEHOLDER.select)} onChange={handleMatchValueChange} options={dataFormatOptions}/>
|
||||
:<Input placeholder={$t(PLACEHOLDER.input)} />}
|
||||
</Form.Item>
|
||||
}
|
||||
|
||||
<Form.Item name={['mask', 'type']} label={$t("脱敏类型")} rules={[{ required: true }]}>
|
||||
<Select placeholder={$t(PLACEHOLDER.select)} onChange={handleMaskTypeChange} options={ matchType && ['name', 'phone', 'id-card', 'bank-card'].indexOf(matchValue) !== -1 ? dataMaskOrderOptions:dataMaskBaseOptions} />
|
||||
</Form.Item>
|
||||
|
||||
{['partial-display', 'partial-masking', 'truncation'].includes(maskType) && (
|
||||
<>
|
||||
<Form.Item name={['mask', 'begin']} label={$t("起始位置")} rules={[{ required: true }]}>
|
||||
<Input type="number" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
<Form.Item name={['mask', 'length']} label={$t("长度")} rules={[{ required: true }]}>
|
||||
<Input type="number" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
{maskType === 'replacement' && (
|
||||
<>
|
||||
<Form.Item name={['mask', 'replace', 'type']} label={$t("替换类型")} rules={[{ required: true }]}>
|
||||
<Select placeholder={$t(PLACEHOLDER.select)} onChange={handleReplaceTypeChange} options={dataMaskReplaceStrOptions}/>
|
||||
</Form.Item>
|
||||
{replaceType === 'custom' && (
|
||||
<Form.Item name={['mask', 'replace', 'value']} label={$t("替换值")} rules={[{ required: true }]}>
|
||||
<Input placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataMaskRuleForm;
|
||||
@@ -0,0 +1,152 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Button, Table, Tooltip } from 'antd';
|
||||
import DataMaskRuleForm from './DataMaskingRuleForm';
|
||||
import { $t } from '@common/locales';
|
||||
import {DataMaskRuleTableProps, MaskRuleData} from "@common/const/policy/type";
|
||||
import { useGlobalContext } from '@common/contexts/GlobalStateContext';
|
||||
import { COLUMNS_TITLE } from '@common/const/const';
|
||||
import TableBtnWithPermission from '@common/components/aoplatform/TableBtnWithPermission';
|
||||
|
||||
|
||||
const DataMaskRuleTable: React.FC<DataMaskRuleTableProps> = ({
|
||||
disabled = false,
|
||||
value,
|
||||
onChange
|
||||
}) => {
|
||||
const [editData, setEditData] = useState<MaskRuleData | undefined>(undefined);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const {state} = useGlobalContext()
|
||||
const openDrawer = (type:'add'|'edit',data?: MaskRuleData) => {
|
||||
setEditData(data);
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
const closeDrawer = () => {
|
||||
setIsModalVisible(false);
|
||||
setEditData(undefined);
|
||||
};
|
||||
|
||||
const handleSave = (newRuleList: MaskRuleData[]) => {
|
||||
onChange?.(newRuleList);
|
||||
};
|
||||
|
||||
const columns = useMemo(()=> [
|
||||
{
|
||||
title: $t('匹配类型'),
|
||||
dataIndex: ['match', 'type'],
|
||||
key: 'matchType',
|
||||
render: (text: string) => {
|
||||
switch (text) {
|
||||
case 'inner':
|
||||
return $t('数据格式');
|
||||
case 'keyword':
|
||||
return $t('关键字');
|
||||
case 'regex':
|
||||
return $t('正则表达式');
|
||||
case 'json_path':
|
||||
return $t('JSON Path');
|
||||
default:
|
||||
return text;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: $t('匹配值'),
|
||||
dataIndex: ['match', 'value'],
|
||||
key: 'matchValue',
|
||||
render: (text: string) => {
|
||||
switch (text) {
|
||||
case 'name':
|
||||
return $t('姓名');
|
||||
case 'phone':
|
||||
return $t('手机号');
|
||||
case 'id-card':
|
||||
return $t('身份证号');
|
||||
case 'bank-card':
|
||||
return $t('银行卡号');
|
||||
case 'date':
|
||||
return $t('日期');
|
||||
case 'amount':
|
||||
return $t('金额');
|
||||
default:
|
||||
return text;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: $t('脱敏类型'),
|
||||
dataIndex: ['mask', 'type'],
|
||||
key: 'maskType',
|
||||
render: (text: string) => {
|
||||
switch (text) {
|
||||
case 'partial-display':
|
||||
return $t('局部显示');
|
||||
case 'partial-masking':
|
||||
return $t('局部遮蔽');
|
||||
case 'truncation':
|
||||
return $t('截取');
|
||||
case 'replacement':
|
||||
return $t('替换');
|
||||
case 'shuffling':
|
||||
return $t('乱序');
|
||||
default:
|
||||
return text;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: $t('脱敏规则'),
|
||||
dataIndex: 'mask',
|
||||
key: 'maskRule',
|
||||
render: (mask: any) => {
|
||||
switch (mask.type) {
|
||||
case 'replacement':
|
||||
return (
|
||||
<Tooltip title={`${$t('类型')}:${mask.replace.type === 'random' ? $t('随机字符串') : $t('自定义字符串; 值:')}${mask.replace.value}`}>
|
||||
{$t('类型')}:{mask.replace.type === 'random' ? $t('随机字符串') : $t('自定义字符串; 值:')}{mask.replace.value}
|
||||
</Tooltip>
|
||||
);
|
||||
case 'shuffling':
|
||||
return '-';
|
||||
default:
|
||||
return (
|
||||
<Tooltip title={$t('起始位置:(0)位;长度:(1)位',[mask.begin,mask.length])}>
|
||||
{$t('起始位置:(0)位;长度:(1)位',[mask.begin,mask.length])}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: COLUMNS_TITLE.operate,
|
||||
key: 'action',
|
||||
render: (_: any, record: MaskRuleData) => (
|
||||
<TableBtnWithPermission key="edit" btnType="edit" onClick={()=>{openDrawer('edit', record)}} btnTitle={$t("编辑")}/>
|
||||
),
|
||||
},
|
||||
],[state.language])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
!disabled &&<Button onClick={() => openDrawer('add')}>
|
||||
{$t('添加配置')}
|
||||
</Button>
|
||||
}
|
||||
{value && value.length >0 && <Table
|
||||
className={disabled ? '' : 'mt-btnbase'}
|
||||
size='small'
|
||||
pagination={false}
|
||||
columns={columns} dataSource={value} rowKey="eoKey" /> }
|
||||
<DataMaskRuleForm
|
||||
editData={editData}
|
||||
ruleList={value}
|
||||
onSave={handleSave}
|
||||
onClose={closeDrawer}
|
||||
modalVisible = {isModalVisible}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataMaskRuleTable;
|
||||
@@ -1,20 +1,23 @@
|
||||
import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
|
||||
import { $t } from "@common/locales/index.ts";
|
||||
import PolicyTabContainer from "./policyTabContainer.tsx";
|
||||
import DataMasking from "./dataMasking.tsx";
|
||||
import PolicyTabContainer from "./PolicyTabContainer.tsx";
|
||||
import DataMasking from "./dataMasking/DataMasking.tsx";
|
||||
import { useGlobalContext } from "@common/contexts/GlobalStateContext.tsx";
|
||||
import { useMemo } from "react";
|
||||
|
||||
|
||||
const PartitionInsideGlobalPolicy = () => {
|
||||
const {state} = useGlobalContext()
|
||||
/**
|
||||
* tab列表
|
||||
*/
|
||||
const tabItems = [
|
||||
const tabItems =useMemo(()=> [
|
||||
{
|
||||
key: 'dataMasking',
|
||||
label: $t('数据脱敏'),
|
||||
children: <div className="pr-[40px] preview-document h-full pb-[40px]"><DataMasking publishBtn rowOperation={['edit', 'logs', 'delete']} /></div>
|
||||
}
|
||||
]
|
||||
],[state.language])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { $t } from "@common/locales/index.ts";
|
||||
import DataMasking from "@core/pages/policy/dataMasking";
|
||||
import PolicyTabContainer from "@core/pages/policy/policyTabContainer";
|
||||
import DataMasking from "./dataMasking/DataMasking";
|
||||
import PolicyTabContainer from "./PolicyTabContainer";
|
||||
|
||||
const servicePolicy = () => {
|
||||
/**
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import {App, Button, Col, Form, Input, Row, Select, Space, Spin, Switch} from "antd";
|
||||
import {forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState} from "react";
|
||||
import EditableTableWithModal from "@common/components/aoplatform/EditableTableWithModal.tsx";
|
||||
import styles from "./SystemInsideApi.module.css"
|
||||
import {BasicResponse, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
|
||||
import {useFetch} from "@common/hooks/http.ts";
|
||||
import { API_PATH_MATCH_RULES, API_PROTOCOL, HTTP_METHOD, MATCH_CONFIG, MatchPositionEnum, MatchTypeEnum } from "../../../const/system/const.tsx";
|
||||
import { SystemInsideRouterCreateHandle, SystemInsideRouterCreateProps, SystemApiProxyFieldType, SystemInsideApiProxyHandle } from "../../../const/system/type.ts";
|
||||
import { MatchItem } from "@common/const/type.ts";
|
||||
import { MatchItem, RouterParams } from "@common/const/type.ts";
|
||||
import { validateUrlSlash } from "@common/utils/validate.ts";
|
||||
import { $t } from "@common/locales/index.ts";
|
||||
import SystemInsideApiProxy from "@core/pages/system/api/SystemInsideApiProxy.tsx";
|
||||
import { LoadingOutlined } from "@ant-design/icons";
|
||||
import { useGlobalContext } from "@common/contexts/GlobalStateContext.tsx";
|
||||
import { RouterParams } from "@core/components/aoplatform/RenderRoutes.tsx";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useSystemContext } from "@core/contexts/SystemContext.tsx";
|
||||
import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
|
||||
@@ -236,8 +234,6 @@ const SystemInsideRouterCreate = forwardRef<SystemInsideRouterCreateHandle,Syste
|
||||
configFields={translatedMatchConfig}
|
||||
/>
|
||||
</Form.Item>
|
||||
{/* } */}
|
||||
|
||||
|
||||
<Row className="mb-btnybase mt-[40px]"><Col ><span className="font-bold mr-[13px]">{$t('转发规则设置')} </span></Col></Row>
|
||||
<Form.Item<SystemApiProxyFieldType>
|
||||
|
||||
@@ -4,10 +4,9 @@ import { useState, useRef, useEffect, useMemo, FC } from "react";
|
||||
import { useParams, Link, useLocation } from "react-router-dom";
|
||||
import PageList, { PageProColumns } from "@common/components/aoplatform/PageList";
|
||||
import { PublishApprovalModalContent } from "@common/components/aoplatform/PublishApprovalModalContent";
|
||||
import { RouterParams } from "@core/components/aoplatform/RenderRoutes";
|
||||
import { PUBLISH_APPROVAL_RECORD_INNER_TABLE_COLUMN, PUBLISH_APPROVAL_VERSION_INNER_TABLE_COLUMN, PublishApplyStatusEnum, PublishStatusEnum, PublishTableStatusColorClass } from "@common/const/approval/const";
|
||||
import { BasicResponse, COLUMNS_TITLE, DELETE_TIPS, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
|
||||
import { SimpleMemberItem } from "@common/const/type.ts";
|
||||
import { RouterParams, SimpleMemberItem } from "@common/const/type.ts";
|
||||
import { MemberTableListItem } from "../../../const/member/type";
|
||||
import { useBreadcrumb } from "@common/contexts/BreadcrumbContext";
|
||||
import { useFetch } from "@common/hooks/http";
|
||||
|
||||
@@ -16,8 +16,6 @@ const TableType = {
|
||||
provider :SERVICE_TABLE_GLOBAL_COLUMNS_CONFIG,
|
||||
subscribers :APPLICATION_TABLE_GLOBAL_COLUMNS_CONFIG
|
||||
}
|
||||
const APP_MODE = import.meta.env.VITE_APP_MODE;
|
||||
|
||||
|
||||
type MonitorTableProps<T> = {
|
||||
type:'api'|'subscribers'|'provider'
|
||||
@@ -77,13 +75,12 @@ const MonitorTable = forwardRef<MonitorTableHandler, MonitorTableProps<unknown>>
|
||||
{
|
||||
title: COLUMNS_TITLE.operate,
|
||||
key: 'option',
|
||||
btnNums:2,
|
||||
btnNums:1,
|
||||
fixed:'right',
|
||||
hideInSetting:true,
|
||||
valueType: 'option',
|
||||
render: (_: React.ReactNode, entity: unknown) => [
|
||||
// <TableBtnWithPermission access="system.dashboard.self.view" key="view" onClick={()=>onRowClick(entity)} btnTitle="查看"/>,
|
||||
APP_MODE === 'pro' ? <TableBtnWithPermission access="" key="view" btnType="view" onClick={()=>onRowClick(entity)} btnTitle="查看"/> : null
|
||||
<TableBtnWithPermission access="" key="view" btnType="view" onClick={()=>onRowClick(entity)} btnTitle="查看"/>
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
|
||||
import { App, Select, Button, Tabs, TabsProps, Empty, Drawer, Spin } from "antd";
|
||||
import { App, Button, Tabs, TabsProps, Empty, Drawer, Spin } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||
import { useState, useEffect, useRef, useReducer } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
|
||||
import { SummaryPieData, SearchBody, PieData, MonitorApiData, MonitorSubscriberData, InvokeData, MessageData } from "@dashboard/const/type";
|
||||
import { getTime, getTimeUnit, changeNumberUnit } from "../utils/dashboard";
|
||||
import { RouterParams } from "@core/components/aoplatform/RenderRoutes";
|
||||
import ScrollableSection from "@common/components/aoplatform/ScrollableSection";
|
||||
import { RangeValue, TimeRange } from "@common/components/aoplatform/TimeRangeSelector";
|
||||
import TimeRangeSelector from "@common/components/aoplatform/TimeRangeSelector";
|
||||
@@ -19,7 +17,6 @@ import DashboardDetail from "@dashboard/pages/DashboardDetail";
|
||||
import { $t } from "@common/locales";
|
||||
|
||||
dayjs.extend(customParseFormat);
|
||||
const APP_MODE = import.meta.env.VITE_APP_MODE;
|
||||
|
||||
export type MonitorTotalPageProps = {
|
||||
fetchPieData:(body:SearchBody)=>Promise<BasicResponse<PieData>>
|
||||
@@ -209,17 +206,17 @@ const MonitorTotalPage = (props:MonitorTotalPageProps) => {
|
||||
{
|
||||
label:$t('API 请求量 Top10'),
|
||||
key:'api',
|
||||
children:<MonitorTable className="py-[10px]" ref={monitorApiTableRef} type='api' id="dashboard_top10_api" onRowClick={(record)=>{APP_MODE !== 'pro' ? null : getDetailData(record as MonitorApiData,'api')}} request={()=>getTablesData(queryData||{},'api')}/>
|
||||
children:<MonitorTable className="py-[10px]" ref={monitorApiTableRef} type='api' id="dashboard_top10_api" onRowClick={(record)=>{ getDetailData(record as MonitorApiData,'api')}} request={()=>getTablesData(queryData||{},'api')}/>
|
||||
},
|
||||
{
|
||||
label:$t('消费者调用量 Top10'),
|
||||
key:'subscribers',
|
||||
children:<MonitorTable className="py-[10px]" ref={monitorSubTableRef} type='subscribers' id="dashboard_top10_subscriber" onRowClick={(record)=>{APP_MODE !== 'pro' ? null : getDetailData(record as MonitorSubscriberData,'subscriber')}} request={()=>getTablesData(queryData||{},'subscriber')} />
|
||||
children:<MonitorTable className="py-[10px]" ref={monitorSubTableRef} type='subscribers' id="dashboard_top10_subscriber" onRowClick={(record)=>{getDetailData(record as MonitorSubscriberData,'subscriber')}} request={()=>getTablesData(queryData||{},'subscriber')} />
|
||||
},
|
||||
{
|
||||
label:$t('服务被调用量 Top10'),
|
||||
key:'providers',
|
||||
children:<MonitorTable className="py-[10px]" ref={monitorSubTableRef} type='provider' id="dashboard_top10_provider" onRowClick={(record)=>{APP_MODE !== 'pro' ? null : getDetailData(record as MonitorSubscriberData,'provider')}} request={()=>getTablesData(queryData||{},'provider')} />
|
||||
children:<MonitorTable className="py-[10px]" ref={monitorSubTableRef} type='provider' id="dashboard_top10_provider" onRowClick={(record)=>{getDetailData(record as MonitorSubscriberData,'provider')}} request={()=>getTablesData(queryData||{},'provider')} />
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
import { Tabs, TabsProps } from "antd";
|
||||
import DashboardTotal from "./DashboardTotal";
|
||||
import { Outlet, useNavigate, useParams } from "react-router-dom";
|
||||
import { RouterParams } from "@core/components/aoplatform/RenderRoutes";
|
||||
import { useEffect, useState } from "react";
|
||||
import { $t } from "@common/locales";
|
||||
import { RouterParams } from "@common/const/type";
|
||||
|
||||
const APP_MODE = import.meta.env.VITE_APP_MODE;
|
||||
|
||||
export default function DashboardTabPage(){
|
||||
const { dashboardType} = useParams<RouterParams>()
|
||||
@@ -41,10 +40,10 @@ export default function DashboardTabPage(){
|
||||
]
|
||||
|
||||
return (<>
|
||||
{APP_MODE === 'pro' ? <Tabs activeKey={activeKey} onChange={(val)=>{
|
||||
<Tabs activeKey={activeKey} onChange={(val)=>{
|
||||
setActiveKey(val);
|
||||
navigateTo(`/analytics/${val === 'total' ? val :`${val}/list`}`)
|
||||
}}
|
||||
items={monitorTabItems} className="h-auto mt-[6px]" size="small" tabBarStyle={{paddingLeft:'10px',marginTop:'0px',marginBottom:'0px'}} />
|
||||
: <Outlet />} </>)
|
||||
items={monitorTabItems} className="h-full overflow-hidden mt-[6px] [&>.ant-tabs-content-holder]:overflow-auto" size="small" tabBarStyle={{paddingLeft:'10px',marginTop:'0px',marginBottom:'0px'}} />
|
||||
</>)
|
||||
}
|
||||
@@ -18,6 +18,7 @@ export type ServiceBasicInfoType = {
|
||||
invokeAddress:string
|
||||
approvalType:'auto'|'manual'
|
||||
serviceKind:'ai'|'rest'
|
||||
sitePrefix?:string
|
||||
}
|
||||
|
||||
export type ServiceDetailType = {
|
||||
|
||||
@@ -335,6 +335,7 @@ p{
|
||||
background-color:transparent
|
||||
}
|
||||
|
||||
|
||||
/* .hidden-switcher .ant-tree-switcher {
|
||||
@apply hidden;
|
||||
} */
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||
import { RouterParams } from "@core/components/aoplatform/RenderRoutes.tsx";
|
||||
import { App, Avatar, Button, Descriptions, Divider, Tabs } from "antd";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useBreadcrumb } from "@common/contexts/BreadcrumbContext.tsx";
|
||||
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const.tsx";
|
||||
import { useFetch } from "@common/hooks/http.ts";
|
||||
import { DefaultOptionType } from "antd/es/cascader";
|
||||
import { ApplyServiceHandle, ServiceBasicInfoType, ServiceDetailType } from "../../const/serviceHub/type.ts";
|
||||
import { EntityItem } from "@common/const/type.ts";
|
||||
import { EntityItem, RouterParams } from "@common/const/type.ts";
|
||||
import { ApplyServiceModal } from "./ApplyServiceModal.tsx";
|
||||
import ServiceHubApiDocument from "./ServiceHubApiDocument.tsx";
|
||||
import Integrate from "./integrate.tsx";
|
||||
import { ApiFilled, ArrowLeftOutlined, BgColorsOutlined } from "@ant-design/icons";
|
||||
import { SimpleSystemItem } from "@core/const/system/type.ts";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import DOMPurify from 'dompurify';
|
||||
import { $t } from "@common/locales/index.ts";
|
||||
@@ -54,7 +52,7 @@ const ServiceHubDetail = () => {
|
||||
}
|
||||
|
||||
const getServiceBasicInfo = () => {
|
||||
fetchData<BasicResponse<{ service: ServiceDetailType }>>('catalogue/service', { method: 'GET', eoParams: { service: serviceId }, eoTransformKeys: ['app_num', 'api_num', 'update_time', 'api_doc', 'invoke_address', 'approval_type', 'service_kind'] }).then(response => {
|
||||
fetchData<BasicResponse<{ service: ServiceDetailType }>>('catalogue/service', { method: 'GET', eoParams: { service: serviceId }, eoTransformKeys: ['app_num', 'api_num', 'update_time', 'api_doc', 'invoke_address', 'approval_type', 'service_kind','site_prefix'] }).then(response => {
|
||||
const { code, data, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
setService({ ...data.service, apiDoc: modifyApiDoc(data.service.apiDoc, data.service.basic?.invokeAddress) })
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import { ServiceDetailType } from "@market/const/serviceHub/type"
|
||||
import { Input, Button, Space, message } from 'antd'
|
||||
import { $t } from "@common/locales"
|
||||
import { RESPONSE_TIPS } from '@common/const/const'
|
||||
import { downloadFile } from "@common/utils/download.ts"
|
||||
|
||||
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
|
||||
import { useParams } from "react-router-dom"
|
||||
import { useState, useEffect } from "react"
|
||||
import { RouterParams } from "@common/const/type"
|
||||
import { useFetch } from "@common/hooks/http"
|
||||
|
||||
const Integrate = ({ service }: { service: ServiceDetailType }) => {
|
||||
console.log('service', service);
|
||||
const stepClass = "leading-[20px] truncate font-bold items-center gap-[4px] mt-[15px]";
|
||||
const url = 'https://www.baidu.com';
|
||||
|
||||
const [url, setUrl] = useState('');
|
||||
const { serviceId} = useParams<RouterParams>()
|
||||
const {fetchData} = useFetch()
|
||||
|
||||
useEffect(()=>{
|
||||
setUrl(`${service?.basic?.sitePrefix || window.location?.origin}/${serviceId}/swagger` )
|
||||
},[service])
|
||||
/**
|
||||
* Agent 平台地址
|
||||
*/
|
||||
@@ -29,13 +35,15 @@ const Integrate = ({ service }: { service: ServiceDetailType }) => {
|
||||
* 下载文件
|
||||
*/
|
||||
const onDownload = () => {
|
||||
console.log('downloadFile');
|
||||
downloadFile({
|
||||
body: '',
|
||||
contentType: 'raw',
|
||||
filename: 'test_response',
|
||||
responseType: 'text',
|
||||
uri: ''
|
||||
fetchData<BasicResponse<null>>(`export/openapi/${serviceId}`, { method: 'GET' ,headers:{
|
||||
'Content-Type': 'application/octet-stream'
|
||||
}}).then(response => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
}
|
||||
return (
|
||||
@@ -46,12 +54,13 @@ const Integrate = ({ service }: { service: ServiceDetailType }) => {
|
||||
<div className="my-[10px]">{$t('不同 Agent 平台的操作细节可查看')} <a href={agentAddress} target="_blank">{$t('《 Agent 对接手册》')}</a>。</div>
|
||||
<p className={stepClass}>{$t('步骤二:导入 API 文档数据')}</p>
|
||||
<div className='my-[10px]'>{$t('可通过以下 URL 或 下载 Json 文件,导入 API 文档数据到 Agent 平台中。')}</div>
|
||||
<div>
|
||||
<Space.Compact className="w-[500px]">
|
||||
<Input disabled defaultValue={url} />
|
||||
<div className="flex w-full items-center gap-[30px]">
|
||||
<Space.Compact className=" flex-1 ">
|
||||
<Input disabled value={url} />
|
||||
<Button type="primary" onClick={copyURL}>{$t('复制 URL')}</Button>
|
||||
</Space.Compact>
|
||||
<span className="text-[14px] font-bold mx-[30px]">OR</span> <Button onClick={onDownload}>{$t('下载 Json 文件')}</Button>
|
||||
<span className="text-[14px] font-bold">OR</span>
|
||||
<Button onClick={onDownload}>{$t('下载 Json 文件')}</Button>
|
||||
</div>
|
||||
<p className={stepClass}>{$t('步骤三:配置 API 密钥')}</p>
|
||||
<div className='my-[10px]'>{$t('在')}<a href={consumerAddress} target="_blank"> {$t('消费者')} </a>{$t('菜单中,选择已通过本 API 服务申请的消费者,')}</div>
|
||||
|
||||
Reference in New Issue
Block a user