Merge pull request #6 from maggieyyy/main

dashboard && tour && guest_login
This commit is contained in:
Dot.L
2024-08-21 18:39:45 +08:00
committed by GitHub
64 changed files with 977 additions and 857 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
## 代码同步
packages目录下,部分子项目为企业版独有,不要同步到开源版:
packages/businessEntry, packages/dashboard, packages/openApi, packages/systemRunning, README.pro.md
packages/businessEntry, packages/openApi, packages/systemRunning, README.pro.md
## 安装依赖
建议使用pnpm
+2 -1
View File
@@ -15,7 +15,7 @@
"dev": "lerna run dev --scope=core --stream",
"dev:pro": "lerna run dev --scope=business-entry --stream",
"stop": "kill-port --port 5000",
"scan":"i18next-scanner --config i18next-scanner.config.js"
"scan": "i18next-scanner --config i18next-scanner.config.js"
},
"keywords": [],
"author": "",
@@ -42,6 +42,7 @@
"react-ace": "^10.1.0",
"react-dom": "^18.2.0",
"react-i18next": "^15.0.1",
"react-joyride": "^2.8.2",
"react-router-dom": "^6.20.0",
"tailwindcss": "^3.3.5",
"uuid": "^9.0.1",
@@ -11,7 +11,6 @@ import {FC,lazy} from 'react';
import { TeamProvider } from '@core/contexts/TeamContext.tsx';
import SystemOutlet from '@core/pages/system/SystemOutlet.tsx';
import { DashboardProvider } from '@core/contexts/DashboardContext.tsx';
import { PartitionProvider } from '@core/contexts/PartitionContext.tsx';
import { TenantManagementProvider } from '@market/contexts/TenantManagementContext.tsx';
type RouteConfig = {
@@ -182,6 +181,11 @@ const PUBLIC_ROUTES:RouteConfig[] = [
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublish.tsx')),
children:[
{
path:'',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublishList.tsx')),
},
{
path:'*',
key: uuidv4(),
@@ -202,6 +206,10 @@ const PUBLIC_ROUTES:RouteConfig[] = [
]
}
]
},{
path:'dashboardsetting',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideDashboardSetting.tsx')),
},
{
path:'cluster',
@@ -338,16 +346,6 @@ const PUBLIC_ROUTES:RouteConfig[] = [
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@openApi/pages/OpenApiList.tsx')),
key:uuidv4(),
},
{
path:'logretrieval',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/logRetrieval/LogRetrieval.tsx')),
key:uuidv4(),
},
{
path:'auditlog',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/auditLog/AuditLog.tsx')),
key:uuidv4(),
},
{
path:'assets',
component:<p></p>,
@@ -394,7 +392,7 @@ const PUBLIC_ROUTES:RouteConfig[] = [
key: uuidv4(),
children:[{
path:'template/:moduleId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '../../../../common/src/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
key:uuidv4()
}]
@@ -405,10 +403,20 @@ const PUBLIC_ROUTES:RouteConfig[] = [
key: uuidv4(),
children:[{
path:'template/:moduleId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '../../../../common/src/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
key:uuidv4()
}]
},
{
path:'userProfile/*',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/UserProfile.tsx')),
key:uuidv4(),
children:[{
path:'changepsw',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/ChangePsw.tsx')),
key:uuidv4()
}]
}
]
},
@@ -433,7 +441,7 @@ const generateRoutes = (routerConfig: RouteConfig[]) => {
const LazyComponent = route.lazy as React.ExoticComponent<unknown>;
routeElement = (
<Suspense fallback={ <div className=''><Skeleton className='m-btnbase w-[calc(100%-20px)]' active /></div>}>
<Suspense fallback={ <div className=''><Skeleton className='m-btnbase w-calc-100vw-minus-padding-r' active /></div>}>
{route.provider ? (
createElement(route.provider, {}, <LazyComponent />)
) : (
@@ -28,6 +28,6 @@
"@businessEntry/*": ["./src/*"],
},
},
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TransferTable.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/GroupTree.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../common/src/components/aoplatform/ScrollableSection.tsx", "../common/src/utils/postcat.tsx", "../common/src/utils/curl.ts", "../common/src/components/aoplatform/ResetPsw.tsx", "../common/src/components/aoplatform/SubscribeApprovalModalContent.tsx", "src/components/aoplatform/RenderRoutes.tsx", "../common/src/components/aoplatform/PublishApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePage.tsx", "../common/src/const/type.ts", "../common/src/components/aoplatform/intelligent-plugin", "../common/src/const/domain"],
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../common/src/components/aoplatform/ScrollableSection.tsx", "../common/src/utils/postcat.tsx", "../common/src/utils/curl.ts", "../common/src/components/aoplatform/ResetPsw.tsx", "../common/src/components/aoplatform/SubscribeApprovalModalContent.tsx", "src/components/aoplatform/RenderRoutes.tsx", "../common/src/components/aoplatform/PublishApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePage.tsx", "../common/src/const/type.ts", "../common/src/components/aoplatform/intelligent-plugin", "../common/src/const/domain"],
"references": [{ "path": "./tsconfig.node.json" }]
}
@@ -55,10 +55,10 @@ const themeToken = {
getNavItem(<a>{$t('API 市场')}</a>, 'serviceHub','/serviceHub',<Icon icon="ic:baseline-hub" width="18" height="18"/>,undefined,undefined,'system.workspace.api_market.view'),
]),
APP_MODE === 'pro' ? getNavItem($t('仪表盘'), 'mainPage', '/dashboard',<Icon icon="ic:baseline-bar-chart" width="18" height="18"/>,[
getNavItem(<a >{$t('运行视图')}</a>, 'dashboard','/dashboard',<ProjectFilled />,undefined,undefined,''),
getNavItem(<a >{$t('系统拓扑图')}</a>, 'systemrunning','/systemrunning',<ProjectFilled />,undefined,undefined,''),
]):null,
getNavItem($t('仪表盘'), 'mainPage', APP_MODE === 'pro' ? '/dashboard' : '/dashboard/total',<Icon icon="ic:baseline-bar-chart" width="18" height="18"/>,[
getNavItem(<a >{$t('运行视图')}</a>, 'dashboard',APP_MODE === 'pro' ? '/dashboard' : '/dashboard/total' ,<ProjectFilled />,undefined,undefined,'system.dashboard.dashboard.view'),
APP_MODE === 'pro' ? getNavItem(<a >{$t('系统拓扑图')}</a>, 'systemrunning','/systemrunning',<ProjectFilled />,undefined,undefined,'system.dashboard.systemrunning.view') : null,
]),
getNavItem($t('系统设置'), 'operationCenter','/member',<Icon icon="ic:baseline-settings" width="18" height="18"/>, [
getNavItem($t('组织'), 'organization','/member',null,[
@@ -70,6 +70,7 @@ const themeToken = {
],undefined,'system.api_market.service_classification.view'),
getNavItem($t('运维与集成'), 'maintenanceCenter','/cluster', null, [
getNavItem(<a>{$t('监控报表')}</a>, 'dashboardsetting','/dashboardsetting',<Icon icon="ic:baseline-monitor-heart" width="18" height="18"/>,undefined,undefined,'system.devops.dashboardsetting.view'),
getNavItem(<a>{$t('集群')}</a>, 'cluster','/cluster',<Icon icon="ic:baseline-device-hub" width="18" height="18"/>,undefined,undefined,'system.devops.cluster.view'),
getNavItem(<a>{$t('证书')}</a>, 'cert','/cert',<Icon icon="ic:baseline-security" width="18" height="18"/>,undefined,undefined,'system.devops.ssl_certificate.view'),
getNavItem(<a>{$t('日志')}</a>, 'logsettings','/logsettings',<Icon icon="ic:baseline-sticky-note-2" width="18" height="18"/>,undefined,undefined,'system.devops.log_configuration.view'),
@@ -79,7 +80,6 @@ const themeToken = {
]),
],[state.language])
useEffect(()=>{console.log(state.language, $t('工作空间'))},[state.language])
useEffect(() => {
if(currentUrl === '/'){
@@ -119,6 +119,7 @@ const themeToken = {
// 初始过滤操作
const res = [...TOTAL_MENU_ITEMS]!.filter(x => x).map((x: any) => (x.routes ? { ...x, routes: filterMenu(x.routes) } : x));
console.log(res)
// 返回处理后的数据
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]);
@@ -196,6 +197,7 @@ const themeToken = {
return document.getElementById('test-pro-layout') || document.body;
}}
>
<ProLayout
prefixCls="apipark-layout"
location={{
@@ -53,7 +53,7 @@ interface PageListProps<T> extends ProTableProps<T, unknown>, RefAttributes<Acti
const PageList = <T extends Record<string, unknown>>(props: React.PropsWithChildren<PageListProps<T>>,ref: React.Ref<ActionType>) => {
const {id,columns,request,dropMenu,searchPlaceholder,showPagination=true,primaryKey='id',addNewBtnTitle,addNewBtnAccess,tableClickAccess,tableClass,onAddNewBtnClick,beforeSearchNode,onSearchWordChange,manualReloadTable,afterNewBtn,dragSortKey,onDragSortEnd,tableTitle,rowSelection,onChange,dataSource,onRowClick,showColSetting=false,minVirtualHeight,noTop,addNewBtnWrapperClass,tableTitleClass,delayLoading = true,besidesTableHeight, noScroll} = props
const {id,columns,request,dropMenu,searchPlaceholder,showPagination=true,primaryKey='id',addNewBtnTitle,addNewBtnAccess,tableClickAccess,tableClass,onAddNewBtnClick,beforeSearchNode,onSearchWordChange,manualReloadTable,afterNewBtn,dragSortKey,onDragSortEnd,tableTitle,rowSelection,onChange,dataSource,onRowClick,showColSetting=false,minVirtualHeight,noTop,addNewBtnWrapperClass = '',tableTitleClass,delayLoading = true,besidesTableHeight, noScroll} = props
const parentRef = useRef<HTMLDivElement>(null);
const [tableHeight, setTableHeight] = useState(minVirtualHeight || window.innerHeight);
const [tableWidth, setTableWidth] = useState<number|undefined>(undefined);
@@ -0,0 +1,18 @@
import JoyRide, { Props } from 'react-joyride'
const Tour = (props: Partial<Props>) => {
return (
<>
<JoyRide
continuous={true}
showSkipButton={false}
showProgress={true}
scrollToFirstStep
scrollOffset={400}
{...props}
/>
</>
)
}
export default Tour
+1 -1
View File
@@ -27,7 +27,7 @@ export const routerKeyMap = new Map<string, string[]|string>([
['operationCenter',['member','user','role','servicecategories']],
['organization',['member','user','role']],
['serviceHubSetting',['servicecategories']],
['maintenanceCenter',['partition','logsettings','resourcesettings','openapi']
['maintenanceCenter',['dashboardsetting','cluster','cert','logsettings','resourcesettings','openapi']
]])
@@ -139,6 +139,11 @@ export const PERMISSION_DEFINITION = [
"anyOf": [{ "backend": ["system.api_market.service_classification.manager"] }]
}
},
"system.devops.dashboardsetting.view":{
"grented":{
"anyOf":[{"backend":[]}]
}
},
"system.devops.cluster.view": {
"granted": {
"anyOf": [{ "backend": ["system.devops.cluster.view"] }]
@@ -224,6 +229,16 @@ export const PERMISSION_DEFINITION = [
"anyOf": [{ "backend": ["system.workspace.api_market.view"] }]
}
},
"system.dashboard.dashboard.view": {
"granted": {
"anyOf": [{ "backend": [] }]
}
},
"system.dashboard.systemrunning.view": {
"granted": {
"anyOf": [{ "backend": [] }]
}
},
"team.service.api.view": {
"granted": {
"anyOf": [{ "backend": ["team.service.api.view"] }]
@@ -17,6 +17,8 @@ export const checkAccess:(access:AccessDataType, accessData:Map<string,string[]>
}
const hasIntersection = (arr1:string[], arr2:string[])=> {
// 当没有对应后端权限字段时,默认有权限
if(arr1.length === 0) return true
const set = new Set(arr1.length > arr2.length ? arr2:arr1)
const arr = arr1.length > arr2.length ? arr1:arr2
for (const item of arr) {
@@ -91,6 +91,9 @@ module.exports = {
'.h-calc-100vh-minus-navbar': {
height: 'calc(100vh - var(--layout-header-height))',
},
'.w-calc-100vw-minus-padding-r': {
width: 'calc(100% - 40px)',
},
}, ['responsive', 'hover']);
}
],
+2
View File
@@ -12,6 +12,7 @@ import 'dayjs/locale/zh-cn';
import dayjs from 'dayjs';
import { useTranslation } from "react-i18next";
import { useGlobalContext } from '@common/contexts/GlobalStateContext';
import Joyride from 'react-joyride';
type Locale = ConfigProviderProps['locale'];
@@ -139,6 +140,7 @@ const antdComponentThemeToken = {
}
}
function App() {
const [locale, setLocal] = useState<Locale>(enUS);
useInitializeMonaco()
@@ -11,8 +11,6 @@ import {useGlobalContext} from "@common/contexts/GlobalStateContext.tsx";
import {FC,lazy} from 'react';
import { TeamProvider } from '@core/contexts/TeamContext.tsx';
import SystemOutlet from '@core/pages/system/SystemOutlet.tsx';
import { DashboardProvider } from '@core/contexts/DashboardContext.tsx';
import { PartitionProvider } from '@core/contexts/PartitionContext.tsx';
import { TenantManagementProvider } from '@market/contexts/TenantManagementContext.tsx';
type RouteConfig = {
@@ -210,6 +208,11 @@ const PUBLIC_ROUTES:RouteConfig[] = [
}
]
},
{
path:'dashboardsetting',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideDashboardSetting.tsx')),
},
{
path:'cluster',
key: uuidv4(),
@@ -343,6 +346,18 @@ const PUBLIC_ROUTES:RouteConfig[] = [
component:<p></p>,
key:uuidv4()
},
{
path:'dashboard',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/Dashboard.tsx')),
key:uuidv4(),
children:[
{
path:'total',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardTotal.tsx')),
},
]
},
{
path:'template/:moduleId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
@@ -403,7 +418,7 @@ const generateRoutes = (routerConfig: RouteConfig[]) => {
const LazyComponent = route.lazy as React.ExoticComponent<unknown>;
routeElement = (
<Suspense fallback={ <div className=''><Skeleton className='m-btnbase w-[calc(100%-20px)]' active /></div>}>
<Suspense fallback={ <div className=''><Skeleton className='m-btnbase w-calc-100vw-minus-padding-r' active /></div>}>
{route.provider ? (
createElement(route.provider, {}, <LazyComponent />)
) : (
+1 -1
View File
@@ -662,7 +662,7 @@ p{
.ant-pro-table-list-toolbar-setting-items{
position:absolute;
top:13px;
top:14px;
right:16px;
z-index:9;
.ant-pro-table-list-toolbar-setting-item{
+68 -46
View File
@@ -1,5 +1,5 @@
import {FC, useCallback, useEffect, useRef, useState} from "react";
import {App, Button, Form, FormInstance, Input} from "antd";
import {App, Button, Divider, Form, FormInstance, Input, Tooltip} from "antd";
import {useGlobalContext} from "@common/contexts/GlobalStateContext.tsx";
import {useFetch} from "@common/hooks/http.ts";
import {BasicResponse, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
@@ -7,6 +7,7 @@ import {useNavigate} from "react-router-dom";
// import {useCrypto} from "../hooks/crypto.ts";
import Logo from '@common/assets/logo.png'
import { $t } from "@common/locales";
import { Icon } from "@iconify/react/dist/iconify.js";
const Login:FC = ()=> {
const {state, dispatch} = useGlobalContext()
@@ -16,16 +17,19 @@ const Login:FC = ()=> {
const formRef = useRef<FormInstance>(null);
const [loading,setLoading] = useState<boolean>()
// const { encryptByEnAES } = useCrypto();
const [allowGuest, setAllowGuest] = useState<boolean>(false)
const check = useCallback(()=>{
fetchData<BasicResponse<{channel:Array<{name:string}>, status:string}>>('account/login',{method:'GET'}).then(response=>{
const {code,data} = response
if(code === STATUS_CODE.SUCCESS && data.status !== 'anonymous'){
dispatch({type:'LOGIN'})
navigate(state.mainPage)
}else{
dispatch({type:'LOGOUT'})
setAllowGuest(data.channel.filter(x=>x.name === 'guest_access').length > 0)
}
})
},[])
@@ -41,47 +45,54 @@ const Login:FC = ()=> {
})
},[])
const fetchLogin = async (values:any)=>{
try {
setLoading(true);
const { username, password } = values;
// const encryptedPassword = encryptByEnAES(username, password);
const body = {
name:username,
password: password
// client: 1,
// type: 1,
// app_type: 4,
};
const {code,msg } = await fetchData<BasicResponse<null>>('account/login/username',{method:'POST',eoBody:(body)})
if (code === STATUS_CODE.SUCCESS) {
dispatch({type:'LOGIN'})
message.success(RESPONSE_TIPS.loginSuccess);
const callbackUrl = new URLSearchParams(window.location.search).get('callbackUrl');
if (callbackUrl && callbackUrl !== 'null') {
navigate(callbackUrl);
} else {
navigate(state.mainPage);
}
}else{
dispatch({type:'LOGOUT'})
message.error(msg)
}
} catch (err) {
console.warn(err);
} finally {
setLoading(false)
}
}
const login = async () => {
if (formRef.current) {
try {
const values = await formRef.current.validateFields();
setLoading(true);
const { username, password } = values;
// const encryptedPassword = encryptByEnAES(username, password);
const body = {
name:username,
password: password
// client: 1,
// type: 1,
// app_type: 4,
};
const {code,msg } = await fetchData<BasicResponse<null>>('account/login/username',{method:'POST',eoBody:(body)})
if (code === STATUS_CODE.SUCCESS) {
dispatch({type:'LOGIN'})
message.success(RESPONSE_TIPS.loginSuccess);
const callbackUrl = new URLSearchParams(window.location.search).get('callbackUrl');
if (callbackUrl && callbackUrl !== 'null') {
navigate(callbackUrl);
} else {
navigate(state.mainPage);
}
}else{
dispatch({type:'LOGOUT'})
message.error(msg)
}
} catch (err) {
console.warn(err);
} finally {
setLoading(false)
}
const values = await formRef.current.validateFields();
fetchLogin(values);
}
};
const loginAsGuest = ()=>{
fetchLogin({username:'guest',password:'12345678'})
}
useEffect(() => {
check()
getSystemInfo()
@@ -133,15 +144,26 @@ const Login:FC = ()=> {
/>
</Form.Item>
<div className=" justify-center">
<Form.Item
className="p-0 bg-transparent rounded border-none mb-0"
>
<Button loading={loading} className="h-[40px] mt-mbase w-full inline-flex justify-center items-center" type="primary" htmlType="submit">
{$t('登录')}
</Button>
</Form.Item>
</div>
<Form.Item
className="p-0 bg-transparent rounded border-none "
>
<Button loading={loading} className="h-[40px] mt-mbase w-full inline-flex justify-center items-center" type="primary" htmlType="submit">
{$t('登录')}
</Button>
</Form.Item>
{
allowGuest && <>
<Divider />
<Form.Item
className="p-0 bg-transparent rounded border-none mb-0"
>
<Button loading={loading} className="h-[40px] w-full inline-flex justify-center items-center" type="default" onClick={loginAsGuest}>
{$t('访客模式')} <Tooltip title={$t('您可通过访客模式查看所有页面和功能,但是无法编辑数据。访客模式仅用于了解产品功能,您可以在正式产品中关闭该功能。')}><Icon icon="ic:baseline-help" height={18} width={18} /></Tooltip>
</Button>
</Form.Item>
</>
}
</Form>
</div>
</div>
@@ -59,7 +59,7 @@ const LogSettings = ()=>{
return (
<>
<Skeleton className='m-btnbase w-[calc(100%-20px)]' active loading={loading}>
<Skeleton className='m-btnbase w-calc-100vw-minus-padding-r' active loading={loading}>
<InsidePage
pageTitle={$t('日志配置')}
description={$t("APIPark 提供详尽的 API 调用日志,帮助企业监控、分析和审计 API 的运行状况。")}
@@ -0,0 +1,106 @@
import { App, Form, Input, Select, Button } from "antd";
import { useEffect } from "react";
import { PartitionDashboardConfigFieldType } from "../../const/partitions/types";
import { DASHBOARD_SETTING_DRIVER_OPTION_LIST } from "../../const/partitions/const";
import WithPermission from "@common/components/aoplatform/WithPermission";
import { BasicResponse, PLACEHOLDER, STATUS_CODE, VALIDATE_MESSAGE } from "@common/const/const";
import { useFetch } from "@common/hooks/http";
export type DashboardPageShowStatus = 'view'|'edit'
export type DashboardSettingEditHandle = {
save:()=>void
}
export type DashboardSettingEditProps = {
changeStatus:(status:DashboardPageShowStatus)=>void
refreshData:()=>void
data?:PartitionDashboardConfigFieldType
}
const DashboardSettingEdit = (props:DashboardSettingEditProps)=>{
const {changeStatus,refreshData,data} = props
const { message } = App.useApp()
const [ form ] = Form.useForm();
const { fetchData} = useFetch()
const onFinish = () => {
form.validateFields().then((value)=>{
fetchData<BasicResponse<{info: PartitionDashboardConfigFieldType}>>('monitor/config',{method: 'POST',body:JSON.stringify(value),eoParams:{}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功,即将刷新页面')
refreshData?.()
}else{
message.error(msg || '操作失败')
}
})
})
}
useEffect(()=>{form.setFieldsValue(data)},[data])
useEffect(() => {
return (form.setFieldsValue({}))
}, []);
return (
<>
<div className="overflow-auto h-full">
<WithPermission access={''} >
<Form
form={form}
className="mx-auto flex flex-col justify-between h-full"
labelCol={{ span: 7 }}
wrapperCol={{ span: 17}}
onFinish={onFinish}
autoComplete="off"
>
<Form.Item<PartitionDashboardConfigFieldType>
label="数据源类型"
name="driver"
rules={[{ required: true, message: VALIDATE_MESSAGE.required }]}
>
<Select className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.select} options={[...DASHBOARD_SETTING_DRIVER_OPTION_LIST]}/>
</Form.Item>
<Form.Item<PartitionDashboardConfigFieldType>
label="数据源地址"
name={['config','addr']}
rules={[{ required: true, message: VALIDATE_MESSAGE.required }]}
>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
<Form.Item<PartitionDashboardConfigFieldType>
label="Organization"
name={['config','org']}
rules={[{ required: true, message: VALIDATE_MESSAGE.required }]}
>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
<Form.Item<PartitionDashboardConfigFieldType>
label="鉴权 Token"
name={['config','token']}
>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
<div className="flex gap-btnbase">
<WithPermission access=''>
<Button type="primary" htmlType="submit">
</Button>
</WithPermission>
<Button type="default" onClick={()=>changeStatus('view')}>
</Button>
</div>
</Form>
</WithPermission>
</div>
</>
)
}
export default DashboardSettingEdit
@@ -1,7 +1,7 @@
import { FC, useEffect, useRef, useState} from "react";
import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx";
import {App, Button, Card, Col, Row, Spin, Tag} from "antd";
import {BasicResponse, STATUS_CODE} from "@common/const/const.tsx";
import {BasicResponse, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts";
import { ClusterPageShowStatus, NodeModalHandle, PartitionClusterNodeTableListItem } from "../../const/partitions/types.ts";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
@@ -12,7 +12,7 @@ import { $t } from "@common/locales/index.ts";
const PartitionInsideCluster:FC = ()=> {
const {setBreadcrumb} = useBreadcrumb()
const {modal, message} = App.useApp()
const { message} = App.useApp()
const {fetchData} = useFetch()
const [nodeData, setNodeData] = useState<PartitionClusterNodeTableListItem>()
const [loading, setLoading] = useState<boolean>(false)
@@ -65,7 +65,7 @@ const PartitionInsideCluster:FC = ()=> {
<div className="h-full overflow-auto">
<Card
classNames={{
body: 'overflow-auto',
body: `overflow-auto ${!nodeData && showStatus === 'view' ? 'hidden': ''}`,
}}
className="overflow-hidden w-full max-h-full flex flex-col justify-between"
title={<div><span className="text-MAIN_TEXT my-btnybase mr-btnbase" > APIPark Node</span>
@@ -0,0 +1,93 @@
import { FC, useEffect, useState} from "react";
import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx";
import {App, Button, Card, Col, Row, Spin, Tag} from "antd";
import {BasicResponse, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { LoadingOutlined } from "@ant-design/icons";
import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
import { $t } from "@common/locales/index.ts";
import DashboardSettingEdit, { DashboardPageShowStatus } from "./DashboardSettingEdit.tsx";
import { PartitionDashboardConfigFieldType } from "@core/const/partitions/types.ts";
const PartitionInsideDashboardSetting:FC = ()=> {
const {setBreadcrumb} = useBreadcrumb()
const {message} = App.useApp()
const {fetchData} = useFetch()
const [data, setData] = useState<PartitionDashboardConfigFieldType>()
const [loading, setLoading] = useState<boolean>(false)
const [showStatus, setShowStatus] = useState<DashboardPageShowStatus>('view')
const getDashboardSettingInfo = () => {
setLoading(true)
return fetchData<BasicResponse<{ nodes:PartitionDashboardConfigFieldType[] }>>('monitor/config', {method: 'GET',eoTransformKeys:[]}).then(response => {
const {code, data, msg} = response
if (code === STATUS_CODE.SUCCESS) {
data?.info?.driver && setData(data.info)
setShowStatus('view')
} else {
message.error(msg || RESPONSE_TIPS.error)
}
}).catch(() => {
return {data: [], success: false}
}).finally(()=>{
setLoading(false)
})
}
useEffect(() => {
setBreadcrumb([
{title: $t('监控报表')}
])
getDashboardSettingInfo()
}, []);
const setDashboardSettingBtn = ()=>{
return (<>
{showStatus === 'view' && <WithPermission access="" key="changeClusterConfig">
<Button type="primary" onClick={() => setShowStatus('edit')}>{$t('修改配置')}</Button>
</WithPermission> }</>
)
}
return (
<>
<InsidePage
pageTitle={$t('监控报表')}
description={$t("设置监控报表的数据来源,设置完成之后即可获得详细的API调用统计图表。")}
showBorder={false}
scrollPage={true}
>
<div className="flex flex-col h-full overflow-auto pb-PAGE_INSIDE_B pr-PAGE_INSIDE_X">
<Spin wrapperClassName=" h-full flex-1" indicator={<LoadingOutlined style={{ fontSize: 24 }} spin/>} spinning={loading}>
<div className="h-full overflow-auto">
<Card
classNames={{
body: `overflow-auto ${(!data || !data?.driver) && showStatus === 'view' ? 'hidden': ''}`,
}}
className="overflow-hidden w-full max-h-full flex flex-col justify-between"
title={<div><span className="text-MAIN_TEXT my-btnybase mr-btnbase" > {$t('统计图表')}</span>
{!loading && !data?.driver && <Tag color='#f50'>{ $t('未配置')}
</Tag>}</div>}
extra={setDashboardSettingBtn()}>
{showStatus === 'view'&& data && data.driver && DashboardConfigPreview(data) }
{showStatus !== 'view' && <DashboardSettingEdit data={data} changeStatus={setShowStatus} refreshData={getDashboardSettingInfo} />}
</Card>
</div>
</Spin>
</div>
</InsidePage>
</>
)
}
export function DashboardConfigPreview (x:PartitionDashboardConfigFieldType){
return <div className="flex flex-col gap-[4px] ">
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('数据源')}</Col><Col>{x?.driver}</Col></Row>
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('地址(IP:端口)')}</Col><Col>{x?.config?.addr}</Col></Row>
<Row className=""><Col className="font-bold text-right pr-[4px]">{$t('组织(Organization')}</Col><Col>{x?.config?.org}</Col></Row>
</div>}
export default PartitionInsideDashboardSetting
@@ -58,7 +58,7 @@ const LogSettings = ()=>{
return (
<>
<Skeleton className='m-btnbase w-[calc(100%-20px)]' active loading={loading}>
<Skeleton className='m-btnbase w-calc-100vw-minus-padding-r' active loading={loading}>
<InsidePage
pageTitle={$t('资源配置')}
>
@@ -1,4 +1,5 @@
import PageList from "@common/components/aoplatform/PageList.tsx"
import Tour from "@common/components/aoplatform/Tour.tsx"
import {ActionType} from "@ant-design/pro-components";
import {FC, useEffect, useMemo, useRef, useState} from "react";
import {useNavigate} from "react-router-dom";
@@ -13,6 +14,7 @@ import { DrawerWithFooter } from "@common/components/aoplatform/DrawerWithFooter
import SystemConfig from "./SystemConfig.tsx";
import { useGlobalContext } from "@common/contexts/GlobalStateContext.tsx";
import { $t } from "@common/locales/index.ts";
import Joyride from "react-joyride";
const SystemList:FC = ()=>{
const navigate = useNavigate();
@@ -114,15 +116,28 @@ const SystemList:FC = ()=>{
return res
},[memberValueEnum,teamList])
const steps = [
{
target: '.my-first-step',
content: '点击按钮新建服务',
},
{
target: '.ant-table-tbody',
content: '点击表格查看详情',
placement: 'top'
},
];
return (
<div className="h-full w-full pr-PAGE_INSIDE_X pb-PAGE_INSIDE_B">
<Joyride steps={steps} run={true} />
<PageList
id="global_system"
ref={pageListRef}
columns={[...columns]}
request={()=>getSystemList()}
addNewBtnTitle={$t("添加服务")}
addNewBtnWrapperClass={'my-first-step'}
searchPlaceholder={$t("输入名称、ID、所属团队、负责人查找服务")}
onAddNewBtnClick={() => {
setOpen(true)
@@ -140,7 +155,6 @@ const SystemList:FC = ()=>{
<SystemConfig ref={drawerFormRef} />
</DrawerWithFooter>
</div>
// </Skeleton>
)
}
+1
View File
@@ -23,6 +23,7 @@
"@core/*": ["./src/*"],
"@common/*": ["../common/src/*"],
"@market/*": ["../market/src/*"],
"@dashboard/*": ["../dashboard/src/*"],
},
},
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../common/src/components/aoplatform/ScrollableSection.tsx", "../common/src/utils/postcat.tsx", "../common/src/utils/curl.ts", "../common/src/components/aoplatform/ResetPsw.tsx", "../common/src/components/aoplatform/SubscribeApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePageForHub.tsx", "src/components/aoplatform/RenderRoutes.tsx", "../common/src/components/aoplatform/PublishApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePage.tsx", "../common/src/const/type.ts", "../common/src/components/aoplatform/intelligent-plugin", "../common/src/const/domain"],
+1
View File
@@ -57,6 +57,7 @@ export default defineConfig({
{ find: '@common', replacement: path.resolve(__dirname, '../common/src') },
{ find: '@market', replacement: path.resolve(__dirname, '../market/src') },
{ find: '@core', replacement: path.resolve(__dirname, './src') },
{ find: '@dashboard', replacement: path.resolve(__dirname, '../dashboard/src') },
]
},
server: {
@@ -7,7 +7,7 @@ import { getTime } from "../utils/dashboard";
import ScrollableSection from "@common/components/aoplatform/ScrollableSection";
import TimeRangeSelector, { RangeValue, TimeRange, TimeRangeButton } from "@common/components/aoplatform/TimeRangeSelector";
import MonitorTable, { MonitorTableHandler } from "./MonitorTable";
import { BasicResponse, STATUS_CODE } from "@common/const/const";
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
import { DefaultOptionType } from "antd/es/select";
import { useParams } from "react-router-dom";
import { RouterParams } from "@core/components/aoplatform/RenderRoutes";
@@ -15,6 +15,7 @@ import { useExcelExport } from "@common/hooks/excel";
import { API_TABLE_GLOBAL_COLUMNS_CONFIG } from "@dashboard/const/const";
import { useFetch } from "@common/hooks/http";
import { EntityItem } from "@common/const/type";
import { $t } from "@common/locales";
export type MonitorApiPageProps = {
fetchTableData:(body:SearchBody)=>Promise<BasicResponse<{statistics:MonitorApiData[]}>>
detailDrawerContent:React.ReactNode
@@ -55,7 +56,7 @@ export default function MonitorApiPage(props:MonitorApiPageProps){
if(code === STATUS_CODE.SUCCESS){
setApiOptionList(data.apis?.map((x:EntityItem)=>({label:x.name, value:x.id})))
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
return setApiOptionList([])
}
}).catch(() => {
@@ -69,7 +70,7 @@ export default function MonitorApiPage(props:MonitorApiPageProps){
if(code === STATUS_CODE.SUCCESS){
setProjectOptionList(data.projects?.map((x:EntityItem)=>({label:x.name, value:x.id})))
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
return setProjectOptionList([])
}
}).catch(() => {
@@ -110,9 +111,9 @@ export default function MonitorApiPage(props:MonitorApiPageProps){
fetchTableData(data).then((resp) => {
const {code,data,msg} = resp
if(code === STATUS_CODE.SUCCESS){
exportExcel('API调用统计', [query!.start!, query!.end!], 'API调用统计', 'dashboard_api', API_TABLE_GLOBAL_COLUMNS_CONFIG, data.statistics)
exportExcel($t('API调用统计'), [query!.start!, query!.end!], $t('API调用统计'), 'dashboard_api', API_TABLE_GLOBAL_COLUMNS_CONFIG, data.statistics)
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
}
})
};
@@ -134,7 +135,7 @@ export default function MonitorApiPage(props:MonitorApiPageProps){
if(code === STATUS_CODE.SUCCESS){
return {data:data.statistics?.map((x:MonitorApiData)=>{x.proxyRate = Number((x.proxyRate*100).toFixed(2));x.requestRate = Number((x.requestRate*100).toFixed(2));return x}), success: true}
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
return {data:[], success:false}
}
}).catch(() => {
@@ -160,7 +161,7 @@ export default function MonitorApiPage(props:MonitorApiPageProps){
initialDatePickerValue={datePickerValue}
onTimeRangeChange={handleTimeRangeChange}/>
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
<label className=" whitespace-nowrap inline-block"></label>
<label className=" whitespace-nowrap inline-block">{$t('服务')}</label>
<Select
className="w-[346px]"
value={queryData?.projects}
@@ -168,7 +169,7 @@ export default function MonitorApiPage(props:MonitorApiPageProps){
mode="multiple"
allowClear
maxCount={3}
placeholder="选择服务"
placeholder={$t("选择服务")}
onChange={(value)=>{setQueryData(prevData=>({...prevData || {}, projects:value}));getApiList(value)}}
/>
</div>
@@ -181,22 +182,22 @@ export default function MonitorApiPage(props:MonitorApiPageProps){
mode="multiple"
allowClear
maxCount={3}
placeholder="选择API"
placeholder={$t("选择API")}
onChange={(value)=>{setQueryData(prevData=>({...prevData || {}, apis:value}))}}
/>
<label className="ml-btnybase whitespace-nowrap"></label>
<label className="ml-btnybase whitespace-nowrap">{$t('路径')}</label>
<div className="w-[346px] inline-block">
{/* <SearchInputGroup eoSingle={false} eoInputVal={queryData.path} eoClick={() => setQueryData({ ...queryData, path: '' })} /> */}
<Input value={queryData?.path} onChange={(e) => debounce((e)=>{setQueryData(prevData=>({...prevData || {}, path:e.target.value}))}, 100)(e)} allowClear placeholder='请输入请求路径进行搜索' prefix={<SearchOutlined className="cursor-pointer"/>}/>
<Input value={queryData?.path} onChange={(e) => debounce((e)=>{setQueryData(prevData=>({...prevData || {}, path:e.target.value}))}, 100)(e)} allowClear placeholder={$t('请输入请求路径进行搜索')} prefix={<SearchOutlined className="cursor-pointer"/>}/>
</div>
<Button className="ml-btnybase" onClick={clearSearch}>
{$t('重置')}
</Button>
<Button type="primary" loading={queryBtnLoading} className="ml-btnybase" onClick={()=>{setQueryBtnLoading(true);getApiTableList()}}>
{$t('查询')}
</Button>
<Button className="ml-btnybase" loading={exportLoading} onClick={exportData}>
{$t('导出')}
</Button>
</div>
</div>
@@ -212,9 +213,9 @@ export default function MonitorApiPage(props:MonitorApiPageProps){
mask={!fullScreen}
title={<>
{fullScreen && <a className="mr-btnrbase text-[14px]" onClick={()=>{setFullScreen?.(false)}}>
<CloseOutlined className="mr-[4px]"/>退
<CloseOutlined className="mr-[4px]"/>{$t('退出全屏')}
</a>}
<span className="mr-btnrbase">{detailEntityName}</span>
<span className="mr-btnrbase">{$t('(0)调用详情',[detailEntityName])}</span>
{!fullScreen && <ExpandOutlined className="text-MAIN_TEXT hover:text-MAIN_HOVER_TEXT" onClick={()=>{setFullScreen?.(true)}}/>}
</>}
width={fullScreen ? '100%' : '60%'}
@@ -5,13 +5,14 @@ import { EntityItem } from "@common/const/type";
import TimeRangeSelector, { RangeValue, TimeRange, TimeRangeButton } from "@common/components/aoplatform/TimeRangeSelector";
import MonitorTable, { MonitorTableHandler } from "./MonitorTable";
import { DefaultOptionType } from "antd/es/select";
import { BasicResponse, STATUS_CODE } from "@common/const/const";
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
import { getTime } from "../utils/dashboard";
import { useExcelExport } from "@common/hooks/excel";
import { APPLICATION_TABLE_GLOBAL_COLUMNS_CONFIG } from "@dashboard/const/const";
import { CloseOutlined, ExpandOutlined } from "@ant-design/icons";
import { useFetch } from "@common/hooks/http";
import { MonitorSubQueryData } from "./MonitorSubPage";
import { $t } from "@common/locales";
export type MonitorAppPageProps = {
fetchTableData:(body:SearchBody)=>Promise<BasicResponse<{statistics:MonitorSubscriberData[]}>>
@@ -60,7 +61,7 @@ export default function MonitorAppPage(props:MonitorAppPageProps){
if(code === STATUS_CODE.SUCCESS){
setListOfApps(data.projects?.map((x:EntityItem)=>({label:x.name, value:x.id})))
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
return setListOfApps([])
}
}).catch(() => {
@@ -98,9 +99,9 @@ export default function MonitorAppPage(props:MonitorAppPageProps){
fetchTableData(data).then((resp) => {
const {code,data,msg} = resp
if(code === STATUS_CODE.SUCCESS){
exportExcel('应用调用统计', [query!.start!, query!.end!], '应用调用统计', 'dashboard_application', APPLICATION_TABLE_GLOBAL_COLUMNS_CONFIG, data.statistics)
exportExcel($t('应用调用统计'), [query!.start!, query!.end!], $t('应用调用统计'), 'dashboard_application', APPLICATION_TABLE_GLOBAL_COLUMNS_CONFIG, data.statistics)
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
}
})
};
@@ -117,7 +118,7 @@ export default function MonitorAppPage(props:MonitorAppPageProps){
if(code === STATUS_CODE.SUCCESS){
return {data:data.statistics?.map((x:MonitorSubscriberData)=>{x.proxyRate = Number((x.proxyRate*100).toFixed(2));x.requestRate = Number((x.requestRate*100).toFixed(2));return x}), success: true}
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
return {data:[], success:false}
}
}).catch(() => {
@@ -143,13 +144,13 @@ export default function MonitorAppPage(props:MonitorAppPageProps){
onTimeRangeChange={handleTimeRangeChange}/>
<div className="flex flex-wrap items-center row-gap-[12px] pt-btnybase mr-btnybase">
<div>
<label className="inline-block whitespace-nowrap"></label>
<label className="inline-block whitespace-nowrap">{$t('应用')}</label>
<Select
className="w-[346px]"
mode="multiple"
maxTagCount={1}
// maxTagPlaceholder={(selectedList) => `and ${selectedList.length} more selected`}
placeholder="请选择"
placeholder={$t("请选择应用")}
value={queryData?.projects}
options={listOfApps}
onChange={(value)=>{setQueryData(prevData=>({...prevData || {}, projects:value}))}}
@@ -157,13 +158,13 @@ export default function MonitorAppPage(props:MonitorAppPageProps){
</div>
<div>
<Button className="ml-btnybase" onClick={clearSearch}>
{$t('重置')}
</Button>
<Button type="primary" loading={queryBtnLoading} className="ml-btnybase" onClick={()=>{setQueryBtnLoading(true);getAppTableList()}}>
{$t('查询')}
</Button>
<Button className="ml-btnybase" loading={exportLoading} onClick={exportData}>
{$t('导出')}
</Button>
</div>
</div>
@@ -3,13 +3,14 @@ import { useState, useEffect, useRef } from "react";
import { InvokeData, MonitorApiData, MonitorSubscriberData, SearchBody } from "@dashboard/const/type";
import TimeRangeSelector, { RangeValue, TimeRange, TimeRangeButton } from "@common/components/aoplatform/TimeRangeSelector";
import MonitorLineGraph from "./MonitorLineGraph";
import { BasicResponse, STATUS_CODE } from "@common/const/const";
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
import { getTime, getTimeUnit } from "../utils/dashboard";
import MonitorTable, { MonitorTableHandler } from "./MonitorTable";
import { DashboardDetailInvokeType } from "@dashboard/pages/DashboardDetail";
import { MonitorApiQueryData } from "./MonitorApiPage";
import { MonitorSubQueryData } from "./MonitorSubPage";
import dayjs from "dayjs";
import { $t } from "@common/locales";
type MonitorDetailPageProps = {
fetchInvokeData:(body:SearchBody)=>Promise<BasicResponse<DashboardDetailInvokeType>>
@@ -38,7 +39,7 @@ export default function MonitorDetailPage(props:MonitorDetailPageProps){
const [timeUnit, setTimeUnit] = useState<string>()
const [invokeStaticError,setInvokeStaticError] = useState<boolean>(false)
const monitorTableRef = useRef<MonitorTableHandler>(null)
const [modalTitle, setModalTitle] = useState<string>('调用趋势')
const [modalTitle, setModalTitle] = useState<string>($t('调用趋势'))
const [queryBtnLoading, setQueryBtnLoading] = useState<boolean>(false)
useEffect(() => {
@@ -71,7 +72,7 @@ export default function MonitorDetailPage(props:MonitorDetailPageProps){
// this.invokeLineRef?.changeLineChart()
}else{
setInvokeStaticError(true)
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
}
}).catch(()=>{setQueryBtnLoading(false)})
};
@@ -83,7 +84,7 @@ export default function MonitorDetailPage(props:MonitorDetailPageProps){
if(code === STATUS_CODE.SUCCESS){
return {data:data.statistics?.map((x:(MonitorApiData|MonitorSubscriberData))=>{x.proxyRate = Number((x.proxyRate*100).toFixed(2));x.requestRate = Number((x.requestRate*100).toFixed(2));return x}), success: true}
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
return {data:[], success:false}
}
}).catch(() => {
@@ -108,18 +109,13 @@ export default function MonitorDetailPage(props:MonitorDetailPageProps){
setDetailInvokeStatic(tendency)
setDetailInvokeError(false)
setTimeUnit(getTimeUnit(timeInterval!))
// this.invokeLineRef?.changeLineChart()
setModalTitle(`${entity.name}-${detailName}调用趋势`);
setModalTitle($t('(0)-(1)调用趋势', [entity.name, detailName]))
setModalVisible(true);
}else{
setInvokeStaticError(true)
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
}
})
// setModalTitle(`${entity.name}-${detailName}调用趋势`);
// setModalVisible(true);
};
const handleCloseModal = () => {
@@ -144,10 +140,10 @@ export default function MonitorDetailPage(props:MonitorDetailPageProps){
onTimeRangeChange={handleTimeRangeChange}
hideTitle={!fullScreen}/>
<Button className="ml-btnybase mt-btnybase" onClick={clearSearch}>
{$t('重置')}
</Button>
<Button className="ant-btn-primary ml-btnybase mt-btnybase" loading={queryBtnLoading} onClick={()=>{setQueryBtnLoading(true);getMonitorData();}}>
{$t('查询')}
</Button>
</div>
</div>
@@ -156,7 +152,7 @@ export default function MonitorDetailPage(props:MonitorDetailPageProps){
{/* 这里应该添加图表组件 */}
{invokeStaticError ? <Empty className="mt-[20%]" image={Empty.PRESENTED_IMAGE_SIMPLE}/>: <MonitorLineGraph
lineData={invokeStatic}
titles={['调用量统计']}
titles={[$t('调用量统计')]}
yAxisTitle={timeUnit || '-'}
type="invoke"
/>}
@@ -175,11 +171,11 @@ export default function MonitorDetailPage(props:MonitorDetailPageProps){
maskClosable={false}
>
<div className=" pb-btnybase flex flex-nowrap flex-col w-full items-center justify-between">
<div className="w-full flex flex-row-reverse"><Checkbox checked={compareTotal} onChange={(e) => {setCompareTotal(e.target.checked)}}></Checkbox></div>
<div className="w-full flex flex-row-reverse"><Checkbox checked={compareTotal} onChange={(e) => {setCompareTotal(e.target.checked)}}>{$t('加入总体数据对比')}</Checkbox></div>
{(detailInvokeError||!modalVisible) ? <Empty className="w-[calc(100%-20px)]" image={Empty.PRESENTED_IMAGE_SIMPLE}/>: <MonitorLineGraph
className="w-[calc(100%-22px)] w-min-[300px]"
lineData={detailInvokeStatic!}
titles={['调用量统计']}
titles={[$t('调用量统计')]}
yAxisTitle={timeUnit || '-'}
type="invoke"
/>}
@@ -188,7 +184,7 @@ export default function MonitorDetailPage(props:MonitorDetailPageProps){
(invokeStaticError ||!modalVisible ) ? <Empty className="w-[calc(100%-20px)]" image={Empty.PRESENTED_IMAGE_SIMPLE}/>: <MonitorLineGraph
className="w-[calc(100%-20px)]"
lineData={invokeStatic}
titles={['调用量统计']}
titles={[$t('调用量统计')]}
yAxisTitle={timeUnit || '-'}
type="invoke"
/>
@@ -4,6 +4,7 @@ import ECharts, { EChartsOption } from 'echarts-for-react';
import { InvokeData, LineGraphType, MessageData } from '@dashboard/const/type';
import { MONITOR_LINE_CHART_BASIC_INVOKE_SELECTED, MONITOR_LINE_CHART_BASIC_MESSAGE_SELECTED, MONITOR_LINE_CHART_OPTION_CONFIG, MONITOR_NAME_MAP } from '@dashboard/const/const';
import { yUnitFormatter } from '../utils/dashboard';
import { $t } from '@common/locales';
type LineGraphProps = {
className?:string
@@ -133,7 +134,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
},
yAxis: [{
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用量` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用量',[yAxisTitle]) : '',
nameLocation: 'end',
nameTextStyle: {
align: 'left'
@@ -148,7 +149,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
},
{
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用成功率` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用成功率',[yAxisTitle]) : '',
position: 'right',
min: 0,
max: 100,
@@ -165,12 +166,12 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
}
}],
series: [
{ type: 'line', symbol: 'none', name: '请求总数', data: (lineData as InvokeData)?.requestTotal, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: '请求成功率', data: (lineData as InvokeData)?.requestRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], yAxisIndex: 1 },
{ type: 'line', symbol: 'none', name: '转发总数', data: (lineData as InvokeData)?.proxyTotal, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: '转发成功率', data: (lineData as InvokeData)?.proxyRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], yAxisIndex: 1 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: '状态码4xx数', data: (lineData as InvokeData)?.status_4xx, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: '状态码5xx数', data: (lineData as InvokeData)?.status_5xx, yAxisIndex: 0 }
{ type: 'line', symbol: 'none', name: $t('请求总数'), data: (lineData as InvokeData)?.requestTotal, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: $t('请求成功率'), data: (lineData as InvokeData)?.requestRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], yAxisIndex: 1 },
{ type: 'line', symbol: 'none', name: $t('转发总数'), data: (lineData as InvokeData)?.proxyTotal, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: $t('转发成功率'), data: (lineData as InvokeData)?.proxyRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], yAxisIndex: 1 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: $t('状态码4xx数'), data: (lineData as InvokeData)?.status_4xx, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: $t('状态码5xx数'), data: (lineData as InvokeData)?.status_5xx, yAxisIndex: 0 }
]
})
@@ -185,11 +186,11 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
// echarts会根据你定义的颜色返回一个生成好的带颜色的标记,直接实用即可
let str = ''
if (i === Math.floor(params.length / 2)) {
str = '<br/><div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + (params[0].seriesIndex === 0 ? dataTitle + '调用总体趋势' :modalTitle) + '</span>&nbsp&nbsp&nbsp<span>' + params[0].axisValue + '</span></div><div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + item.marker
str = '<br/><div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + (params[0].seriesIndex === 0 ? dataTitle + $t('调用总体趋势') :modalTitle) + '</span>&nbsp&nbsp&nbsp<span>' + params[0].axisValue + '</span></div><div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + item.marker
} else {
str = '<div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + item.marker
}
if (item.seriesName === '请求成功率' || item.seriesName === '转发成功率') {
if (item.seriesName === $t('请求成功率') || item.seriesName === $t('转发成功率')) {
str += (item.seriesName + '&nbsp&nbsp&nbsp </span><span style="font-weight:bold"> ' + item.value + '% </span></section></div>')
} else {
str += (item.seriesName + '&nbsp&nbsp&nbsp </span><span style="font-weight:bold"> ' + item.value + '</span></section></div>')
@@ -258,7 +259,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
yAxis: [
{
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用量` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用量',[yAxisTitle]) : '',
nameLocation: 'end',
nameTextStyle: {
align: 'left'
@@ -274,7 +275,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
},
{
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用成功率` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用成功率',[yAxisTitle]) : '',
position: 'right',
min: 0,
max: 100,
@@ -294,7 +295,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
{
gridIndex: 1,
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用量` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用量',[yAxisTitle]) : '',
nameLocation: 'end',
nameTextStyle: {
align: 'left'
@@ -311,7 +312,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
{
gridIndex: 1,
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用成功率` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用成功率',[yAxisTitle]): '',
position: 'right',
min: 0,
max: 100,
@@ -329,18 +330,18 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
}
}],
series: [
{ type: 'line', symbol: 'none', name: '请求总数', data: (lineData as InvokeData)?.requestTotal, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: '请求成功率', data: (lineData as InvokeData)?.requestRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], xAxisIndex: 0, yAxisIndex: 1 },
{ type: 'line', symbol: 'none', name: '转发总数', data: (lineData as InvokeData)?.proxyTotal, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: '转发成功率', data: (lineData as InvokeData)?.proxyRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], xAxisIndex: 0, yAxisIndex: 1 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: '状态码4xx数', data: (lineData as InvokeData)?.status_4xx, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: '状态码5xx数', data: (lineData as InvokeData)?.status_5xx, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: '请求总数', data: (compareData as InvokeData)?.requestTotal, xAxisIndex: 1, yAxisIndex: 2 },
{ type: 'line', symbol: 'none', name: '请求成功率', data: (compareData as InvokeData)?.requestRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], xAxisIndex: 1, yAxisIndex: 3 },
{ type: 'line', symbol: 'none', name: '转发总数', data: (compareData as InvokeData)?.proxyTotal, xAxisIndex: 1, yAxisIndex: 2 },
{ type: 'line', symbol: 'none', name: '转发成功率', data: (compareData as InvokeData)?.proxyRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], xAxisIndex: 1, yAxisIndex: 3 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: '状态码4xx数', data: (compareData as InvokeData)?.status_4xx, xAxisIndex: 1, yAxisIndex: 2 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: '状态码5xx数', data: (compareData as InvokeData)?.status_5xx, xAxisIndex: 1, yAxisIndex: 2 }
{ type: 'line', symbol: 'none', name: $t('请求总数'), data: (lineData as InvokeData)?.requestTotal, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: $t('请求成功率'), data: (lineData as InvokeData)?.requestRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], xAxisIndex: 0, yAxisIndex: 1 },
{ type: 'line', symbol: 'none', name: $t('转发总数'), data: (lineData as InvokeData)?.proxyTotal, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: $t('转发成功率'), data: (lineData as InvokeData)?.proxyRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], xAxisIndex: 0, yAxisIndex: 1 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: $t('状态码4xx数'), data: (lineData as InvokeData)?.status_4xx, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: $t('状态码5xx数'), data: (lineData as InvokeData)?.status_5xx, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: $t('请求总数'), data: (compareData as InvokeData)?.requestTotal, xAxisIndex: 1, yAxisIndex: 2 },
{ type: 'line', symbol: 'none', name: $t('请求成功率'), data: (compareData as InvokeData)?.requestRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], xAxisIndex: 1, yAxisIndex: 3 },
{ type: 'line', symbol: 'none', name: $t('转发总数'), data: (compareData as InvokeData)?.proxyTotal, xAxisIndex: 1, yAxisIndex: 2 },
{ type: 'line', symbol: 'none', name: $t('转发成功率'), data: (compareData as InvokeData)?.proxyRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], xAxisIndex: 1, yAxisIndex: 3 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: $t('状态码4xx数'), data: (compareData as InvokeData)?.status_4xx, xAxisIndex: 1, yAxisIndex: 2 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: $t('状态码5xx数'), data: (compareData as InvokeData)?.status_5xx, xAxisIndex: 1, yAxisIndex: 2 }
]
})
@@ -381,7 +382,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
yAxis: [
{
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用量` : '',
name: (lineData as InvokeData)?.date.length > 0 ?$t('(0)调用量',[yAxisTitle]) : '',
nameLocation: 'end',
nameTextStyle: {
align: 'left'
@@ -398,7 +399,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
},
{
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用成功率` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用成功率',[yAxisTitle]) : '',
position: 'right',
min: 0,
max: 100,
@@ -415,10 +416,10 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
}
}],
series: [
{ type: 'line', symbol: 'none', name: '转发总数', data: (lineData as InvokeData)?.proxyTotal, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: '转发成功率', data: (lineData as InvokeData)?.proxyRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], yAxisIndex: 1 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: '状态码4xx数', data: (lineData as InvokeData)?.status_4xx, yAxisIndex: 0 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: '状态码5xx数', data: (lineData as InvokeData)?.status_5xx, yAxisIndex: 0 }
{ type: 'line', symbol: 'none', name: $t('转发总数'), data: (lineData as InvokeData)?.proxyTotal, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: $t('转发成功率'), data: (lineData as InvokeData)?.proxyRate?.map((x) => Number((Number(x) * 100).toFixed(2))) || [], yAxisIndex: 1 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: $t('状态码4xx数'), data: (lineData as InvokeData)?.status_4xx, yAxisIndex: 0 },
{ type: 'line', lineStyle: { type: 'dashed' }, symbol: 'none', name: $t('状态码5xx数'), data: (lineData as InvokeData)?.status_5xx, yAxisIndex: 0 }
]})
const generateInvokeServiceCompareLineChartOption = ()=>({
@@ -452,18 +453,18 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
tooltip: {
trigger: 'axis',
formatter: (params:Array<Record<string,unknown>>) => {
const startHtml = '<div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + (params[0].seriesIndex === 0 ? modalTitle : dataTitle + '调用总体趋势') + '</span>&nbsp&nbsp&nbsp<span>' + params[0].axisValue + '</span></div>'
const startHtml = '<div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + (params[0].seriesIndex === 0 ? modalTitle : dataTitle + $t('调用总体趋势')) + '</span>&nbsp&nbsp&nbsp<span>' + params[0].axisValue + '</span></div>'
const listArr = []
for (let i = 0; i < params.length; i++) {
const item = params[i]
// echarts会根据你定义的颜色返回一个生成好的带颜色的标记,直接实用即可
let str = ''
if (i === Math.floor(params.length / 2)) {
str = '<br/><div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + (params[0].seriesIndex === 0 ? dataTitle + '调用总体趋势' : modalTitle) + '</span>&nbsp&nbsp&nbsp<span>' + params[0].axisValue + '</span></div><div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + item.marker
str = '<br/><div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + (params[0].seriesIndex === 0 ? dataTitle + $t('调用总体趋势') : modalTitle) + '</span>&nbsp&nbsp&nbsp<span>' + params[0].axisValue + '</span></div><div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + item.marker
} else {
str = '<div><section style="align-items: center;display:flex; justify-content: space-between;flex-wrap: nowrap;"><span> ' + item.marker
}
if (item.seriesName === '请求成功率' || item.seriesName === '转发成功率') {
if (item.seriesName === $t('请求成功率') || item.seriesName === $t('转发成功率')) {
str += (item.seriesName + '&nbsp&nbsp&nbsp </span><span style="font-weight:bold"> ' + item.value + '% </span></section></div>')
} else {
str += (item.seriesName + '&nbsp&nbsp&nbsp </span><span style="font-weight:bold"> ' + item.value + '</span></section></div>')
@@ -513,7 +514,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
yAxis: [
{
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用量` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用量',[yAxisTitle]) : '',
nameLocation: 'end',
nameTextStyle: {
align: 'left'
@@ -529,7 +530,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
},
{
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用成功率` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用成功率',[yAxisTitle]) : '',
position: 'right',
min: 0,
max: 100,
@@ -549,7 +550,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
{
gridIndex: 1,
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用量` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用量',[yAxisTitle]): '',
nameLocation: 'end',
nameTextStyle: {
align: 'left'
@@ -566,7 +567,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
{
gridIndex: 1,
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用成功率` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用成功率',[yAxisTitle]) : '',
position: 'right',
min: 0,
max: 100,
@@ -584,14 +585,14 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
}
}],
series: [
{ type: 'line', symbol: 'none', name: '转发总数', data: (lineData as InvokeData)?.proxyTotal, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: '转发成功率', data: (lineData as InvokeData)?.proxyRate, xAxisIndex: 0, yAxisIndex: 1 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: '状态码4xx数', data: (lineData as InvokeData)?.status_4xx, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: '状态码5xx数', data: (lineData as InvokeData)?.status_5xx, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: '转发总数', data: (compareData as InvokeData)?.proxyTotal, xAxisIndex: 1, yAxisIndex: 2 },
{ type: 'line', symbol: 'none', name: '转发成功率', data: (compareData as InvokeData)?.proxyRate, xAxisIndex: 1, yAxisIndex: 3 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: '状态码4xx数', data: (compareData as InvokeData)?.status_4xx, xAxisIndex: 1, yAxisIndex: 2 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: '状态码5xx数', data: (compareData as InvokeData)?.status_5xx, xAxisIndex: 1, yAxisIndex: 2 }
{ type: 'line', symbol: 'none', name: $t('转发总数'), data: (lineData as InvokeData)?.proxyTotal, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: $t('转发成功率'), data: (lineData as InvokeData)?.proxyRate, xAxisIndex: 0, yAxisIndex: 1 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: $t('状态码4xx数'), data: (lineData as InvokeData)?.status_4xx, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: $t('状态码5xx数'), data: (lineData as InvokeData)?.status_5xx, xAxisIndex: 0, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: $t('转发总数'), data: (compareData as InvokeData)?.proxyTotal, xAxisIndex: 1, yAxisIndex: 2 },
{ type: 'line', symbol: 'none', name: $t('转发成功率'), data: (compareData as InvokeData)?.proxyRate, xAxisIndex: 1, yAxisIndex: 3 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: $t('状态码4xx数'), data: (compareData as InvokeData)?.status_4xx, xAxisIndex: 1, yAxisIndex: 2 },
{ type: 'line', symbol: 'none', lineStyle: { type: 'dashed' }, name: $t('状态码5xx数'), data: (compareData as InvokeData)?.status_5xx, xAxisIndex: 1, yAxisIndex: 2 }
]})
const generateTrafficLineChartOption = ()=>({
@@ -645,7 +646,7 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
},
{
type: 'value',
name: (lineData as InvokeData)?.date.length > 0 ? `${yAxisTitle}调用成功率` : '',
name: (lineData as InvokeData)?.date.length > 0 ? $t('(0)调用成功率',[yAxisTitle]) : '',
position: 'right',
min: 0,
max: 100,
@@ -663,8 +664,8 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
}
}],
series: [
{ type: 'line', symbol: 'none', name: '请求报文量', data: (lineData as MessageData).requestMessage, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: '响应报文量', data: (lineData as MessageData).responseMessage, yAxisIndex: 0 }
{ type: 'line', symbol: 'none', name: $t('请求报文量'), data: (lineData as MessageData).requestMessage, yAxisIndex: 0 },
{ type: 'line', symbol: 'none', name: $t('响应报文量'), data: (lineData as MessageData).responseMessage, yAxisIndex: 0 }
]})
@@ -694,9 +695,9 @@ const MonitorLineGraph: FC<LineGraphProps> = ({ className, lineData, titles, yAx
}
// 当勾选请求成功率或转发成功率其中之一时,显示右侧y轴
if (legendSelected && lineData?.date && lineData?.date.length > 0) {
if (!legendSelected['转发成功率'] && !legendSelected['请求成功率'] && (option.yAxis as Array<unknown>)?.length > 1 && option.yAxis[1].show !== false) {
if (!legendSelected[$t('转发成功率')] && !legendSelected[$t('请求成功率')] && (option.yAxis as Array<unknown>)?.length > 1 && option.yAxis[1].show !== false) {
option.yAxis[1].show = false
} else if ((legendSelected['转发成功率'] || legendSelected['请求成功率']) && (option.yAxis as Array<unknown>)?.length > 1 && option.yAxis[1].show !== true) {
} else if ((legendSelected[$t('转发成功率')] || legendSelected[$t('请求成功率')]) && (option.yAxis as Array<unknown>)?.length > 1 && option.yAxis[1].show !== true) {
option.yAxis[1].show = true
}
}
@@ -1,6 +1,7 @@
import {FC} from 'react';
import ECharts,{EChartsOption} from 'echarts-for-react';
import { changeNumberUnit } from '../utils/dashboard';
import { $t } from '@common/locales';
type PieGraphProps = {
className?:string,
@@ -98,13 +99,13 @@ const MonitorPieGraph: FC<PieGraphProps> = ({ className,title, pieData, labelNam
<ul className="list-none truncate my-0 ps-[10px]">
<li className="h-[18px]"></li>
<li className="text-[#999999] mt-[16px]">
4XX数
{$t('状态码4XX数')}
<span className="text-[#999999] inline-block w-[50px] ml-[10px] text-right">
{changeNumberUnit(status4xxCount)}
</span>
</li>
<li className="text-[#999999] mt-[18px]">
5XX数
{$t('状态码5XX数')}
<span className="text-[#999999] inline-block w-[50px] ml-[10px] text-right">
{changeNumberUnit(status5xxCount)}
</span>
@@ -5,7 +5,7 @@ import { EntityItem } from "@common/const/type";
import TimeRangeSelector, { RangeValue, TimeRange, TimeRangeButton } from "@common/components/aoplatform/TimeRangeSelector";
import MonitorTable, { MonitorTableHandler } from "./MonitorTable";
import { DefaultOptionType } from "antd/es/select";
import { BasicResponse, STATUS_CODE } from "@common/const/const";
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
import { getTime } from "../utils/dashboard";
import { useParams } from "react-router-dom";
import { RouterParams } from "@core/components/aoplatform/RenderRoutes";
@@ -13,6 +13,7 @@ import { useExcelExport } from "@common/hooks/excel";
import { SERVICE_TABLE_GLOBAL_COLUMNS_CONFIG } from "@dashboard/const/const";
import { CloseOutlined, ExpandOutlined } from "@ant-design/icons";
import { useFetch } from "@common/hooks/http";
import { $t } from "@common/locales";
export type MonitorSubQueryData = SearchBody & { projects?:string[] ,type?:'subscriber'|'provider'}
@@ -64,7 +65,7 @@ export default function MonitorSubPage(props:MonitorSubPageProps){
if(code === STATUS_CODE.SUCCESS){
setListOfProjects(data.projects?.map((x:EntityItem)=>({label:x.name, value:x.id})))
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
return setListOfProjects([])
}
}).catch(() => {
@@ -102,9 +103,9 @@ export default function MonitorSubPage(props:MonitorSubPageProps){
fetchTableData(data).then((resp) => {
const {code,data,msg} = resp
if(code === STATUS_CODE.SUCCESS){
exportExcel('服务调用统计', [query!.start!, query!.end!], '服务调用统计', 'dashboard_service', SERVICE_TABLE_GLOBAL_COLUMNS_CONFIG, data.statistics)
exportExcel($t('服务调用统计'), [query!.start!, query!.end!], $t('服务调用统计'), 'dashboard_service', SERVICE_TABLE_GLOBAL_COLUMNS_CONFIG, data.statistics)
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
}
})
};
@@ -121,7 +122,7 @@ export default function MonitorSubPage(props:MonitorSubPageProps){
if(code === STATUS_CODE.SUCCESS){
return {data:data.statistics?.map((x:MonitorSubscriberData)=>{x.proxyRate = Number((x.proxyRate*100).toFixed(2));x.requestRate = Number((x.requestRate*100).toFixed(2));return x}), success: true}
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
return {data:[], success:false}
}
}).catch(() => {
@@ -152,8 +153,7 @@ export default function MonitorSubPage(props:MonitorSubPageProps){
className="w-[346px]"
mode="multiple"
maxTagCount={1}
// maxTagPlaceholder={(selectedList) => `and ${selectedList.length} more selected`}
placeholder="请选择"
placeholder={$t("请选择服务")}
value={queryData?.projects}
options={listOfProjects}
onChange={(value)=>{setQueryData(prevData=>({...prevData || {}, projects:value}))}}
@@ -161,13 +161,13 @@ export default function MonitorSubPage(props:MonitorSubPageProps){
</div>
<div>
<Button className="ml-btnybase" onClick={clearSearch}>
{$t('重置')}
</Button>
<Button type="primary" loading={queryBtnLoading} className="ml-btnybase" onClick={()=>{setQueryBtnLoading(true);getAppTableList()}}>
{$t('查询')}
</Button>
<Button className="ml-btnybase" loading={exportLoading} onClick={exportData}>
{$t('导出')}
</Button>
</div>
</div>
@@ -182,9 +182,9 @@ export default function MonitorSubPage(props:MonitorSubPageProps){
mask={!fullScreen}
title={<>
{fullScreen && <a className="mr-btnrbase text-[14px]" onClick={()=>{setFullScreen?.(false)}}>
<CloseOutlined className="mr-[4px]"/>退
<CloseOutlined className="mr-[4px]"/>{$t('退出全屏')}
</a>}
<span className="mr-btnrbase">{detailEntityName}</span>
<span className="mr-btnrbase">{detailEntityName}{$t('调用详情')}</span>
{!fullScreen && <ExpandOutlined className="text-MAIN_TEXT hover:text-MAIN_HOVER_TEXT" onClick={()=>{setFullScreen?.(true)}}/>}
</>}
width={fullScreen ? '100%' : '60%'}
@@ -1,16 +1,19 @@
import { ActionType, ProColumns } from "@ant-design/pro-components"
import { ActionType } from "@ant-design/pro-components"
import { useImperativeHandle, useRef, useState } from "react"
import PageList from "@common/components/aoplatform/PageList"
import PageList, { PageProColumns } from "@common/components/aoplatform/PageList"
import TableBtnWithPermission from "@common/components/aoplatform/TableBtnWithPermission"
import { API_TABLE_GLOBAL_COLUMNS_CONFIG,SERVICE_TABLE_GLOBAL_COLUMNS_CONFIG, APPLICATION_TABLE_GLOBAL_COLUMNS_CONFIG } from "@dashboard/const/const"
import {forwardRef} from "react"
import { COLUMNS_TITLE } from "@common/const/const"
const TableType = {
api :API_TABLE_GLOBAL_COLUMNS_CONFIG,
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'
@@ -60,17 +63,17 @@ const MonitorTable = forwardRef<MonitorTableHandler, MonitorTableProps<unknown>>
})
}
const operation:ProColumns<unknown>[] =[
const operation:PageProColumns<unknown>[] =[
{
title: '操作',
title: COLUMNS_TITLE.operate,
key: 'option',
width: 98,
btnNums:2,
fixed:'right',
hideInSetting:true,
valueType: 'option',
render: (_: React.ReactNode, entity: unknown) => [
// <TableBtnWithPermission access="system.dashboard.self.view" key="view" onClick={()=>onRowClick(entity)} btnTitle="查看"/>,
<TableBtnWithPermission access="" key="view" onClick={()=>onRowClick(entity)} btnTitle="查看"/>,
<TableBtnWithPermission access="" key="view" btnType="view" onClick={()=>onRowClick(entity)} btnTitle="查看"/>,
],
}
]
@@ -83,7 +86,7 @@ const MonitorTable = forwardRef<MonitorTableHandler, MonitorTableProps<unknown>>
besidesTableHeight={inModal ? 64+56+258: undefined}
ref={tableRef}
showPagination={showPagination}
columns = {[...(TableType[type] || []),...operation]}
columns = {[...(TableType[type] || []),...(APP_MODE === 'pro' ? operation : [])]}
request={getTableDataSource}
dataSource={tableListDataSource}
// tableClickAccess="system.dashboard.self.view"
@@ -16,8 +16,10 @@ import MonitorPieGraph from "./MonitorPieGraph";
import MonitorTable, { MonitorTableHandler } from "./MonitorTable";
import { CloseOutlined, ExpandOutlined, LoadingOutlined } from "@ant-design/icons";
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>>
@@ -127,7 +129,7 @@ const MonitorTotalPage = (props:MonitorTotalPageProps) => {
setTotalEmpty(data.requestSummary.total === 0 && data.proxySummary.total === 0)
}else{
setPieError(true)
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
}
}).finally(()=>{
dispatch({ type: ACTIONS.REQUEST_COMPLETE, payload: 'getPieData' });
@@ -146,7 +148,7 @@ const MonitorTotalPage = (props:MonitorTotalPageProps) => {
// this.invokeLineRef?.changeLineChart()
}else{
setInvokeStaticError(true)
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
}
}).finally(()=>{
dispatch({ type: ACTIONS.REQUEST_COMPLETE, payload: 'getInvokeData' });
@@ -163,7 +165,7 @@ const MonitorTotalPage = (props:MonitorTotalPageProps) => {
// this.trafficLineRef?.changeLineChart()
}else{
setTrafficStaticError(true)
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
}
}).finally(()=>{
dispatch({ type: ACTIONS.REQUEST_COMPLETE, payload: 'getMessageData' });
@@ -178,7 +180,7 @@ const MonitorTotalPage = (props:MonitorTotalPageProps) => {
if(code === STATUS_CODE.SUCCESS){
return {data:data.top10.map((x:MonitorApiData | MonitorSubscriberData)=>{x.proxyRate = Number((x.proxyRate*100).toFixed(2));x.requestRate = Number((x.requestRate*100).toFixed(2));return x}), success: true}
}else{
message.error(msg || '获取数据失败,请重试')
message.error(msg || RESPONSE_TIPS.dataError)
return {data:[], success:false}
}
}).catch(() => {
@@ -205,19 +207,19 @@ const MonitorTotalPage = (props:MonitorTotalPageProps) => {
const monitorTopDataTabItems:TabsProps['items'] = [
{
label:'API 请求量 Top10',
label:$t('API 请求量 Top10'),
key:'api',
children:<MonitorTable className="pb-[10px]" ref={monitorApiTableRef} type='api' id="dashboard_top10_api" onRowClick={(record)=>{getDetailData(record as MonitorApiData,'api')}} request={()=>getTablesData(queryData||{},'api')}/>
children:<MonitorTable className="pb-[10px]" ref={monitorApiTableRef} type='api' id="dashboard_top10_api" onRowClick={(record)=>{APP_MODE !== 'pro' ? null : getDetailData(record as MonitorApiData,'api')}} request={()=>getTablesData(queryData||{},'api')}/>
},
{
label:'应用调用量 Top10',
label:$t('应用调用量 Top10'),
key:'subscribers',
children:<MonitorTable className="pb-[10px]" ref={monitorSubTableRef} type='subscribers' id="dashboard_top10_subscriber" onRowClick={(record)=>{getDetailData(record as MonitorSubscriberData,'subscriber')}} request={()=>getTablesData(queryData||{},'subscriber')} />
children:<MonitorTable className="pb-[10px]" ref={monitorSubTableRef} type='subscribers' id="dashboard_top10_subscriber" onRowClick={(record)=>{APP_MODE !== 'pro' ? null : getDetailData(record as MonitorSubscriberData,'subscriber')}} request={()=>getTablesData(queryData||{},'subscriber')} />
},
{
label:'服务被调用量 Top10',
label:$t('服务被调用量 Top10'),
key:'providers',
children:<MonitorTable className="pb-[10px]" ref={monitorSubTableRef} type='provider' id="dashboard_top10_provider" onRowClick={(record)=>{getDetailData(record as MonitorSubscriberData,'provider')}} request={()=>getTablesData(queryData||{},'provider')} />
children:<MonitorTable className="pb-[10px]" ref={monitorSubTableRef} type='provider' id="dashboard_top10_provider" onRowClick={(record)=>{APP_MODE !== 'pro' ? null : getDetailData(record as MonitorSubscriberData,'provider')}} request={()=>getTablesData(queryData||{},'provider')} />
}
]
@@ -230,31 +232,17 @@ const MonitorTotalPage = (props:MonitorTotalPageProps) => {
return (
<div className={`h-[calc(100vh-140px)] overflow-hidden pb-btnybase flex flex-col ${totalEmpty ? 'bg-[#fff]' : 'bg-MENU_BG'}`}>
{/* 筛选区域 */}
<ScrollableSection>
<div className="flex items-center flex-wrap pb-[10px] px-btnbase content-before bg-MAIN_BG">
{/* 筛选集群 */}
{/* <div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
<label className="whitespace-nowrap"></label>
<Select
className="w-INPUT_NORMAL"
mode="multiple"
placeholder="请选择"
value={queryData?.clusters}
options={clusterList}
onChange={(value)=>{setQueryData(prevData=>({...prevData || {}, clusters:value}))}}
/>
</div> */}
<TimeRangeSelector
labelSize="small"
initialTimeButton={timeButton}
onTimeButtonChange={setTimeButton}
initialDatePickerValue={datePickerValue}
onTimeRangeChange={handleTimeRangeChange}/>
{/* 重置和查询按钮 */}
<div className="flex flex-nowrap items-center pt-btnybase">
<Button onClick={resetQuery}></Button>
<Button className="ml-btnybase" type="primary" loading={queryBtnLoading} onClick={() => {getMonitorData();setQueryBtnLoading(true)}}></Button>
<Button onClick={resetQuery}>{$t('重置')}</Button>
<Button className="ml-btnybase" type="primary" loading={queryBtnLoading} onClick={() => {getMonitorData();setQueryBtnLoading(true)}}>{$t('查询')}</Button>
</div>
</div>
<Spin wrapperClassName={`flex-1 ${totalEmpty ?'':'overflow-auto'}`} indicator={<LoadingOutlined style={{ fontSize: 24 }} spin/>} spinning={queryBtnLoading}>
@@ -263,25 +251,25 @@ const MonitorTotalPage = (props:MonitorTotalPageProps) => {
{/* 图表区域 */}
<div className=" px-btnbase mt-[12px] mb-[16px] grid gap-[20px]" style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(570px, 1fr))'}}>
{/* 请求统计饼图 */}
{pieError ? <Empty className="pt-[80px] bg-MAIN_BG w-[50%] h-[200px] m-0 mr-[16px]" image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无请求统计数据"/>: <MonitorPieGraph
{pieError ? <Empty className="pt-[80px] bg-MAIN_BG w-[50%] h-[200px] m-0 mr-[16px]" image={Empty.PRESENTED_IMAGE_SIMPLE} description={$t("暂无请求统计数据")}/>: <MonitorPieGraph
className="bg-MAIN_BG"
title="请求统计"
title={$t("请求统计")}
pieData={requestPie}
labelName="请求成功率"
labelName={$t("请求成功率")}
labelValue={requestSucRate}
subText="请求总数"
subText={$t("请求总数")}
subValue={changeNumberUnit(requestStatic?.total)}
status4xxCount={requestStatic?.status_4xx}
status5xxCount={requestStatic?.status_5xx}
/>}
{/* 转发统计饼图 */}
{pieError ? <Empty className="pt-[80px] bg-MAIN_BG w-[50%] h-[200px] m-0" image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无转发统计数据"/>: <MonitorPieGraph
{pieError ? <Empty className="pt-[80px] bg-MAIN_BG w-[50%] h-[200px] m-0" image={Empty.PRESENTED_IMAGE_SIMPLE} description={$t("暂无转发统计数据")}/>: <MonitorPieGraph
className=" bg-MAIN_BG"
title="转发统计"
title={$t("转发统计")}
pieData={proxyPie}
labelName="转发成功率"
labelName={$t("转发成功率")}
labelValue={proxySucRate}
subText="转发总数"
subText={$t("转发总数")}
subValue={changeNumberUnit(proxyStatic?.total)}
status4xxCount={proxyStatic?.status_4xx}
status5xxCount={proxyStatic?.status_5xx}
@@ -289,18 +277,18 @@ const MonitorTotalPage = (props:MonitorTotalPageProps) => {
</div>
{/* 折线图区域 */}
{/* 调用量统计折线图 */}
{invokeStaticError ? <Empty className="pt-[80px] m-btnbase mb-[16px] h-[200px] bg-MAIN_BG" image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无调用量统计数据"/>: <MonitorLineGraph
{invokeStaticError ? <Empty className="pt-[80px] m-btnbase mb-[16px] h-[200px] bg-MAIN_BG" image={Empty.PRESENTED_IMAGE_SIMPLE} description={$t("暂无调用量统计数据")}/>: <MonitorLineGraph
className=" bg-MAIN_BG pt-[16px]"
lineData={invokeStatic}
titles={['调用量统计']}
titles={[$t('调用量统计')]}
yAxisTitle={timeUnit || '-'}
type="invoke"
/>}
{/* 报文量统计折线图 */}
{trafficStaticError ? <Empty className=" bg-MAIN_BG pt-[80px] m-btnbase mb-0 h-[200px]" image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无报文量统计数据"/>:<MonitorLineGraph
{trafficStaticError ? <Empty className=" bg-MAIN_BG pt-[80px] m-btnbase mb-0 h-[200px]" image={Empty.PRESENTED_IMAGE_SIMPLE} description={$t("暂无报文量统计数据")}/>:<MonitorLineGraph
className=" bg-MAIN_BG pt-[16px]"
lineData={trafficStatic}
titles={['报文量统计']}
titles={[$t('报文量统计')]}
yAxisTitle={timeUnit || '-'}
type="traffic"
/>}
@@ -314,9 +302,9 @@ const MonitorTotalPage = (props:MonitorTotalPageProps) => {
mask={!fullScreen}
title={<>
{fullScreen && <a className="mr-btnrbase text-[14px]" onClick={()=>{setFullScreen(false)}}>
<CloseOutlined className="mr-[4px]"/>退
<CloseOutlined className="mr-[4px]"/>{$t('退出全屏')}
</a>}
<span className="mr-btnrbase">{detailEntityName}</span>
<span className="mr-btnrbase">{detailEntityName}{$t('调用详情')}</span>
{!fullScreen && <ExpandOutlined className="text-MAIN_TEXT hover:text-MAIN_HOVER_TEXT" onClick={()=>{setFullScreen(true)}}/>}
</>}
width={fullScreen ? '100%' : '60%'}
+63 -63
View File
@@ -1,13 +1,14 @@
import { ProColumns } from "@ant-design/pro-components"
import { MonitorApiData, MonitorData } from "./type"
import { EChartsOption } from "echarts-for-react"
import { Tooltip } from "antd"
import { $t } from "@common/locales"
import { PageProColumns } from "@common/components/aoplatform/PageList"
// 监控表格参数
export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:string})[] = [
export const DASHBOARD_BASE_COLUMNS_CONFIG:(PageProColumns<MonitorData>&{eoTitle:string})[] = [
{
title: '请求总数',
eoTitle:'请求总数',
title:$t('请求总数'),
eoTitle:$t('请求总数'),
dataIndex: 'requestTotal',
sorter: (a,b)=> {
return a.requestTotal - b.requestTotal
@@ -16,9 +17,8 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
width: 96
},
{
// title: '请求成功数',
title: <Tooltip title="请求成功数" ></Tooltip>,
eoTitle:'请求成功数',
title: <Tooltip title={$t("请求成功数")} >{$t('请求成功数')}</Tooltip>,
eoTitle:$t('请求成功数'),
dataIndex: 'requestSuccess',
width: 106,
ellipsis:true,
@@ -27,9 +27,9 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
},
},
{
// title: '请求成功率',
title: <Tooltip title="请求成功率" ></Tooltip>,
eoTitle:'请求成功率',
// title:$t('请求成功率',
title: <Tooltip title={$t('请求成功率')} >{$t('请求成功率')}</Tooltip>,
eoTitle:$t('请求成功率'),
dataIndex: 'requestRate',
valueType:'percent',
ellipsis:true,
@@ -39,9 +39,9 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
width: 106
},
{
title: '转发总数',
eoTitle:'转发总数',
width: 96,
title:$t('转发总数'),
eoTitle:$t('转发总数'),
width: 96,
dataIndex: 'proxyTotal',
ellipsis:true,
sorter: (a,b)=> {
@@ -49,10 +49,10 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
},
},
{
// title: '转发成功数',
title: <Tooltip title="转发成功数" ></Tooltip>,
eoTitle:'转发成功数',
width: 106,
// title:$t('转发成功数',
title: <Tooltip title={$t("转发成功数")} >{$t('转发成功数')}</Tooltip>,
eoTitle:$t('转发成功数'),
width: 106,
dataIndex: 'proxySuccess',
ellipsis:true,
sorter: (a,b)=> {
@@ -60,10 +60,10 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
},
},
{
// title: '转发成功率',
title: <Tooltip title="转发成功率" ></Tooltip>,
eoTitle:'转发成功率',
width: 106,
// title:$t('转发成功率',
title: <Tooltip title={$t("转发成功率")} >{$t('转发成功率')}</Tooltip>,
eoTitle:$t('转发成功率'),
width: 106,
dataIndex: 'proxyRate',
valueType:'percent',
ellipsis:true,
@@ -72,10 +72,10 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
},
},
{
// title: '失败状态码数',
title: <Tooltip title="失败状态码数" ></Tooltip>,
eoTitle:'失败状态码数',
width: 120,
// title:$t('失败状态码数',
title: <Tooltip title={$t("失败状态码数")} >{$t('失败状态码数')}</Tooltip>,
eoTitle:$t('失败状态码数'),
width: 120,
dataIndex: 'statusFail',
ellipsis:true,
sorter: (a,b)=> {
@@ -83,9 +83,9 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
},
},
{
// title: '平均响应时间(ms)',
title: <Tooltip title="平均响应时间(ms)" >(ms)</Tooltip>,
eoTitle:'平均响应时间(ms)',
// title:$t('平均响应时间(ms)',
title: <Tooltip title={$t("平均响应时间(ms)")} >{$t('平均响应时间(ms)')}</Tooltip>,
eoTitle:$t('平均响应时间(ms)'),
width: 148,
dataIndex: 'avgResp',
valueType:'digit',
@@ -95,9 +95,9 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
},
},
{
// title: '最大响应时间(ms)',
title: <Tooltip title="最大响应时间(ms)" >(ms)</Tooltip>,
eoTitle:'最大响应时间(ms)',
// title:$t('最大响应时间(ms)',
title: <Tooltip title={$t("最大响应时间(ms)")} >{$t('最大响应时间(ms)')}</Tooltip>,
eoTitle:$t('最大响应时间(ms)'),
width: 148,
dataIndex: 'maxResp',
valueType:'digit',
@@ -107,9 +107,9 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
},
},
{
// title: '最小响应时间(ms)',
title: <Tooltip title="最小响应时间(ms)" >(ms)</Tooltip>,
eoTitle:'最小响应时间(ms)',
// title:$t('最小响应时间(ms)',
title: <Tooltip title={$t("最小响应时间(ms)")} >{$t('最小响应时间(ms)')}</Tooltip>,
eoTitle:$t('最小响应时间(ms)'),
width: 148,
dataIndex: 'minResp',
valueType:'digit',
@@ -119,9 +119,9 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
},
},
{
// title: '平均请求流量(KB)',
title: <Tooltip title="平均请求流量(KB)" >(KB)</Tooltip>,
eoTitle:'平均请求流量(KB)',
// title:$t('平均请求流量(KB)',
title: <Tooltip title={$t("平均请求流量(KB)")} >{$t('平均请求流量(KB)')}</Tooltip>,
eoTitle:$t('平均请求流量(KB)'),
width: 148,
dataIndex: 'avgTraffic',
valueType:'digit',
@@ -131,9 +131,9 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
},
},
{
// title: '最大请求流量(KB)',
title: <Tooltip title="最大请求流量(KB)" >(KB)</Tooltip>,
eoTitle:'最大请求流量(KB)',
// title:$t('最大请求流量(KB)',
title: <Tooltip title={$t("最大请求流量(KB)")} >{$t('最大请求流量(KB)')}</Tooltip>,
eoTitle:$t('最大请求流量(KB)'),
width: 148,
dataIndex: 'maxTraffic',
valueType:'digit',
@@ -143,9 +143,9 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
},
},
{
// title: '最小请求流量(KB)',
title: <Tooltip title="最小请求流量(KB)" >(KB)</Tooltip>,
eoTitle:'最小请求流量(KB)',
// title:$t('最小请求流量(KB)',
title: <Tooltip title={$t("最小请求流量(KB)")} >{$t('最小请求流量(KB)')}</Tooltip>,
eoTitle:$t('最小请求流量(KB)'),
width: 148,
dataIndex: 'minTraffic',
valueType:'digit',
@@ -156,11 +156,11 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
}]
export const API_TABLE_GLOBAL_COLUMNS_CONFIG:(ProColumns<MonitorApiData>&{eoTitle:string})[] = [
export const API_TABLE_GLOBAL_COLUMNS_CONFIG:(PageProColumns<MonitorApiData>&{eoTitle:string})[] = [
{
title: 'API 名称',
eoTitle:'API 名称',
title:$t('API 名称'),
eoTitle:$t('API 名称'),
dataIndex: 'name',
width:120,
ellipsis:true,
@@ -168,29 +168,29 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
disable:true
},
{
title: '请求路径',
eoTitle:'请求路径',
title:$t('请求路径'),
eoTitle:$t('请求路径'),
dataIndex: 'path',
ellipsis:true,
width: 80
},
{
title: '所属服务',
eoTitle:'所属服务',
title:$t('所属服务'),
eoTitle:$t('所属服务'),
dataIndex: ['project','name'],
ellipsis:true,
width: 80
},
...DASHBOARD_BASE_COLUMNS_CONFIG as (ProColumns<MonitorApiData>&{eoTitle:string})[]
...DASHBOARD_BASE_COLUMNS_CONFIG as (PageProColumns<MonitorApiData>&{eoTitle:string})[]
]
export const APPLICATION_TABLE_GLOBAL_COLUMNS_CONFIG:(ProColumns<MonitorApiData>&{eoTitle:string})[] = [
export const APPLICATION_TABLE_GLOBAL_COLUMNS_CONFIG:(PageProColumns<MonitorApiData>&{eoTitle:string})[] = [
{
title: '应用名称',
eoTitle:'应用名称',
title:$t('应用名称'),
eoTitle:$t('应用名称'),
dataIndex: 'name',
width:160,
ellipsis:true,
@@ -198,21 +198,21 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
disable:true
},
{
title: '应用 ID',
eoTitle:'应用 ID',
title:$t('应用 ID'),
eoTitle:$t('应用 ID'),
dataIndex: 'id',
width: 140,
ellipsis:true,
fixed: 'left'
},
...DASHBOARD_BASE_COLUMNS_CONFIG as (ProColumns<MonitorApiData>&{eoTitle:string})[]
...DASHBOARD_BASE_COLUMNS_CONFIG as (PageProColumns<MonitorApiData>&{eoTitle:string})[]
]
export const SERVICE_TABLE_GLOBAL_COLUMNS_CONFIG:(ProColumns<MonitorApiData>&{eoTitle:string})[] = [
export const SERVICE_TABLE_GLOBAL_COLUMNS_CONFIG:(PageProColumns<MonitorApiData>&{eoTitle:string})[] = [
{
title: '服务名称',
eoTitle:'服务名称',
title:$t('服务名称'),
eoTitle:$t('服务名称'),
dataIndex: 'name',
width:160,
ellipsis:true,
@@ -220,14 +220,14 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
disable:true
},
{
title: '服务 ID',
eoTitle:'服务 ID',
title:$t('服务 ID'),
eoTitle:$t('服务 ID'),
dataIndex: 'id',
width: 140,
ellipsis:true,
fixed: 'left'
},
...DASHBOARD_BASE_COLUMNS_CONFIG as (ProColumns<MonitorApiData>&{eoTitle:string})[]
...DASHBOARD_BASE_COLUMNS_CONFIG as (PageProColumns<MonitorApiData>&{eoTitle:string})[]
]
export const MONITOR_LINE_CHART_BASIC_INVOKE_SELECTED = {
@@ -2,6 +2,7 @@
import { useEffect } from "react";
import { useBreadcrumb } from "@common/contexts/BreadcrumbContext";
import DashboardPage from "./DashboardTabPage";
import { $t } from "@common/locales";
export default function Dashboard(){
const { setBreadcrumb } = useBreadcrumb()
@@ -9,7 +10,7 @@ export default function Dashboard(){
useEffect(() => {
setBreadcrumb([
{
title:'运行视图'
title:$t('运行视图')
},
])
@@ -17,8 +18,9 @@ export default function Dashboard(){
return (
<>
<DashboardPage />
<div className="h-full w-full pr-PAGE_INSIDE_X pb-PAGE_INSIDE_B ">
<DashboardPage />
</div>
</>
)
}
@@ -4,6 +4,9 @@ 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";
const APP_MODE = import.meta.env.VITE_APP_MODE;
export default function DashboardTabPage(){
const { dashboardType} = useParams<RouterParams>()
@@ -16,32 +19,32 @@ export default function DashboardTabPage(){
const monitorTabItems:TabsProps['items'] = [
{
label:'监控总览',
label:$t('监控总览'),
key:'total',
children:<DashboardTotal />
},
{
label:'服务被调用统计',
label:$t('服务被调用统计'),
key:'subscriber',
children:<Outlet />
},
{
label:'应用调用统计',
label:$t('应用调用统计'),
key:'provider',
children:<Outlet />
},
{
label:'API 调用统计',
label:$t('API 调用统计'),
key:'api',
children:<Outlet />
}
]
return (<>
<Tabs activeKey={activeKey} onChange={(val)=>{
{APP_MODE === 'pro' ? <Tabs activeKey={activeKey} onChange={(val)=>{
setActiveKey(val);
navigateTo(`/dashboard/${val === 'total' ? val :`${val}/list`}`)
}}
items={monitorTabItems} className="h-auto mt-[6px]" size="small" tabBarStyle={{paddingLeft:'10px',marginTop:'0px',marginBottom:'0px'}} />
</>)
: <Outlet />} </>)
}
@@ -1,5 +1,6 @@
import { RangeValue } from "@common/components/aoplatform/TimeRangeSelector";
import { $t } from "@common/locales";
export function getTime (
timeButton: string,
@@ -45,23 +46,23 @@ export function getTime (
// 相差秒数
switch (timeInterval) {
case '1m': {
timeUnit = '每分钟'
timeUnit = $t('每分钟')
break
}
case '5m': {
timeUnit = '每5分钟'
timeUnit = $t('每5分钟')
break
}
case '1h': {
timeUnit = '每小时'
timeUnit = $t('每小时')
break
}
case '1d': {
timeUnit = '每天'
timeUnit = $t('每天')
break
}
case '1w': {
timeUnit = '每周'
timeUnit = $t('每周')
break
}
}
@@ -71,23 +72,23 @@ export function getTime (
// 当数据超过10万时,保留两个小数点,单位为万,如123212,显示12.32万;
export function changeNumberUnit (value?:number):string {
if (value && value > 1000000000) {
return (value && value / 100000000).toFixed(2) + '亿'
return (value && value / 100000000).toFixed(2) + $t('亿')
} else if (value && value > 1000000) {
return (value && value / 10000).toFixed(0) + '万'
return (value && value / 10000).toFixed(0) + $t('万')
} else if (value && value > 10000) {
return (value && value / 10000).toFixed(2) + '万'
return (value && value / 10000).toFixed(2) + $t('万')
}
return (value ?? '-') + ' 次'
return (value ?? '-') + $t(' 次')
}
export function yUnitFormatter (value:number):string {
let res:string = ''
if (value > 100000000) {
res = (value / 100000000).toFixed(2) + '亿'
res = (value / 100000000).toFixed(2) + $t('亿')
} else if (value > 1000000) {
res = (value / 10000).toFixed(0) + '万'
res = (value / 10000).toFixed(0) + $t('万')
} else if (value > 100000) {
res = (value / 10000).toFixed(2) + '万'
res = (value / 10000).toFixed(2) + $t('万')
} else {
res = value.toFixed(0)
}
+1 -1
View File
@@ -24,6 +24,6 @@
"@dashboard/*": ["./src/*"]
},
},
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TransferTable.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/GroupTree.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../core/src/pages/serviceCategory/ServiceHubCategoryConfig.tsx"],
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../core/src/pages/serviceCategory/ServiceHubCategoryConfig.tsx"],
"references": [{ "path": "./tsconfig.node.json" }]
}
@@ -147,7 +147,7 @@ const generateRoutes = (routerConfig: RouteConfig[]) => {
const LazyComponent = route.lazy as React.ExoticComponent<unknown>;
routeElement = (
<Suspense fallback={ <div className=''><Skeleton className='m-btnbase w-[calc(100%-20px)]' active /></div>}>
<Suspense fallback={ <div className=''><Skeleton className='m-btnbase w-calc-100vw-minus-padding-r' active /></div>}>
{route.provider ? (
createElement(route.provider, {}, <LazyComponent />)
) : (
@@ -1,12 +1,13 @@
import { ProColumns } from "@ant-design/pro-components";
import { MenuProps } from "antd";
import { getItem } from "@common/utils/navigation";
import { ServiceHubTableListItem } from "./type";
import { $t } from "@common/locales";
import { PageProColumns } from "@common/components/aoplatform/PageList";
export const SERVICE_HUB_TABLE_COLUMNS: ProColumns<ServiceHubTableListItem>[] = [
export const SERVICE_HUB_TABLE_COLUMNS: PageProColumns<ServiceHubTableListItem>[] = [
{
title: '服务名称',
title:$t('服务名称'),
dataIndex: 'name',
ellipsis:true,
width:160,
@@ -16,29 +17,29 @@ export const SERVICE_HUB_TABLE_COLUMNS: ProColumns<ServiceHubTableListItem>[] =
},
},
{
title: '服务ID',
title:$t('服务ID'),
dataIndex: 'id',
width: 140,
ellipsis:true
},
{
title: '服务标签',
title:$t('服务标签'),
dataIndex: 'tags',
ellipsis:true,
renderText:(_,entity:ServiceHubTableListItem)=>entity.tags?.map(x=>x.name).join(',') || '-'
},
{
title: '所属系统',
title:$t('所属系统'),
dataIndex: ['app','name'],
ellipsis:true
},
{
title: '所属团队',
title:$t('所属团队'),
dataIndex: ['team','name'],
ellipsis:true
},
{
title: '服务分类',
title:$t('服务分类'),
dataIndex: ['catalogue','name'],
ellipsis:true
}
@@ -47,7 +48,7 @@ export const SERVICE_HUB_TABLE_COLUMNS: ProColumns<ServiceHubTableListItem>[] =
export const TENANT_MANAGEMENT_APP_MENU: MenuProps['items'] = [
getItem('订阅的服务', 'service'),
getItem('访问授权', 'authorization'),
getItem('应用管理', 'setting'),
getItem($t('订阅的服务'), 'service'),
getItem($t('访问授权'), 'authorization'),
getItem($t('应用管理'), 'setting'),
];
@@ -93,7 +93,7 @@ export type ServiceHubAppListItem = {
export type TenantManagementServiceListItem = {
id:string
service:EntityItem
applyStatus:SubscribeEnum
applyStatus:typeof SubscribeEnum
app:EntityItem
team:EntityItem
from:SubscribeFromEnum
+2 -2
View File
@@ -353,13 +353,13 @@ p{
color:var(--primary-color) !important;
}
button.ant-btn:not(:disabled):not(.text-table_text){
/* button.ant-btn:not(:disabled):not(.text-table_text){
color:var(--primary-color);
}
button.ant-btn:not(:disabled):not(.text-table_text):hover{
color:var(--button-primary-hover-background-color) !important;
}
} */
}
.ant-popover .ant-popover-inner{
@@ -1,159 +0,0 @@
import {FC, useCallback, useEffect, useRef, useState} from "react";
import {App, Button, Form, FormInstance, Input} from "antd";
import {useGlobalContext} from "@common/contexts/GlobalStateContext.tsx";
import {useFetch} from "@common/hooks/http.ts";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {useNavigate} from "react-router-dom";
// import {useCrypto} from "../hooks/crypto.ts";
import Logo from '@common/assets/logo.png'
const Login:FC = ()=> {
const {state, dispatch} = useGlobalContext()
const {fetchData} = useFetch()
const { message } = App.useApp()
const navigate = useNavigate();
const formRef = useRef<FormInstance>(null);
const [loading,setLoading] = useState<boolean>()
// const { encryptByEnAES } = useCrypto();
const check = useCallback(()=>{
fetchData<BasicResponse<{channel:Array<{name:string}>, status:string}>>('account/login',{method:'GET'}).then(response=>{
const {code,data} = response
if(code === STATUS_CODE.SUCCESS && data.status !== 'anonymous'){
dispatch({type:'LOGIN'})
//console.log('校验成功')
navigate('/serviceHub/list')
}else{
dispatch({type:'LOGOUT'})
}
})
},[])
const getSystemInfo = useCallback(()=>{
fetchData<BasicResponse<{version:string, buildTime:string}>>('common/version',{method:'GET', eoTransformKeys:['build_time']}).then(response=>{
const {code,data} = response
if(code === STATUS_CODE.SUCCESS){
dispatch({type:'UPDATE_VERSION',version:data.version})
dispatch({type:'UPDATE_DATE',updateDate:data.buildTime})
}
})
},[])
const login = async () => {
if (formRef.current) {
try {
const values = await formRef.current.validateFields();
setLoading(true);
const { username, password } = values;
// const encryptedPassword = encryptByEnAES(username, password);
const body = {
name:username,
password: password
// client: 1,
// type: 1,
// app_type: 4,
};
const {code,msg } = await fetchData<BasicResponse<null>>('account/login/username',{method:'POST',eoBody:(body)})
if (code === STATUS_CODE.SUCCESS) {
dispatch({type:'LOGIN'})
message.success('登录成功');
const callbackUrl = new URLSearchParams(window.location.search).get('callbackUrl');
if (callbackUrl && callbackUrl !== 'null') {
navigate(callbackUrl);
} else {
navigate('/serviceHub/list')
}
}else{
dispatch({type:'LOGOUT'})
//console.log(msg)
message.error(msg)
}
} catch (err) {
console.warn(err);
} finally {
setLoading(false)
}
}
};
useEffect(() => {
check()
getSystemInfo()
}, []);
return (
<div className="h-full w-full flex bg-[#f5f7fa] items-center overflow-auto min-h-[490px]">
<div className="w-[410px] mx-auto">
<div className="mx-auto">
<span className="flex items-center justify-center">
<img
className="h-[40px] mr-[8px]"
src={Logo}
/>
</span>
</div>
<section className="block w-[410px] mx-auto mt-[46px] bg-MAIN_BG px-[30px] pt-[30px] box-border rounded-[10px] shadow-[0_5px_20px_0_rgba(0,0,0,5%)]">
<div className="h-full">
<div>
<div className="flex justify-center items-center">
<span className="text-[24px] text-[#101010]"></span>
</div>
<Form onFinish={login} className="w-[350px] pt-[28px] pb-[8px]"
ref={formRef}>
<Form.Item
className="p-0 bg-transparent rounded border-none"
name="username"
rules={[{ required: true, message: '请输入账号' ,whitespace:true }]}
>
<Input
className="w-[350px] h-[40px]"
placeholder="账号"
autoComplete="on"
autoFocus
/>
</Form.Item>
<Form.Item
className="p-0 bg-transparent rounded border-none"
name="password"
rules={[{ required: true, message: '请输入密码' }]}
>
<Input.Password
className="w-[350px] h-[40px]"
placeholder="密码"
autoComplete="off"
/>
</Form.Item>
<div className=" justify-center">
<Form.Item
className="p-0 bg-transparent rounded border-none"
>
<Button loading={loading} className="h-[40px] mt-mbase w-full inline-flex justify-center items-center" type="primary" htmlType="submit">
</Button>
</Form.Item>
</div>
</Form>
</div>
</div>
</section>
<section className="flex flex-col items-center mt-[46px] text-SECOND_TEXT">
<p className="leading-[28px]">Version {state.version}-{state.updateDate}</p>
<p className="leading-[28px]">{state.powered}</p>
</section>
</div>
</div>
);
}
export default Login;
@@ -7,6 +7,7 @@ import {DataNode} from "antd/es/tree";
import {ApiDetail} from "@common/const/api-detail";
import ApiTest from "@common/components/postcat/ApiTest.tsx";
import DirectoryTree from "antd/es/tree/DirectoryTree";
import { $t } from "@common/locales";
type ApiTestGroupType = {
apiInfoList:ApiDetail[]
@@ -62,7 +63,7 @@ export default function ApiTestGroup({apiInfoList,selectedApiId }:ApiTestGroupTy
<div className="flex flex-1 h-full w-full">
<div className="w-[220px] p-btnbase border-0 border-solid border-r-[1px] border-r-BORDER">
<Input className=" my-btnybase" onChange={(e) => debounce(onSearchWordChange, 100)(e)}
allowClear placeholder="搜索分类或标签"
allowClear placeholder={$t("搜索分类或标签")}
prefix={<SearchOutlined className="cursor-pointer" onClick={(e) => {
onSearchWordChange(e)
}}/>}/>
@@ -78,7 +79,7 @@ export default function ApiTestGroup({apiInfoList,selectedApiId }:ApiTestGroupTy
</div>
{selectedApiInfo ?
<ApiTest apiInfo={selectedApiInfo} /> :
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无API数据"/>
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={$t("暂无API数据")}/>
}
</div>
)
@@ -2,9 +2,10 @@
import { App, Form, Row, Col, Select, Input } from "antd";
import { forwardRef, useEffect, useImperativeHandle, useMemo } from "react";
import WithPermission from "@common/components/aoplatform/WithPermission";
import { BasicResponse, STATUS_CODE } from "@common/const/const";
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE, VALIDATE_MESSAGE } from "@common/const/const";
import { ApplyServiceHandle, ApplyServiceProps } from "../../const/serviceHub/type";
import { useFetch } from "@common/hooks/http";
import { $t } from "@common/locales";
export const ApplyServiceModal = forwardRef<ApplyServiceHandle,ApplyServiceProps>((props,ref)=>{
const { message } = App.useApp()
@@ -22,11 +23,11 @@ export const ApplyServiceModal = forwardRef<ApplyServiceHandle,ApplyServiceProps
fetchData<BasicResponse<null>>('catalogue/service/subscribe',{method:'POST',eoParams:{team:entity?.team?.id}, eoBody:({...value,service:entity.id})}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
@@ -48,23 +49,23 @@ export const ApplyServiceModal = forwardRef<ApplyServiceHandle,ApplyServiceProps
autoComplete="off"
>
<Row className="mb-btnybase h-[32px]" >
<Col span={6} className="pb-[8px] text-left"></Col>
<Col span={6} className="pb-[8px] text-left">{$t('服务名称')}</Col>
<Col span={18}>{entity.name}</Col>
</Row>
<Row className="h-[32px] mb-btnybase">
<Col span={6} className="pb-[8px] text-left"> ID</Col>
<Col span={6} className="pb-[8px] text-left">{$t('服务 ID')}</Col>
<Col span={18}>{entity.id}</Col>
</Row>
<Form.Item
label="应用"
label={$t("应用")}
name="applications"
rules={[{ required: true, message: '必填项' }]}
rules={[{ required: true, message: VALIDATE_MESSAGE.required }]}
>
<Select className="w-INPUT_NORMAL" disabled={reApply} placeholder="搜索或选择应用" mode="multiple" options={mySystemOptionList?.filter((x)=>x.value !== entity.id)}/>
<Select className="w-INPUT_NORMAL" disabled={reApply} placeholder={$t("搜索或选择应用")} mode="multiple" options={mySystemOptionList?.filter((x)=>x.value !== entity.id)}/>
</Form.Item>
<Form.Item
label="申请理由"
label={$t("申请理由")}
name="reason"
>
<Input.TextArea className="w-INPUT_NORMAL" placeholder=""/>
@@ -8,6 +8,7 @@ import {ApiDetail} from "@common/const/api-detail";
import {ServiceDetailType } from "../../const/serviceHub/type.ts";
import ApiMatch from "@common/components/postcat/api/ApiPreview/components/ApiMatch/index.tsx";
import ApiProxy from "@common/components/postcat/api/ApiPreview/components/ApiProxy/index.tsx";
import { $t } from "@common/locales/index.ts";
const ServiceHubApiDocument = ({service}:{service:ServiceDetailType})=>{
const {serviceId} = useParams<RouterParams>();
@@ -28,7 +29,7 @@ const ServiceHubApiDocument = ({service}:{service:ServiceDetailType})=>{
{
key: 'apiDocument-list',
href: '#apiDocument-list',
title: 'API 列表',
title:$t('API 列表'),
children:apiDocs?.map((x)=>({
key:x.id,
href:`#apiDocument-${x.id}`,
@@ -38,7 +39,7 @@ const ServiceHubApiDocument = ({service}:{service:ServiceDetailType})=>{
// {
// key: 'apiDocument-statusCode',
// href: '#apiDocument-statusCode',
// title: '状态码',
// title:$t('状态码',
// },
], [apiDocs]);
@@ -63,9 +64,6 @@ const ServiceHubApiDocument = ({service}:{service:ServiceDetailType})=>{
return (
<>
<div className="flex flex-col p-btnbase pt-[4px] h-full flex-1 overflow-auto" id='layout-ref'>
{/* <div className="bg-[#fff] rounded p-btnbase pl-0 mb-[16px]">
<Descriptions className="bg-bar-theme p-[16px] rounded service-hub-description" title="" items={getBasicInfo} column={4} labelStyle={{width:'80px',justifyContent:'flex-end',fontWeight:'bold'}} contentStyle={{color:'#333'}}/>
</div> */}
<div className='bg-[#fff] rounded p-btnbase pl-0 flex justify-between'>
<div className="w-[calc(100%-220px)]" >
<p className="font-bold text-[20px] leading-[32px] mb-[12px] h-[32px]" id="apiDocument-list">API </p>
@@ -83,23 +81,20 @@ const ServiceHubApiDocument = ({service}:{service:ServiceDetailType})=>{
readOnly
addonBefore={apiDetail?.method}
value={apiDetail?.path}
// enterButton={<SearchBtn entity={apiDetail}/>}
// onSearch={handleTest}
/>
</Space>
{
apiDetail?.match && apiDetail.match?.length > 0 &&
<ApiMatch title='高级匹配' rows={apiDetail?.match} />
<ApiMatch title={$t('高级匹配')} rows={apiDetail?.match} />
}
{
apiDetail?.proxy && Object.keys(apiDetail?.proxy).length > 0 &&
<ApiProxy title='转发规则' proxyInfo={apiDetail?.proxy} />
<ApiProxy title={$t('转发规则')} proxyInfo={apiDetail?.proxy} />
}
{apiDetail && <ApiPreview entity={{...apiDetail.doc,name:apiDetail.name, method:apiDetail.method,uri:apiDetail.path, protocol:apiDetail.protocol||'HTTP'}} />}
</div>
// <ApiPreview testClick={()=>testClick(apiDocs.id)} entity={doc} />
}]}
activeKey={activeKey}
onChange={(val)=>{setActiveKey(val as string[])}}
@@ -108,18 +103,10 @@ const ServiceHubApiDocument = ({service}:{service:ServiceDetailType})=>{
))}
</div>
{/* <div className="h-[16px] bg-[#f7f8fa] mx-[-16px]"></div>
<div className='bg-[#fff] rounded pt-btnbase'>
<p className="font-bold text-[20px] leading-[32px] mb-[12px] h-[32px]" id="apiDocument-statusCode"></p>
<CodePage />
</div> */}
</div>
<FloatButton.Group shape="circle" style={floatButtonStyle}>
<Anchor
// className='absolute py-5 px-btnbase left-0 z-[13]'
// affix={false}
// showInkInFixed={true}
targetOffset={60}
getContainer = {()=> document.getElementById('layout-ref')!}
items={category}
@@ -133,7 +120,7 @@ const ServiceHubApiDocument = ({service}:{service:ServiceDetailType})=>{
maskClosable={false}
width="100%" placement="right" onClose={onClose} open={apiTestDrawOpen}
extra={
<Button onClick={onClose}>退</Button>
<Button onClick={onClose}>{$t('退出测试')}</Button>
}
closeIcon={false}
>
@@ -3,17 +3,18 @@ import {RouterParams} from "@core/components/aoplatform/RenderRoutes.tsx";
import { App, Avatar, Button, Descriptions, Divider, Tabs} from "antd";
import { useEffect, useRef, useState} from "react";
import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
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 { ApplyServiceModal } from "./ApplyServiceModal.tsx";
import ServiceHubApiDocument from "./ServiceHubApiDocument.tsx";
import { ApiFilled, ArrowLeftOutlined, LeftOutlined } from "@ant-design/icons";
import { ApiFilled, ArrowLeftOutlined } 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";
const ServiceHubDetail = ()=>{
@@ -44,7 +45,7 @@ const ServiceHubDetail = ()=>{
setServiceDoc(DOMPurify.sanitize(data.service.document))
setActiveKey(data.service.apis.map((x)=>x.id))
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
})
}
@@ -61,8 +62,8 @@ const ServiceHubDetail = ()=>{
getMySelectList()
setBreadcrumb(
[
{title:<Link to={`/serviceHub/list`}></Link>},
{title:'服务详情'}
{title:<Link to={`/serviceHub/list`}>{$t('服务市场')}</Link>},
{title:$t('服务详情')}
]
)
@@ -78,7 +79,7 @@ const ServiceHubDetail = ()=>{
label:x.name, value:x.id
}}))
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
})
}
@@ -86,15 +87,15 @@ const ServiceHubDetail = ()=>{
const openModal = (type:'apply')=>{
modal.confirm({
title:'申请服务',
title:$t('申请服务'),
content:<ApplyServiceModal ref={applyRef} entity={{...serviceBasicInfo!, name:serviceName!, id:serviceId!}} mySystemOptionList={mySystemOptionList!}/>,
onOk:()=>{
return applyRef.current?.apply().then((res)=>{
if(res === true) setApplied(true)
})
},
okText:'确认',
cancelText:'取消',
okText:$t('确认'),
cancelText:$t('取消'),
closable:true,
icon:<></>,
width:600
@@ -104,13 +105,13 @@ const ServiceHubDetail = ()=>{
const items = [
{
key: 'introduction',
label: '介绍',
label: $t('介绍'),
children: <><div className="p-btnbase preview-document" dangerouslySetInnerHTML={{__html: serviceDoc || ''}}></div></>,
icon: <Icon icon="ic:baseline-space-dashboard" width="14" height="14"/>,
},
{
key: 'api-document',
label: 'API 文档',
label: $t('API 文档'),
children: <div className="p-btnbase"><ServiceHubApiDocument service={service!} /></div>,
icon: <ApiFilled />
}
@@ -122,7 +123,7 @@ const ServiceHubDetail = ()=>{
<section className="flex flex-col gap-btnbase p-btnbase ">
<div className="text-[18px] leading-[25px] pb-[12px]">
<Button type="text" onClick={()=>navigate(`/serviceHub/list`)}><ArrowLeftOutlined className="max-h-[14px]" /></Button>
<Button type="text" onClick={()=>navigate(`/serviceHub/list`)}><ArrowLeftOutlined className="max-h-[14px]" />{$t('返回')}</Button>
</div>
<div className="flex">
{/* <Avatar shape="square" size={50} className=" bg-[linear-gradient(135deg,white,#f0f0f0)] text-[#333] rounded-[12px]" > {service?.name?.substring(0,1)}</Avatar> */}
@@ -137,7 +138,7 @@ const ServiceHubDetail = ()=>{
<div className="mt-[10px] flex flex-col gap-btnrbase font-normal">
{serviceDesc || '-'}
<div>
<Button type="primary" onClick={()=>openModal('apply')}></Button>
<Button type="primary" onClick={()=>openModal('apply')}>{$t('申请')}</Button>
</div>
</div>
</div>
@@ -151,16 +152,16 @@ const ServiceHubDetail = ()=>{
</section>
<section className="col-span-1 p-btnbase px-btnrbase">
<Descriptions title="服务信息" column={1} size={'small'}>
<Descriptions.Item label="接入应用">{serviceBasicInfo?.appNum ?? '-'}</Descriptions.Item>
<Descriptions.Item label="供应方">{serviceBasicInfo?.team?.name || '-'}</Descriptions.Item>
<Descriptions.Item label="分类">{serviceBasicInfo?.catalogue?.name || '-'}</Descriptions.Item>
<Descriptions.Item label="标签">{serviceBasicInfo?.tags?.map(x=>x.name)?.join(',') || '-'}</Descriptions.Item>
<Descriptions title={$t("服务信息")} column={1} size={'small'}>
<Descriptions.Item label={$t("接入应用")}>{serviceBasicInfo?.appNum ?? '-'}</Descriptions.Item>
<Descriptions.Item label={$t("供应方")}>{serviceBasicInfo?.team?.name || '-'}</Descriptions.Item>
<Descriptions.Item label={$t("分类")}>{serviceBasicInfo?.catalogue?.name || '-'}</Descriptions.Item>
<Descriptions.Item label={$t("标签")}>{serviceBasicInfo?.tags?.map(x=>x.name)?.join(',') || '-'}</Descriptions.Item>
</Descriptions>
<Divider />
<Descriptions column={1} >
<Descriptions.Item label="版本">{ serviceBasicInfo?.version || '-'}</Descriptions.Item>
<Descriptions.Item label="更新时间"><span className="truncate" title={serviceBasicInfo?.updateTime}>{serviceBasicInfo?.updateTime || '-'}</span></Descriptions.Item>
<Descriptions.Item label={$t("版本")}>{ serviceBasicInfo?.version || '-'}</Descriptions.Item>
<Descriptions.Item label={$t("更新时间")}><span className="truncate" title={serviceBasicInfo?.updateTime}>{serviceBasicInfo?.updateTime || '-'}</span></Descriptions.Item>
</Descriptions>
</section>
</section>
@@ -3,11 +3,12 @@ import {SearchOutlined} from "@ant-design/icons";
import {App, Divider, Input, TreeDataNode} from "antd";
import {useCallback, useEffect, useState} from "react";
import Tree, {DataNode} from "antd/es/tree";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {BasicResponse, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts";
import { CategorizesType } from "../../const/serviceHub/type.ts";
import { filterServiceList, initialServiceHubListState, SERVICE_HUB_LIST_ACTIONS, ServiceHubListActionType } from "./ServiceHubList.tsx";
import { EntityItem } from "@common/const/type.ts";
import { $t } from "@common/locales/index.ts";
type ServiceHubGroup = {
children:JSX.Element
@@ -35,11 +36,11 @@ export const ServiceHubGroup = ({children,filterOption,dispatch}:ServiceHubGroup
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
dispatch({type:SERVICE_HUB_LIST_ACTIONS.GET_CATEGORIES,payload:data.catalogues})
dispatch({type:SERVICE_HUB_LIST_ACTIONS.GET_TAGS,payload:[...data.tags,{id:'empty',name:'无标签'}]})
dispatch({type:SERVICE_HUB_LIST_ACTIONS.GET_TAGS,payload:[...data.tags,{id:'empty',name:$t('无标签')}]})
dispatch({type:SERVICE_HUB_LIST_ACTIONS.SET_SELECTED_CATE,payload:[...data.catalogues.map((x:CategorizesType)=>x.id)]})
dispatch({type:SERVICE_HUB_LIST_ACTIONS.SET_SELECTED_TAG,payload:[...data.tags.map((x:EntityItem)=>x.id),'empty']})
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
})
}
@@ -75,11 +76,11 @@ export const ServiceHubGroup = ({children,filterOption,dispatch}:ServiceHubGroup
<div className="w-[220px] border-0 border-solid border-r-[1px] border-r-BORDER">
<div className=" h-full">
<Input className="rounded-SEARCH_RADIUS m-[10px] h-[40px] bg-[#f8f8f8] w-[200px]" onChange={(e) => debounce(onSearchWordChange, 500)(e.target.value)}
allowClear placeholder="搜索服务"
allowClear placeholder={$t("搜索服务")}
prefix={<SearchOutlined className="cursor-pointer"/>}/>
<div className="h-[calc(100%-60px)] overflow-auto">
<div className="mt-[20px] ml-[20px] pr-[10px] ">
<p className="text-[18px] h-[25px] leading-[25px] font-bold mb-[15px]"></p>
<p className="text-[18px] h-[25px] leading-[25px] font-bold mb-[15px]">{$t('分类')}</p>
<Tree
className={`no-selected-tree ${transferToTreeData(filterOption.categoriesList).filter(x=>x.children && x.children.length > 0).length > 0 ? '' : 'no-first-switch-tree'}`}
checkable
@@ -93,7 +94,7 @@ export const ServiceHubGroup = ({children,filterOption,dispatch}:ServiceHubGroup
</div>
<Divider className="my-[20px]" />
<div className="ml-[20px] pr-[10px]">
<p className="text-[18px] h-[25px] leading-[25px] font-bold mb-[15px]"></p>
<p className="text-[18px] h-[25px] leading-[25px] font-bold mb-[15px]">{$t('标签')}</p>
<Tree
className="no-first-switch-tree no-selected-tree"
checkable
@@ -3,7 +3,7 @@ import {FC, forwardRef, useEffect, useReducer, useRef} from "react";
import { useNavigate, useParams} from "react-router-dom";
import {App,Card, Avatar, Tag, Empty, Spin, Tooltip} from "antd";
import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {BasicResponse, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts";
import {RouterParams} from "@core/components/aoplatform/RenderRoutes.tsx";
import { CategorizesType, ServiceHubTableListItem } from "../../const/serviceHub/type.ts";
@@ -11,6 +11,7 @@ import { VirtuosoGrid } from 'react-virtuoso';
import { ApiOutlined,LoadingOutlined } from "@ant-design/icons";
import ServiceHubGroup from "./ServiceHubGroup.tsx";
import { EntityItem } from "@common/const/type.ts";
import { $t } from "@common/locales/index.ts";
export enum SERVICE_HUB_LIST_ACTIONS {
GET_CATEGORIES = 'GET_CATEGORIES',
@@ -102,7 +103,7 @@ const ServiceHubList:FC = ()=>{
dispatch({type:SERVICE_HUB_LIST_ACTIONS.SET_SERVICES,payload: filterServiceList({...filterOption, servicesList:data.services})})
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
}).finally(()=>{ dispatch({type:SERVICE_HUB_LIST_ACTIONS.LIST_LOADING,payload:false})})
}
@@ -117,7 +118,7 @@ const ServiceHubList:FC = ()=>{
useEffect(() => {
setBreadcrumb(
[
{title:'服务市场'}
{title:$t('服务市场')}
]
)
getServiceList()
@@ -137,7 +138,7 @@ const ServiceHubList:FC = ()=>{
<div className="pt-[20px]">
<Card title={CardTitle(item)} className="shadow-[0_5px_10px_0_rgba(0,0,0,0.05)] rounded-[10px] overflow-visible cursor-pointer h-[180px] m-0 transition duration-500 hover:shadow-[0_5px_20px_0_rgba(0,0,0,0.15)] hover:scale-[1.05]" classNames={{header:'border-b-[0px] p-[20px] ', body:"pt-0"}} onClick={()=>showDocumentDetail(item)}>
<span className="line-clamp-3 text-[12px] text-[#666] "
style={{'word-break':'auto-phrase'}}>{item.description || '暂无服务描述'}</span>
style={{'word-break':'auto-phrase'}}>{item.description || $t('暂无服务描述')}</span>
</Card>
</div>
);
@@ -182,10 +183,10 @@ const CardTitle = (service:ServiceHubTableListItem)=>{
<div className="mt-[10px] h-[20px] flex items-center font-normal">
<Tag color="#7371fc1b" className="text-theme font-normal border-0 mr-[12px] max-w-[150px] truncate" key={service.id} bordered={false} title={service.catalogue?.name || '-'}>{service.catalogue?.name || '-'}</Tag>
<Tooltip title='API 数量'>
<Tooltip title={$t('API 数量')}>
<span className="mr-[12px]"><ApiOutlined className="mr-[1px] text-[14px] h-[14px] w-[14px]"/><span className="font-normal text-[14px]">{service.apiNum ?? '-'}</span></span>
</Tooltip>
<Tooltip title='接入应用数量'>
<Tooltip title={$t('接入应用数量')}>
<span className="mr-[12px] flex items-center"><span className="h-[14px] mr-[4px] flex items-center"><iconpark-icon className="max-h-[14px] h-[14px] w-[14px]" name="auto-generate-api"></iconpark-icon></span><span className="font-normal text-[14px]">{service.subscriberNum ?? '-'}</span></span>
</Tooltip>
</div>
@@ -2,10 +2,11 @@
import { App, Form, Row, Col, Input } from "antd"
import { forwardRef, useImperativeHandle, useEffect } from "react"
import WithPermission from "@common/components/aoplatform/WithPermission"
import { BasicResponse, STATUS_CODE } from "@common/const/const"
import { BasicResponse, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const"
import { useFetch } from "@common/hooks/http"
import { SYSTEM_SUBSCRIBE_APPROVAL_DETAIL_LIST } from "@core/const/system/const"
import { SubSubscribeApprovalModalHandle, SubSubscribeApprovalModalProps } from "@core/const/system/type"
import { $t } from "@common/locales"
type FieldType = {
reason: string
@@ -28,11 +29,11 @@ export const ApprovalModalContent = forwardRef<SubSubscribeApprovalModalHandle,S
fetchData<BasicResponse<null>>('catalogue/service/subscribe',{method: 'POST',eoParams:{team:teamId}, eoBody:({service:data!.service.id, applications:[serviceId], reason:value.reason})}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
@@ -59,8 +60,6 @@ export const ApprovalModalContent = forwardRef<SubSubscribeApprovalModalHandle,S
form={form}
className="mx-auto "
name="subSubscribeApprovalDetailModalContent"
// labelCol={{ span: 8 }}
// wrapperCol={{ span: 12}}
autoComplete="off"
disabled={type === 'view'}
>
@@ -69,22 +68,21 @@ export const ApprovalModalContent = forwardRef<SubSubscribeApprovalModalHandle,S
return (
<Row key={x.key} className="leading-[32px] mb-btnbase">
<Col className="text-left" span={8}>{x.title}</Col>
{/* <Col >{showData(x)}</Col> */}
<Col >{x.nested ? data?.[x.key]?.[x.nested] : ( (data as {[k:string]:unknown})?.[x.key] || '-')}</Col>
</Row>)
})}
<Form.Item<FieldType>
label="申请原因"
label={$t("申请原因")}
name="reason"
>
<Input.TextArea className="w-INPUT_NORMAL" disabled={type === 'view'} placeholder="请输入" />
<Input.TextArea className="w-INPUT_NORMAL" disabled={type === 'view'} placeholder={PLACEHOLDER.input} />
</Form.Item>
<Form.Item<FieldType>
label="审核意见"
label={$t("审核意见")}
name="opinion"
>
<Input.TextArea className="w-INPUT_NORMAL" placeholder="请输入" disabled={true} />
<Input.TextArea className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input} disabled={true} />
</Form.Item>
</Form>
</WithPermission>
@@ -1,13 +1,14 @@
import { RouterParams } from "@core/components/aoplatform/RenderRoutes";
import { useParams } from "react-router-dom";
import ManagementConfig from "./ManagementConfig";
import { $t } from "@common/locales";
export default function ManagementAppSetting(){
const {teamId,appId} = useParams<RouterParams>()
return (
<div className=" h-full pt-[32px]">
<div className="flex items-center justify-between w-full ml-[10px] text-[18px] leading-[25px] pb-[16px]" ><span className="font-bold"></span></div>
<div className="flex items-center justify-between w-full ml-[10px] text-[18px] leading-[25px] pb-[16px]" ><span className="font-bold">{$t('应用管理')}</span></div>
<div className="h-[calc(100%-41px)] flex flex-col ">
<ManagementConfig type='edit' teamId={teamId!} appId={appId!}/>
</div>
@@ -1,13 +1,13 @@
import {forwardRef, useEffect, useImperativeHandle, useState} from "react";
import {App, Checkbox, Form, Input, Select,Switch} from "antd";
import moment from "moment";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {App, Checkbox, DatePicker, Form, Input, Select,Switch} from "antd";
import {BasicResponse, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE, VALIDATE_MESSAGE} from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts";
import DatePicker from "@common/components/aoplatform/DatePicker.tsx";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { v4 as uuidv4} from 'uuid';
import { ALGORITHM_ITEM } from "@core/const/system/const.tsx";
import { EditAuthFieldType } from "@core/const/system/type";
import { RangePickerProps } from "antd/es/date-picker";
import dayjs from 'dayjs';
import { $t } from "@common/locales";
export type ManagementAuthorityConfigProps = {
type:'add'|'edit'
@@ -35,11 +35,11 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
fetchData<BasicResponse<null>>('app/authorization',{method:type === 'add'? 'POST' : 'PUT',eoBody:({...value,expireTime:value.expireTime ? value.expireTime.unix() : 0}), eoParams:type === 'add' ? {app:appId,team:teamId}:{authorization:data!.id,app:appId,team:teamId},eoTransformKeys:['hideCredential','expireTime','tokenName','userName']}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
@@ -70,18 +70,17 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
}
const disabledDate = (current: moment.Moment | null): boolean => {
// 禁用今天以前的日期,包括今天
// 使用current?.startOf('day')是为了获取日期的开始时间点,以确保整个今天都被禁用
// 如果只需要禁用今天之前的日期(今天可选),则可以将`isBefore`的第二个参数设置为 'day'
return current ? current.startOf('day') < moment().startOf('day') : false;
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
// Can not select days before today and today
return current && current.isBefore(dayjs().endOf('day'), 'day');
};
useEffect(() => {
//console.log(data)
if(type === 'edit' && data){
form.setFieldsValue({...data,expireTime:data.expireTime === 0 ? '' : moment(data.expireTime *1000)})
form.setFieldsValue({...data,expireTime:data.expireTime === 0 ? '' : dayjs(data.expireTime * 1000)})
forceUpdate({})
}else{
form.setFieldsValue({driver, position:'Header',tokenName:'Authorization'})
@@ -106,29 +105,29 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
autoComplete="off"
>
<Form.Item<EditAuthFieldType>
label="名称"
label={$t("名称")}
name="name"
rules={[{required: true, message: '必填项',whitespace:true }]}
rules={[{required: true, message: VALIDATE_MESSAGE.required,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入鉴权名称"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
<Form.Item<EditAuthFieldType>
label="鉴权类型"
label={$t("鉴权类型")}
name="driver"
rules={[{required: true, message: '必填项'}]}
rules={[{required: true, message: VALIDATE_MESSAGE.required}]}
>
<Select disabled={type === 'edit'} className="w-INPUT_NORMAL" options={[
{label:'Basic',value:'basic'},
{label:'Jwt',value:'jwt'},
{label:'AkSk',value:'aksk'},
{label:'Apikey',value:'apikey'}]} onChange={(e)=>onDriverChange(e)} placeholder="请输入鉴权类型"/>
{label:'Apikey',value:'apikey'}]} onChange={(e)=>onDriverChange(e)} placeholder={PLACEHOLDER.input}/>
</Form.Item>
<Form.Item
name="tokenName"
label="参数位置"
rules={[{required: true, message: '必填项',whitespace:true }]}
label={$t("参数位置")}
rules={[{required: true, message: VALIDATE_MESSAGE.required,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" addonBefore={prefixSelector} style={{width: '100%'}}/>
</Form.Item>
@@ -138,70 +137,70 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
case 'basic':
return <>
<Form.Item<EditAuthFieldType>
label="用户名"
label={$t("用户名")}
name={['config','userName']}
rules={[{required: true, message: '必填项',whitespace:true }]}
rules={[{required: true, message: VALIDATE_MESSAGE.required,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="英文数字下划线任意一种,首字母必须为英文"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.startWithAlphabet}/>
</Form.Item>
<Form.Item<EditAuthFieldType>
label="密码"
label={$t("密码")}
name={['config','password']}
rules={[{required: true, message: '必填项',whitespace:true }]}
rules={[{required: true, message: VALIDATE_MESSAGE.required,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
</>
case 'jwt':
return <>
<Form.Item<EditAuthFieldType>
label="Iss"
label={$t("Iss")}
name={['config','iss']}
rules={[{required: true, message: '必填项'}]}
rules={[{required: true, message: VALIDATE_MESSAGE.required}]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
<Form.Item<EditAuthFieldType>
label="签名算法"
label={$t("签名算法")}
name={['config','algorithm']}
rules={[{required: true, message: '必填项'}]}
rules={[{required: true, message: VALIDATE_MESSAGE.required}]}
>
<Select className="w-INPUT_NORMAL" options={ALGORITHM_ITEM} onChange={(value)=>{onAlgorithmChange(value)}}/>
</Form.Item>
<Form.Item<EditAuthFieldType>
label={ algorithm.includes('HS') ? 'Secret':'RSA 公钥'}
label={ algorithm.includes('HS') ? $t('Secret'):$t('RSA 公钥')}
name={algorithm.includes('HS') ? ['config','secret']:['config','publicKey']}
rules={[{required: true, message: '必填项'}]}
rules={[{required: true, message: VALIDATE_MESSAGE.required}]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
<Form.Item<EditAuthFieldType>
label="用户名"
label={$t("用户名")}
name={['config','user']}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
<Form.Item<EditAuthFieldType>
label="用户名 JsonPath"
label={$t("用户名 JsonPath")}
name={['config','userPath']}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
<Form.Item<EditAuthFieldType>
label="校验字段"
label={$t("校验字段")}
name={['config','claimsToVerify']}
>
<Select className="w-INPUT_NORMAL" mode="multiple" options={[{label:'exp',value:'exp'},{label:'nbf',value:'nbf'}]} placeholder="请输入"/>
<Select className="w-INPUT_NORMAL" mode="multiple" options={[{label:'exp',value:'exp'},{label:'nbf',value:'nbf'}]} placeholder={PLACEHOLDER.input}/>
</Form.Item>
{algorithm.includes('HS') && <Form.Item<EditAuthFieldType>
label="是否 Base64 加密"
label={$t("是否 Base64 加密")}
name={['config', 'signatureIsBase64']}
>
<Switch />
@@ -210,29 +209,29 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
case 'aksk':
return <>
<Form.Item<EditAuthFieldType>
label="AK"
label={$t("AK")}
name={['config','ak']}
rules={[{required: true, message: '必填项'}]}
rules={[{required: true, message: VALIDATE_MESSAGE.required}]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
<Form.Item<EditAuthFieldType>
label="SK"
label={$t("SK")}
name={['config','sk']}
rules={[{required: true, message: '必填项'}]}
rules={[{required: true, message: VALIDATE_MESSAGE.required}]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
</>
case 'apikey':
return <>
<Form.Item<EditAuthFieldType>
label="Apikey"
label={$t("Apikey")}
name={['config','apikey']}
rules={[{required: true, message: '必填项',whitespace:true }]}
rules={[{required: true, message: VALIDATE_MESSAGE.required,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
</>
}
@@ -240,7 +239,7 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
})()}
<Form.Item<EditAuthFieldType>
label="过期时间"
label={$t("过期时间")}
name="expireTime"
>
<DatePicker
@@ -250,7 +249,7 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
</Form.Item>
<Form.Item<EditAuthFieldType>
label="隐藏鉴权信息"
label={$t("隐藏鉴权信息")}
name="hideCredential" valuePropName="checked"
>
<Checkbox/>
@@ -1,12 +1,13 @@
import {App, Button, Divider, Form, Input, Row} from "antd";
import {App, Button, Form, Input, Row} from "antd";
import {forwardRef, useEffect, useImperativeHandle, useState} from "react";
import {v4 as uuidv4} from 'uuid'
import WithPermission from "@common/components/aoplatform/WithPermission";
import { BasicResponse, STATUS_CODE } from "@common/const/const";
import { BasicResponse, DELETE_TIPS, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE, VALIDATE_MESSAGE } from "@common/const/const";
import { useFetch } from "@common/hooks/http";
import { useNavigate } from "react-router-dom";
import { useTenantManagementContext } from "@market/contexts/TenantManagementContext";
import { $t } from "@common/locales";
export type ManagementConfigFieldType = {
name:string
@@ -41,13 +42,13 @@ const ManagementConfig = forwardRef<ManagementConfigHandle,ManagementConfigProps
fetchData<BasicResponse<{apps:ManagementConfigFieldType}>>(type === 'add'? 'team/app' : 'app/info',{method:type === 'add'? 'POST' : 'PUT',eoBody:(value), eoParams:type === 'add' ? {team:teamId}:{app:appId,team:teamId}}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
form.setFieldsValue(data.apps)
type === 'edit' && setAppName(data.apps.name)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
@@ -62,7 +63,7 @@ const ManagementConfig = forwardRef<ManagementConfigHandle,ManagementConfigProps
setAppName(data.app.name)
setTimeout(()=>{form.setFieldsValue({...data.app})},0)
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
})
};
@@ -70,20 +71,20 @@ const ManagementConfig = forwardRef<ManagementConfigHandle,ManagementConfigProps
const deleteApplicationModal = async ()=>{
setDelBtnLoading(true)
modal.confirm({
title:'删除',
content:'该数据删除后将无法找回,请确认是否删除?',
title:$t('删除'),
content:DELETE_TIPS.default,
onOk:()=> {
return deleteApplication()
},
width:600,
okText:'确认',
okText:$t('确认'),
okButtonProps:{
danger:true
},
onCancel:()=>{
setDelBtnLoading(false)
},
cancelText:'取消',
cancelText:$t('取消'),
closable:true,
icon:<></>
})
@@ -94,10 +95,10 @@ const ManagementConfig = forwardRef<ManagementConfigHandle,ManagementConfigProps
fetchData<BasicResponse<null>>('app',{method:'DELETE',eoParams:{app:appId,team:teamId}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
navigate(`/tenantManagement/list`)
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
})
}
@@ -125,41 +126,38 @@ const ManagementConfig = forwardRef<ManagementConfigHandle,ManagementConfigProps
form={form}
className="mx-auto w-full pb-[20px]"
name="managementConfig"
// labelCol={{ offset:1, span: 4 }}
// wrapperCol={{ span: 19}}
autoComplete="off"
onFinish={save}
>
<div>
<Form.Item<ManagementConfigFieldType>
label="应用名称"
label={$t("应用名称")}
name="name"
rules={[{ required: true, message: '必填项',whitespace:true }]}
rules={[{ required: true, message: VALIDATE_MESSAGE.required,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
<Form.Item<ManagementConfigFieldType>
label="应用 ID"
label={$t("应用 ID")}
name="id"
rules={[{ required: true, message: '必填项' ,whitespace:true }]}
rules={[{ required: true, message: VALIDATE_MESSAGE.required ,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入" disabled={type === 'edit'}/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input} disabled={type === 'edit'}/>
</Form.Item>
<Form.Item
label="描述"
label={$t("描述")}
name="description"
>
<Input.TextArea className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input.TextArea className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
{type === 'edit' && <>
<Row className="mb-[10px]"
// wrapperCol={{ offset: 5, span: 19 }}
>
<WithPermission access={type === 'edit'? 'team.application.application.edit' :'team.application.application.add'}>
<Button type="primary" htmlType="submit">
{$t('保存')}
</Button>
</WithPermission>
</Row> </>
@@ -169,10 +167,10 @@ const ManagementConfig = forwardRef<ManagementConfigHandle,ManagementConfigProps
<WithPermission access="team.application.application.delete" showDisabled={false}>
<div className="bg-[rgb(255_120_117_/_5%)] rounded-[10px] mt-[50px] p-btnrbase pb-0">
<p className="text-left"><span className="font-bold"></span></p>
<p className="text-left"><span className="font-bold">{$t('删除应用')}</span>{$t('删除操作不可恢复,请谨慎操作!')}</p>
<div className="text-left">
<WithPermission access="team.application.application.delete">
<Button className="m-auto mt-[16px] mb-[20px]" type="default" danger={true} onClick={deleteApplicationModal} loading={delBtnLoading}></Button>
<Button className="m-auto mt-[16px] mb-[20px]" type="default" danger={true} onClick={deleteApplicationModal} loading={delBtnLoading}>{$t('删除应用')}</Button>
</WithPermission>
</div>
</div>
@@ -2,18 +2,18 @@ import { MoreOutlined } from "@ant-design/icons"
import { message, Card, Button, Tag, Dropdown, App, Empty } from "antd"
import { useState, useEffect, forwardRef, useRef } from "react"
import { VirtuosoGrid } from "react-virtuoso"
import { BasicResponse, STATUS_CODE } from "@common/const/const"
import { useBreadcrumb } from "@common/contexts/BreadcrumbContext"
import { BasicResponse, DELETE_TIPS, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const"
import { useFetch } from "@common/hooks/http"
import { EditAuthFieldType, SystemAuthorityTableListItem } from "@core/const/system/type"
import { Link, useParams } from "react-router-dom"
import { useParams } from "react-router-dom"
import { RouterParams } from "@core/components/aoplatform/RenderRoutes"
import moment from "moment"
import { useTenantManagementContext } from "../../../contexts/TenantManagementContext"
import { ManagementAuthorityConfig, ManagementAuthorityConfigHandle } from "./ManagementAuthorityConfig"
import { ManagementAuthorityView } from "./ManagementAuthorityView"
import { checkAccess } from "@common/utils/permission"
import { useGlobalContext } from "@common/contexts/GlobalStateContext"
import dayjs from 'dayjs';
import { $t } from "@common/locales"
export default function ManagementInsideAuth(){
const {modal} = App.useApp()
@@ -22,7 +22,6 @@ export default function ManagementInsideAuth(){
const {appId,teamId} = useParams<RouterParams>()
const addRef = useRef<ManagementAuthorityConfigHandle>(null)
const editRef = useRef<ManagementAuthorityConfigHandle>(null)
const {appName} = useTenantManagementContext()
const {accessData} = useGlobalContext()
@@ -32,7 +31,7 @@ export default function ManagementInsideAuth(){
if(code === STATUS_CODE.SUCCESS){
setAuthList(data.authorizations)
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
}).catch(() => {
return {data:[], success:false}
@@ -49,11 +48,11 @@ export default function ManagementInsideAuth(){
fetchData<BasicResponse<null>>('app/authorization',{method:'DELETE',eoParams:{authorization:entity!.id,app:appId, team:teamId}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
})
@@ -65,36 +64,36 @@ export default function ManagementInsideAuth(){
let content:string|React.ReactNode = ''
switch (type){
case 'view':{
title='鉴权详情'
message.loading('正在加载数据')
title=$t('鉴权详情')
message.loading(RESPONSE_TIPS.loading)
const {code,data,msg} = await fetchData<BasicResponse<{details:{[k:string]:string}}>>('app/authorization/details',{method:'GET',eoParams:{authorization:entity!.id,app:appId, team:teamId}})
message.destroy()
if(code === STATUS_CODE.SUCCESS){
content=<ManagementAuthorityView entity={data.details}/>
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
return
}}
break;
case 'add':
title='添加鉴权'
title=$t('添加鉴权')
content=<ManagementAuthorityConfig ref={addRef} type={type} appId={appId!} teamId={teamId!}/>
break;
case 'edit':{
title='编辑鉴权'
message.loading('正在加载数据')
title=$t('编辑鉴权')
message.loading(RESPONSE_TIPS.loading)
const {code,data,msg} = await fetchData<BasicResponse<{authorization:EditAuthFieldType}>>('app/authorization',{method:'GET',eoParams:{authorization:entity!.id,app:appId, team:teamId},eoTransformKeys:['hide_credential','token_name','expire_time','user_name','public_key','user_path','claims_to_verify','signature_is_base64']})
message.destroy()
if(code === STATUS_CODE.SUCCESS){
content=<ManagementAuthorityConfig ref={editRef} type={type} data={data.authorization} appId={appId!} teamId={teamId!}/>
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
return
}}
break;
case 'delete':
title='删除'
content='该数据删除后将无法找回,请确认是否删除?'
title=$t('删除')
content=DELETE_TIPS.default
break;
}
@@ -114,11 +113,11 @@ export default function ManagementInsideAuth(){
}
},
width:600,
okText: '确认',
okText: $t('确认'),
okButtonProps:{
disabled : !checkAccess( `team.application.authorization.${type}`, accessData)
},
cancelText:type === 'view'? '关闭':'取消',
cancelText:type === 'view'? $t('关闭'):$t('取消'),
closable:true,
icon:<></>,
footer:(_, { OkBtn, CancelBtn }) =>{
@@ -136,28 +135,24 @@ export default function ManagementInsideAuth(){
{
key: 'edit',
label: (
// <WithPermission access="system.organization.member.department.add" key="addChildPermission">
<Button key="edit" type="text" className="h-[32px] border-none p-0 flex items-center bg-transparent " onClick={()=>openModal('edit',entity)}>
{$t('修改')}
</Button>
// </WithPermission>
),
},
{
key: 'delete',
label: (
// <WithPermission access="system.organization.member.department.delete" key="deletePermission">
<Button key="delete" type="text" className="h-[32px] border-none p-0 flex items-center bg-transparent " onClick={()=>openModal('delete',entity)}>
{$t('删除')}
</Button>
// </WithPermission>
),
},
]
return (<div className=" h-full pt-[32px]">
<div className="flex items-center justify-between w-full ml-[10px] text-[18px] leading-[25px] pb-[16px]" ><span className="font-bold">访</span></div>
<div><Button className="mb-[20px] ml-[10px]" type="primary" onClick={()=>openModal('add')}></Button></div>
<div className="flex items-center justify-between w-full ml-[10px] text-[18px] leading-[25px] pb-[16px]" ><span className="font-bold">{$t('访问授权')}</span></div>
<div><Button className="mb-[20px] ml-[10px]" type="primary" onClick={()=>openModal('add')}>{$t('添加授权')}</Button></div>
{authList && authList.length > 0 ? <VirtuosoGrid
style={{ height: 'calc(100% - 93px)'}}
data={authList}
@@ -166,12 +161,12 @@ export default function ManagementInsideAuth(){
const item = authList[index];
return (<Card className="shadow-[0_5px_10px_0_rgba(0,0,0,0.05)] rounded-[10px] m-[10px]" classNames={{body:' flex items-center justify-center p-[20px]'}} >
<div className="w-full">
<div className="flex items-center justify-between w-full"><span>{item.name}</span><div><Button type="text" className="bg-[#7371fc20] hover:bg-[#7371fc19] text-theme" onClick={()=>openModal('view',item)}></Button><Dropdown className="ml-btnbase" menu={{items:dropdownMenu(item)}} trigger={['hover']} >
<div className="flex items-center justify-between w-full"><span>{item.name}</span><div><Button type="text" className="bg-[#7371fc20] hover:bg-[#7371fc19] text-theme" onClick={()=>openModal('view',item)}>{$t('查看')}</Button><Dropdown className="ml-btnbase" menu={{items:dropdownMenu(item)}} trigger={['hover']} >
<Button type="text" className="px-[7px]" onClick={(e)=>{ e.stopPropagation();}}><MoreOutlined rotate={90} className="tree-title-more" /></Button>
</Dropdown></div></div>
<div>
<Tag bordered={false} color="orange">{`${item.driver.substring(0,1).toLocaleUpperCase()}${item.driver.substring(1)}`}</Tag>
<Tag bordered={false}>{item.expireTime === 0 ? '永不过期' : `到期时间${moment(item.expireTime * 1000).format('YYYY-MM-DD hh:mm:ss')}`}</Tag></div>
<Tag bordered={false}>{item.expireTime === 0 ?$t('永不过期') : `${$t('到期时间')}${dayjs(item.expireTime * 1000).format('YYYY-MM-DD hh:mm:ss')}`}</Tag></div>
</div>
</Card>
);
@@ -183,9 +178,6 @@ export default function ManagementInsideAuth(){
{...props}
style={{
display: 'grid',
// gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',
// gap: '20px',
// padding:'30px',
...style,
}}
>
@@ -12,6 +12,7 @@ import { RouterParams } from "@core/components/aoplatform/RenderRoutes";
import { useTenantManagementContext } from "@market/contexts/TenantManagementContext";
import { ManagementConfigFieldType } from "./ManagementConfig";
import { useGlobalContext } from "@common/contexts/GlobalStateContext";
import { $t } from "@common/locales";
export default function ManagementInsidePage(){
const { message } = App.useApp()
@@ -51,7 +52,7 @@ export default function ManagementInsidePage(){
}
setBreadcrumb(
[
{title:<Link to={`/tenantManagement/list/${teamId}`}></Link>},
{title:<Link to={`/tenantManagement/list/${teamId}`}>{$t('应用')}</Link>},
...(_appName ? [{title:_appName}] : [])
]
)
@@ -75,7 +76,7 @@ useEffect(()=>{
<div className="flex flex-1 h-full">
<div className="w-[220px] border-0 border-solid border-r-[1px] border-r-BORDER">
<div className="text-[18px] leading-[25px] pl-[12px] py-[12px]">
<Button type="text" onClick={()=>navigateTo(`/tenantManagement/list/${teamId}`)}><ArrowLeftOutlined className="max-h-[14px]" /></Button>
<Button type="text" onClick={()=>navigateTo(`/tenantManagement/list/${teamId}`)}><ArrowLeftOutlined className="max-h-[14px]" />{$t('返回')}</Button>
</div>
<Menu
onClick={onMenuClick}
@@ -3,45 +3,40 @@ import { Card, Input,Button ,Dropdown,App, Tag, Empty } from "antd"
import { debounce } from "lodash-es"
import { forwardRef, useEffect, useState } from "react"
import { VirtuosoGrid } from "react-virtuoso"
import { BasicResponse, STATUS_CODE } from "@common/const/const"
import { useBreadcrumb } from "@common/contexts/BreadcrumbContext"
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const"
import { useFetch } from "@common/hooks/http"
import { SubscribeApprovalInfoType } from "@common/const/approval/type"
import { Link, useNavigate, useOutletContext, useParams } from "react-router-dom"
import { useOutletContext, useParams } from "react-router-dom"
import { RouterParams } from "@core/components/aoplatform/RenderRoutes"
import { TenantManagementServiceListItem } from "../../../const/serviceHub/type"
import { useTenantManagementContext } from "../../../contexts/TenantManagementContext"
import { ApprovalModalContent } from "./ApprovalModalContent"
import { checkAccess } from "@common/utils/permission"
import { useGlobalContext } from "@common/contexts/GlobalStateContext"
import { $t } from "@common/locales"
export default function ManagementInsideService(){
const {message, modal} = App.useApp()
const [serviceList, setServiceList] = useState<TenantManagementServiceListItem[]>([])
const {fetchData} = useFetch()
const { setBreadcrumb} = useBreadcrumb()
const {teamId,appId} = useParams<RouterParams>()
const navigateTo = useNavigate()
const [keyword, setKeyword] = useState<string>('')
const { refreshGroup} = useOutletContext<{refreshGroup:()=>void,appName:string}>()
const {appName} = useTenantManagementContext()
const { refreshGroup} = useOutletContext<{refreshGroup:()=>void}>()
const {accessData} = useGlobalContext()
const onSearchWordChange = (e)=>{
setKeyword(e.target.value)
}
const cancelSubscribeApply = (entity:TenantManagementServiceListItem) => {
return new Promise((resolve, reject)=>{
fetchData<BasicResponse<null>>('application/subscription/cancel_apply',{method:'POST',eoParams:{subscription:entity.id!,application:appId!,team:teamId}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
})
@@ -52,11 +47,11 @@ export default function ManagementInsideService(){
fetchData<BasicResponse<null>>('application/subscription/cancel',{method:'POST',eoParams:{subscription:entity.id!,application:appId!,team:teamId}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
})
@@ -67,25 +62,25 @@ export default function ManagementInsideService(){
let content:string|React.ReactNode = ''
switch (type){
case 'view':{
message.loading('正在加载数据')
message.loading(RESPONSE_TIPS.loading)
const {code,data,msg} = await fetchData<BasicResponse<{approval:SubscribeApprovalInfoType}>>('app/subscription/approval',{method:'GET',eoParams:{subscription:entity!.id, app:appId,team:teamId},eoTransformKeys:['apply_project','apply_team','apply_time','approval_time']})
message.destroy()
if(code === STATUS_CODE.SUCCESS){
title='审批详情'
title=$t('审批详情')
content = <ApprovalModalContent data={data.approval} type={type} systemId={appId}/>;
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
return
}
break;
}
case 'cancelSub':
title='取消订阅'
content='请确认是否取消订阅?'
title=$t('取消订阅')
content=$t('请确认是否取消订阅?')
break;
case 'cancelSubApply':
title='取消订阅申请'
content='请确认是否取消订阅申请?'
title=$t('取消订阅申请')
content=$t('请确认是否取消订阅申请?')
break;
}
@@ -103,11 +98,11 @@ export default function ManagementInsideService(){
}
},
width:600,
okText:'确认',
okText:$t('确认'),
okButtonProps:{
disabled : !checkAccess( `team.application.authorization.${type}`, accessData)
},
cancelText:'取消',
cancelText:$t('取消'),
closable:true,
icon:<></>,
})
@@ -130,7 +125,7 @@ export default function ManagementInsideService(){
label: (
// <WithPermission access="system.organization.member.department.delete" key="deletePermission">
<Button key="cancelSubApply" type="text" className="h-[32px] border-none p-0 flex items-center bg-transparent " onClick={()=>openModal('cancelSubApply',entity)}>
{$t('取消订阅申请')}
</Button>
// </WithPermission>
),
@@ -139,7 +134,7 @@ export default function ManagementInsideService(){
label: (
// <WithPermission access="system.organization.member.department.delete" key="deletePermission">
<Button key="cancelSub" type="text" className="h-[32px] border-none p-0 flex items-center bg-transparent " onClick={()=>openModal('cancelSub',entity)}>
{$t('取消订阅')}
</Button>
// </WithPermission>
),
@@ -153,7 +148,7 @@ export default function ManagementInsideService(){
setServiceList(data.subscriptions && data.subscriptions.length > 0 ? [...data.subscriptions] : [])
// return {data:data.services, success: true,total:data.total}
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
// return {data:[], success:false}
}
})
@@ -166,8 +161,8 @@ export default function ManagementInsideService(){
return (<div className=" mx-auto h-full pt-[32px]">
<div className="flex items-center justify-between w-full ml-[10px] text-[18px] leading-[25px] pb-[18px]" >
<span className="font-bold"></span>
<Input className="w-[200px] mr-[20px] rounded-[20px]" onChange={ onSearchWordChange ? (e) => debounce(onSearchWordChange, 100)(e) : undefined } onPressEnter={()=>getServiceList()} allowClear placeholder='搜索服务' prefix={<SearchOutlined className="cursor-pointer" onClick={()=>{getServiceList()}}/>}/>
<span className="font-bold">{$t('服务')}</span>
<Input className="w-[200px] mr-[20px] rounded-[20px]" onChange={ onSearchWordChange ? (e) => debounce(onSearchWordChange, 100)(e) : undefined } onPressEnter={()=>getServiceList()} allowClear placeholder={$t('搜索服务')} prefix={<SearchOutlined className="cursor-pointer" onClick={()=>{getServiceList()}}/>}/>
</div>
{ (keyword ? serviceList.filter(x=>x.service.name.includes(keyword)) :serviceList)?.length > 0 ?
<VirtuosoGrid
@@ -178,10 +173,10 @@ export default function ManagementInsideService(){
const item = (keyword ? serviceList.filter(x=>x.service.name.includes(keyword)) :serviceList)[index];
return (<Card className="shadow-[0_5px_10px_0_rgba(0,0,0,0.05)] rounded-[10px] m-[10px]" classNames={{body:' flex items-center justify-center'}} >
<div className="flex items-center justify-between w-full"><span><span>{item.service.name}</span>{ item.applyStatus === 1 &&
<Tag className="ml-[8px]" bordered={false} color="orange"></Tag>
<Tag className="ml-[8px]" bordered={false} color="orange">{$t('审批中')}</Tag>
}</span>
<div>
<Button type="text" className="bg-[#7371fc20] hover:bg-[#7371fc19] text-theme" onClick={()=>window.open(`/serviceHub/detail/${item.service.id}`,'_blank')}>API </Button>
<Button type="text" className="bg-[#7371fc20] hover:bg-[#7371fc19] text-theme" onClick={()=>window.open(`/serviceHub/detail/${item.service.id}`,'_blank')}>{$t('API 文档')}</Button>
<Dropdown className="ml-btnbase" menu={{items:dropdownMenu(item)}} trigger={['hover']} >
<Button type="text" className="px-[7px]" onClick={(e)=>{ e.stopPropagation();}}><MoreOutlined rotate={90} className="tree-title-more" onClick={(e)=>{ e.stopPropagation();}} /></Button></Dropdown></div></div>
</Card>
@@ -194,9 +189,6 @@ export default function ManagementInsideService(){
{...props}
style={{
display: 'grid',
// gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
// gap: '20px',
// padding:'30px',
...style,
}}
>
@@ -1,7 +1,7 @@
import { MenuProps, Menu, App, Avatar, Card, Tooltip, Empty } from "antd";
import { useState, forwardRef, useEffect, useRef } from "react";
import { VirtuosoGrid } from "react-virtuoso";
import { BasicResponse, STATUS_CODE } from "@common/const/const";
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
import { ServiceHubAppListItem } from "../../../const/serviceHub/type";
import { useFetch } from "@common/hooks/http";
import { useBreadcrumb } from "@common/contexts/BreadcrumbContext";
@@ -12,6 +12,7 @@ import { SimpleTeamItem } from "@common/const/type";
import { useTenantManagementContext } from "../../../contexts/TenantManagementContext";
import { Icon } from "@iconify/react/dist/iconify.js";
import { useGlobalContext } from "@common/contexts/GlobalStateContext";
import { $t } from "@common/locales";
export default function ServiceHubManagement() {
const { message ,modal} = App.useApp()
@@ -30,14 +31,13 @@ export default function ServiceHubManagement() {
const getServiceList = ()=>{
//console.log(pagination,sorter,categoryId,tagId)
setServiceLoading(true)
return fetchData<BasicResponse<{apps:ServiceHubAppListItem}>>(!checkPermission('system.workspace.application.view_all') ? 'my_apps':'apps',{method:'GET',eoParams:{ team:teamId,keyword:''},eoTransformKeys:['api_num','subscribe_num','subscribe_verify_num']}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setServiceList([...data.apps,{type:'addNewItem'}])
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
}).finally(()=>{
setServiceLoading(false)
@@ -59,7 +59,7 @@ const getServiceList = ()=>{
navigateTo(data.teams[0].id)
}
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
}).finally(()=>{
setPageLoading(false)
@@ -73,7 +73,7 @@ const getServiceList = ()=>{
let content:string|React.ReactNode = ''
switch (type){
case 'add':
title='添加应用'
title=$t('添加应用')
content=<ManagementConfig ref={addManagementRef} type={type} teamId={teamId!} />
break;
// case 'edit':{
@@ -84,7 +84,7 @@ const getServiceList = ()=>{
// if(code === STATUS_CODE.SUCCESS){
// content=<ManagementConfig ref={editManagementRef} type={type} entity={data.app}/>
// }else{
// message.error(msg || '操作失败')
// message.error(msg || RESPONSE_TIPS.error)
// return
// }
// break;}
@@ -108,8 +108,8 @@ const getServiceList = ()=>{
}
},
width:600,
okText:'确认',
cancelText:'取消',
okText:$t('确认'),
cancelText:$t('取消'),
closable:true,
icon:<></>,
})
@@ -128,7 +128,7 @@ useEffect(()=>{
useEffect(() => {
setBreadcrumb(
[
{title:'应用'}
{title:$t('应用')}
]
)
getTeamsList()
@@ -139,7 +139,7 @@ useEffect(() => {
teamList && teamList.length > 0 ?
<div className="flex flex-1 h-full">
<div className="w-[220px] border-0 border-solid border-r-[1px] border-r-BORDER h-full overflow-hidden">
<div className="text-[18px] leading-[25px] pl-[20px] pt-[20px] pb-[10px] font-bold"></div>
<div className="text-[18px] leading-[25px] pl-[20px] pt-[20px] pb-[10px] font-bold">{$t('团队')}</div>
<Menu
onClick={onClick}
style={{ width: 220}}
@@ -150,7 +150,7 @@ useEffect(() => {
/>
</div>
<div className="w-[calc(100%-220px)] padding-top-20">
<div className="mt-[20px] ml-[40px] text-[18px] leading-[25px] font-bold"></div>
<div className="mt-[20px] ml-[40px] text-[18px] leading-[25px] font-bold">{$t('应用')}</div>
<VirtuosoGrid
style={{ height: 'calc(100% - 45px)'}}
data={serviceList}
@@ -160,9 +160,9 @@ useEffect(() => {
return (
<div className="pt-[20px]">{
item.type === 'addNewItem' ?<Card className="shadow-[0_5px_10px_0_rgba(0,0,0,0.05)] rounded-[10px] overflow-visible cursor-pointer h-[180px] transition duration-500 hover:shadow-[0_5px_20px_0_rgba(0,0,0,0.15)] hover:scale-[1.05]" classNames={{body:'h-[180px] flex items-center justify-center cursor-pointer'}} onClick={()=>{openModal('add')}}>
<div className="flex items-center"><Icon icon="ic:baseline-add" width="18" height="18"/><span></span></div>
<div className="flex items-center"><Icon icon="ic:baseline-add" width="18" height="18"/><span>{$t('添加应用')}</span></div>
</Card> : <Card title={CardTitle(item)} className="shadow-[0_5px_10px_0_rgba(0,0,0,0.05)] rounded-[10px] overflow-visible cursor-pointer h-[180px] transition duration-500 hover:shadow-[0_5px_20px_0_rgba(0,0,0,0.15)] hover:scale-[1.05]" classNames={{header:'border-b-[0px] p-[20px] ', body:"pt-0"}} onClick={()=>{setAppName(item.name);navigateTo(`/tenantManagement/${teamId}/inside/${item.id}/service`)}}>
<span className="line-clamp-3 break-all">{item.description || '暂无服务描述'}</span>
<span className="line-clamp-3 break-all">{item.description || $t('暂无服务描述')}</span>
</Card>}</div>
);
@@ -204,7 +204,7 @@ const CardTitle = (service:ServiceHubAppListItem)=>{
<div className="pl-[20px] w-[calc(100%-50px)]">
<p className="text-[14px] h-[20px] leading-[20px] truncate">{service.name}</p>
<div className="mt-[10px] h-[20px] flex items-center font-normal">
<Tooltip title={`订阅的服务数量:已通过 ${service.subscribeNum ?? '-'} 个,申请中 ${service.subscribeVerifyNum ?? '-'}`}>
<Tooltip title={$t('订阅的服务数量:已通过 (0) 个,申请中 (1) 个',[service.subscribeNum ?? '-', service.subscribeVerifyNum ?? '-'])}>
<span className="mr-[12px] flex items-center"><span className="h-[14px] mr-[4px] flex items-center"><iconpark-icon className="max-h-[14px] h-[14px] w-[14px]" name="auto-generate-api"></iconpark-icon></span><span className="font-normal text-[14px]">{(service.subscribeNum + service.subscribeVerifyNum)?? '-'}</span></span>
</Tooltip>
</div>
+1 -1
View File
@@ -24,6 +24,6 @@
"@market/*": ["./src/*"]
},
},
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TransferTable.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/GroupTree.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../core/src/pages/serviceCategory/ServiceHubCategoryConfig.tsx"],
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../core/src/pages/serviceCategory/ServiceHubCategoryConfig.tsx"],
"references": [{ "path": "./tsconfig.node.json" }]
}
@@ -0,0 +1,49 @@
import { PageProColumns } from "@common/components/aoplatform/PageList";
import { OpenApiTableListItem } from "./type";
import { $t } from "@common/locales/index.ts";
export const OPENAPI_LIST_COLUMNS: PageProColumns<OpenApiTableListItem>[] = [
{
title:$t('应用名称'),
dataIndex: 'name',
ellipsis:true,
width:160,
fixed:'left'
},
{
title:$t('应用 ID'),
dataIndex: 'id',
ellipsis:true,
width: 140,
},
{
title:$t('鉴权 Token'),
dataIndex: 'token',
ellipsis:{
showTitle:true
}
},
{
title:$t('关联标签'),
dataIndex: 'tag'
},
{
title:$t('启用'),
dataIndex: 'status'
},
{
title:$t('更新者'),
dataIndex: ['operator','name'],
filters: true,
onFilter: true,
valueType: 'select',
filterSearch: true
},
{
title:$t('更新时间'),
width:182,
dataIndex: 'updateTime',
sorter: (a,b)=>(new Date(a.updateTime)).getTime() - (new Date(b.updateTime)).getTime()
}
];
@@ -0,0 +1,11 @@
import { EntityItem } from "@common/const/type";
export type OpenApiTableListItem = {
id:string;
name: string;
token:string;
tags:string;
status:boolean;
operator:EntityItem;
updateTime:string;
};
@@ -1,10 +1,11 @@
import {App, Form, Input} from "antd";
import {forwardRef, useEffect, useImperativeHandle} from "react";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {BasicResponse, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE, VALIDATE_MESSAGE} from "@common/const/const.tsx";
import {useFetch} from "@common/hooks/http.ts";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import {v4 as uuidv4} from 'uuid'
import { $t } from "@common/locales";
export type OpenApiConfigFieldType = {
id?:string
@@ -33,11 +34,11 @@ export const OpenApiConfig = forwardRef<OpenApiConfigHandle,OpenApiConfigProps>(
fetchData<BasicResponse<null>>('external-app',{method:type === 'add'? 'POST' : 'PUT',eoBody:(value), eoParams:type === 'add' ? {}:{id:entity!.id}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
@@ -65,31 +66,29 @@ export const OpenApiConfig = forwardRef<OpenApiConfigHandle,OpenApiConfigProps>(
form={form}
className="mx-auto "
name="OpenApiConfig"
// labelCol={{ offset:1, span: 4 }}
// wrapperCol={{ span: 19}}
autoComplete="off"
>
<Form.Item<OpenApiConfigFieldType>
label="应用名称"
label={$t("应用名称")}
name="name"
rules={[{ required: true, message: '必填项',whitespace:true }]}
rules={[{ required: true, message: VALIDATE_MESSAGE.required,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
<Form.Item<OpenApiConfigFieldType>
label="应用 ID"
label={$t("应用 ID")}
name="id"
rules={[{ required: true, message: '必填项' ,whitespace:true }]}
rules={[{ required: true, message: VALIDATE_MESSAGE.required ,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入" disabled={type === 'edit'}/>
<Input className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input} disabled={type === 'edit'}/>
</Form.Item>
<Form.Item
label="描述"
label={$t("描述")}
name="desc"
>
<Input.TextArea className="w-INPUT_NORMAL" placeholder="请输入"/>
<Input.TextArea className="w-INPUT_NORMAL" placeholder={PLACEHOLDER.input}/>
</Form.Item>
</Form>
@@ -1,71 +1,18 @@
import PageList from "@common/components/aoplatform/PageList.tsx";
import PageList, { PageProColumns } from "@common/components/aoplatform/PageList.tsx";
import {useEffect, useRef, useState} from "react";
import {ActionType, ProColumns} from "@ant-design/pro-components";
import {ActionType} from "@ant-design/pro-components";
import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx";
import { App, Divider, Switch} from "antd";
import copy from "copy-to-clipboard";
import {useFetch} from "@common/hooks/http.ts";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {BasicResponse, COLUMNS_TITLE, DELETE_TIPS, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import {OpenApiConfig, OpenApiConfigFieldType, OpenApiConfigHandle} from "./OpenApiConfig.tsx";
import { EntityItem } from "@common/const/type.ts";
import { SimpleMemberItem } from "@common/const/type.ts";
import TableBtnWithPermission from "@common/components/aoplatform/TableBtnWithPermission.tsx";
import { frontendTimeSorter } from "@common/utils/dataTransfer.ts";
type OpenApiTableListItem = {
id:string;
name: string;
token:string;
tags:string;
status:boolean;
operator:EntityItem;
updateTime:string;
};
const OPENAPI_LIST_COLUMNS: ProColumns<OpenApiTableListItem>[] = [
{
title: '应用名称',
dataIndex: 'name',
ellipsis:true,
width:160,
fixed:'left'
},
{
title: '应用 ID',
dataIndex: 'id',
ellipsis:true,
width: 140,
},
{
title: '鉴权 Token',
dataIndex: 'token',
ellipsis:{
showTitle:true
}
},
{
title: '关联标签',
dataIndex: 'tag'
},
{
title: '启用',
dataIndex: 'status'
},
{
title: '更新者',
dataIndex: ['operator','name'],
filters: true,
onFilter: true,
valueType: 'select',
filterSearch: true
},
{
title: '更新时间',
width:182,
dataIndex: 'updateTime',
sorter: (a,b)=>(new Date(a.updateTime)).getTime() - (new Date(b.updateTime)).getTime()
}
];
import { OPENAPI_LIST_COLUMNS } from "@market/consts/const.tsx";
import { OpenApiTableListItem } from "@market/consts/type.ts";
import { $t } from "@common/locales/index.ts";
export default function OpenApiList(){
@@ -74,7 +21,7 @@ export default function OpenApiList(){
const [init, setInit] = useState<boolean>(true)
const [tableListDataSource, setTableListDataSource] = useState<OpenApiTableListItem[]>([]);
const [tableHttpReload, setTableHttpReload] = useState(true);
const [columns,setColumns] = useState<ProColumns<OpenApiTableListItem>[] >([])
const [columns,setColumns] = useState<PageProColumns<OpenApiTableListItem>[] >([])
const pageListRef = useRef<ActionType>(null);
const addOpenApiRef = useRef<OpenApiConfigHandle>(null)
const editOpenApiRef = useRef<OpenApiConfigHandle>(null)
@@ -82,21 +29,21 @@ export default function OpenApiList(){
const { setBreadcrumb } = useBreadcrumb()
const [memberValueEnum, setMemberValueEnum] = useState<{[k:string]:{text:string}}>({})
const operation:ProColumns<OpenApiTableListItem>[] =[
const operation:PageProColumns<OpenApiTableListItem>[] =[
{
title: '操作',
title: COLUMNS_TITLE.operate,
key: 'option',
width: 266,
btnNums:4,
valueType: 'option',
fixed:'right',
render: (_: React.ReactNode, entity: OpenApiTableListItem) => [
<TableBtnWithPermission access="system.openapi.self.updateToken" key="refreshToken" onClick={()=>{refreshToken(entity)}} btnTitle="更新token"/>,
<TableBtnWithPermission access="system.openapi.self.updateToken" key="refresh" btnType="refresh" onClick={()=>{refreshToken(entity)}} btnTitle="更新token"/>,
<Divider type="vertical" className="mx-0" key="div1"/>,
<TableBtnWithPermission access="system.openapi.self.view" key="copyToken" onClick={()=>{copyToken(entity)}} btnTitle="复制token"/>,
<TableBtnWithPermission access="system.openapi.self.view" key="copy" btnType="copy" onClick={()=>{copyToken(entity)}} btnTitle="复制token"/>,
<Divider type="vertical" className="mx-0" key="div2"/>,
<TableBtnWithPermission access="system.openapi.self.edit" key="edit" onClick={()=>{openModal('edit',entity)}} btnTitle="编辑"/>,
<TableBtnWithPermission access="system.openapi.self.edit" key="edit" btnType="edit" onClick={()=>{openModal('edit',entity)}} btnTitle="编辑"/>,
<Divider type="vertical" className="mx-0" key="div3"/>,
<TableBtnWithPermission access="system.openapi.self.delete" key="delete" onClick={()=>{openModal('delete',entity)}} btnTitle="删除"/>
<TableBtnWithPermission access="system.openapi.self.delete" key="delete" btnType="delete" onClick={()=>{openModal('delete',entity)}} btnTitle="删除"/>
],
}
]
@@ -118,7 +65,7 @@ export default function OpenApiList(){
setTableHttpReload(false)
return {data:data.apps, success: true}
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
return {data:[], success:false}
}
}).catch(() => {
@@ -130,19 +77,19 @@ export default function OpenApiList(){
fetchData<BasicResponse<null>>('external-app/token',{method:'PUT',eoParams:{id:entity.id}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
manualReloadTable()
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
})
}
const copyToken = (entity: OpenApiTableListItem)=>{
if(copy(entity.token)){
message.success('复制成功')
message.success(RESPONSE_TIPS.copySuccess)
}else{
message.error('复制失败,请重试')
message.error(RESPONSE_TIPS.copyError)
}
}
@@ -156,11 +103,11 @@ export default function OpenApiList(){
fetchData<BasicResponse<null>>('external-app',{method:'DELETE',eoParams:{id:entity!.id}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
resolve(true)
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
reject(msg || RESPONSE_TIPS.error)
}
}).catch((errorInfo)=> reject(errorInfo))
})
@@ -172,24 +119,24 @@ export default function OpenApiList(){
let content:string|React.ReactNode = ''
switch (type){
case 'add':
title='添加 Open Api'
title=$t('添加 Open Api')
content=<OpenApiConfig ref={addOpenApiRef} type={type} />
break;
case 'edit':{
title='配置 Open Api'
message.loading('正在加载数据')
title=$t('配置 Open Api')
message.loading(RESPONSE_TIPS.loading)
const {code,data,msg} = await fetchData<BasicResponse<{app:OpenApiConfigFieldType}>>('external-app',{method:'GET',eoParams:{id:entity!.id}})
message.destroy()
if(code === STATUS_CODE.SUCCESS){
content=<OpenApiConfig ref={editOpenApiRef} type={type} entity={data.app}/>
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
return
}
break;}
case 'delete':
title='删除'
content='该数据删除后将无法找回,请确认是否删除?'
title=$t('删除')
content=DELETE_TIPS.default
break;
}
@@ -207,8 +154,8 @@ export default function OpenApiList(){
}
},
width:600,
okText:'确认',
cancelText:'取消',
okText:$t('确认'),
cancelText:$t('取消'),
closable:true,
icon:<></>,
})
@@ -218,10 +165,10 @@ export default function OpenApiList(){
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 || '操作成功!')
message.success(msg || RESPONSE_TIPS.success)
manualReloadTable()
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
})
}
@@ -237,13 +184,13 @@ export default function OpenApiList(){
})
setMemberValueEnum(tmpValueEnum)
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
}
useEffect(() => {
setBreadcrumb([{ title: 'Open Api'}])
setBreadcrumb([{ title:$t('Open Api')}])
getMemberList()
setColumns(OPENAPI_LIST_COLUMNS
.map((x)=>{
@@ -269,7 +216,7 @@ export default function OpenApiList(){
dataSource={tableListDataSource}
showPagination={false}
primaryKey="id"
addNewBtnTitle="添加应用"
addNewBtnTitle={$t("添加应用")}
addNewBtnAccess="system.openapi.self.add"
onChange={() => {
setTableHttpReload(false)
+1 -1
View File
@@ -24,6 +24,6 @@
"@market/*": ["./src/*"]
},
},
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TransferTable.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/GroupTree.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../core/src/pages/serviceCategory/ServiceHubCategoryConfig.tsx"],
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../core/src/pages/serviceCategory/ServiceHubCategoryConfig.tsx"],
"references": [{ "path": "./tsconfig.node.json" }]
}
@@ -11,9 +11,10 @@ import { PictureTypeEnum, NodeClickItem, GraphData } from "@core/const/system-ru
import { UnionFind, edgesFormatter, getNodeSpacing, nodesFormatter } from "@common/utils/systemRunning.ts";
import { EDGE_STYLE, END_ARROW_STYLE, OUT_SPACE_CONTENT_EDGE_COLOR, RELATIVE_PICTURE_NODE_FONTSIZE, SELF_SPACE_CONTENT_EDGE_COLOR, SYSTEM_TUNNING_CONFIG } from "@core/const/system-running/const.ts";
import ReactDOM from "react-dom";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {BasicResponse, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import SystemRunningInstruction from "./SystemRunningInstruction.tsx";
import { useBreadcrumb } from "@common/contexts/BreadcrumbContext.tsx";
import { $t } from "@common/locales/index.ts";
export type TopologyItem = {
projects:TopologyProjectItem[]
@@ -114,9 +115,6 @@ export default function SystemRunning(){
const newNodes = nodesFormatter(projects)
const newEdges = edgesFormatter(tmpProjectConnectMap)
// 从 edges 中提取所有唯一的节点
// const allNodeIds = tmpProjectConnectMap.map(({ source, target }) => source || []).concat(tmpProjectConnectMap.map(({ source, target }) => target || [])).filter(Boolean);
// const nodes: Node[] = allNodeIds.map(id => ({ id }));
// 从 edges 中提取所有唯一的节点,并将 Set 转换为数组
const allNodeIds:string[] = Array.from(new Set(newEdges.flatMap(edge => [edge.source, edge.target]))) as string[];
@@ -239,7 +237,7 @@ export default function SystemRunning(){
setShowGraph(newGraphData?.nodes?.length > 0)
}else{
message.error(msg || '操作失败')
message.error(msg || RESPONSE_TIPS.error)
}
}).finally(()=>setLoading(false))
}
@@ -351,7 +349,7 @@ export default function SystemRunning(){
},
defaultEdge: {
// type: 'quadratic',
label: '调用服务',
label: $t('调用服务'),
labelCfg: {
style: {
fill: '5B8FF9',
@@ -544,24 +542,24 @@ export default function SystemRunning(){
useEffect(() => {
setBreadcrumb([{title:'系统拓扑图'}])
setBreadcrumb([{title:$t('系统拓扑图')}])
getNodeData()
}, []);
return (<>
{
showGraph ?
<div className="h-full overflow-hidden w-full">
<div className="mt-[10px] ml-[10px] absolute top-navbar-height right-0">
<div className="flex justify-between">
<div>
</div>
<div className="border border-solid border-color-base rounded mr-[20px] z-[999] h-8 bg-[#fff]">
<Button id="zoom-in-button" type="text" title="放大" icon={<ZoomInOutlined />} onClick={()=>updateZoomTo(true)}/>
<Button id="zoom-out-button" type="text" title="缩小" icon={<ZoomOutOutlined />} onClick={()=>updateZoomTo(false)} />
<div className="h-full overflow-hidden w-full pb-PAGE_INSIDE_B pr-PAGE_INSIDE_X relative">
<div className=" absolute top-[10px] right-PAGE_INSIDE_X">
<div className="flex justify-between">
<div>
</div>
<div className="border border-solid border-color-base rounded z-[999] h-8 bg-[#fff]">
<Button id="zoom-in-button" type="text" title={$t("放大")} icon={<ZoomInOutlined />} onClick={()=>updateZoomTo(true)}/>
<Button id="zoom-out-button" type="text" title={$t("缩小")} icon={<ZoomOutOutlined />} onClick={()=>updateZoomTo(false)} />
</div>
</div>
</div>
</div>
<div className="h-full w-full" ref={graphContainerRef} >
@@ -1,4 +1,5 @@
import { useBreadcrumb } from "@common/contexts/BreadcrumbContext";
import { $t } from "@common/locales";
import { useEffect } from "react";
import { Link } from "react-router-dom";
export default function SystemRunningInstruction() {
@@ -6,7 +7,7 @@ export default function SystemRunningInstruction() {
useEffect(()=>{
setBreadcrumb([
{title:'系统拓扑图'}
{title:$t('系统拓扑图')}
])
},[])
return (
@@ -23,6 +23,6 @@
"@core/*": ["../core/src/*"],
},
},
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TransferTable.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/GroupTree.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../core/src/pages/serviceCategory/ServiceHubCategoryConfig.tsx"],
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../core/src/pages/serviceCategory/ServiceHubCategoryConfig.tsx"],
"references": [{ "path": "./tsconfig.node.json" }]
}