Merge branch 'feature/1.8-cx' into 'main'

feature/1.8-Improve system observability

See merge request apipark/APIPark!345
This commit is contained in:
lichunxian
2025-04-29 17:27:37 +08:00
22 changed files with 300 additions and 110 deletions
@@ -36,7 +36,7 @@ function BasicLayout({ project = 'core' }: { project: string }) {
const { state, accessData, checkPermission, accessInit, dispatch, resetAccess, getGlobalAccessData, menuList } =
useGlobalContext()
const [pathname, setPathname] = useState(currentUrl)
const mainPage = project === 'core' ? '/service/list' : '/serviceHub/list'
const mainPage = project === 'core' ? '/service/list' : '/portal/list'
const [menuItems, setMenuItems] = useState<MenuProps['items']>()
const pluginSlotHub = usePluginSlotHub()
@@ -187,6 +187,7 @@ export const TranslateWord = () => {
{$t('调用地址')}
{$t('消费者 IP')}
{$t('鉴权名称')}
{$t('日志输出')}
</>
)
}
@@ -106,7 +106,7 @@ const ServiceInfoCard = ({
* 打开服务详情页面
*/
const openInPortal = () => {
window.open(`/serviceHub/detail/${serviceOverview?.id}`, '_blank')
window.open(`/portal/detail/${serviceOverview?.id}`, '_blank')
}
// 格式化调用次数,添加K和M单位
@@ -87,8 +87,8 @@ const mockData = [
},
{
name: 'API 市场',
key: 'serviceHub',
path: '/serviceHub',
key: 'portal',
path: '/portal',
icon: 'ic:baseline-hub',
access: 'system.api_portal.api_portal.view'
},
@@ -107,15 +107,15 @@ const mockData = [
},
{
name: '服务',
key: 'analyticsSubscriber',
path: '/analytics/subscriber/list',
key: 'analyticsService',
path: '/analytics/service/list',
icon: 'ic:baseline-blinds-closed',
access: 'system.analysis.run_view.view'
},
{
name: '消费者',
key: 'analyticsProvider',
path: '/analytics/provider/list',
key: 'analyticsConsumer',
path: '/analytics/consumer/list',
icon: 'ic:baseline-apps',
access: 'system.analysis.run_view.view'
},
@@ -253,7 +253,7 @@ const mockData = [
access: 'system.settings.ssl_certificate.view'
},
{
name: '日志',
name: '日志输出',
key: 'logsettings',
path: '/logsettings',
icon: 'ic:baseline-sticky-note-2',
@@ -112,10 +112,10 @@ const mockData = {
},
{
driver: 'apipark.builtIn.component',
name: 'serviceHub',
name: 'portal',
router: [
{
path: 'serviceHub',
path: 'portal',
type: 'normal'
}
]
@@ -4,7 +4,7 @@
"Kb58e0c3f": "Service",
"Kc9e489f5": "Team",
"K61c89f5f": "API Portal",
"K16d71239": "Analysis",
"K16d71239": "Analytics",
"K714c192d": "Call Statistics",
"Kd57dfe97": "Topology",
"K3fe97dcc": "System Settings",
+3 -3
View File
@@ -535,7 +535,7 @@ export const routerMap: Map<string, RouterMapConfig> = new Map([
],
[
'serviceHub',
'portal',
{
type: 'module',
component: <Outlet />,
@@ -702,12 +702,12 @@ export const routerMap: Map<string, RouterMapConfig> = new Map([
children: [
{
path: 'total',
key: 'analytics2',
key: 'analyticsTotal',
lazy: lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardTotal.tsx'))
},
{
path: ':dashboardType',
key: 'analytics3',
key: 'analyticsOther',
component: <Outlet />,
children: [
{
@@ -123,6 +123,7 @@ export type PartitionDataLogHeaderListFieldType = {
export type PartitionDataLogConfigFieldType = {
headers: PartitionDataLogHeaderListFieldType[]
url: string
driver?: string
}
export const PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS: PageProColumns<PartitionDataLogConfigFieldType & { _id: string }>[] = [
@@ -68,7 +68,7 @@ const LogSettings = () => {
<>
<Skeleton className="m-btnbase w-calc-100vw-minus-padding-r" active loading={loading}>
<InsidePage
pageTitle={$t('日志置')}
pageTitle={$t('日志输出设置')}
description={'APIPark ' + $t('提供详尽的 API 调用日志,帮助企业监控、分析和审计 API 的运行状况。')}
>
<div className="flex h-full">
@@ -1,11 +1,15 @@
import EditableTable from "@common/components/aoplatform/EditableTable"
import WithPermission from "@common/components/aoplatform/WithPermission"
import { BasicResponse, PLACEHOLDER, STATUS_CODE } from "@common/const/const"
import { useFetch } from "@common/hooks/http"
import { $t } from "@common/locales"
import { PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS, PartitionDataLogConfigFieldType, PartitionDataLogHeaderListFieldType } from "@core/const/partitions/types"
import { Button, Form, Input, message } from "antd"
import { useEffect } from "react"
import EditableTable from '@common/components/aoplatform/EditableTable'
import WithPermission from '@common/components/aoplatform/WithPermission'
import { BasicResponse, PLACEHOLDER, STATUS_CODE } from '@common/const/const'
import { useFetch } from '@common/hooks/http'
import { $t } from '@common/locales'
import {
PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS,
PartitionDataLogConfigFieldType,
PartitionDataLogHeaderListFieldType
} from '@core/const/partitions/types'
import { Button, Form, Input, message, Select } from 'antd'
import { useEffect } from 'react'
export type DashboardPageShowStatus = 'view' | 'edit'
export type DashboardSettingEditProps = {
@@ -15,7 +19,7 @@ export type DashboardSettingEditProps = {
}
const DataLogSettingEdit = (props: DashboardSettingEditProps) => {
const { changeStatus, refreshData, data } = props
const [form] = Form.useForm();
const [form] = Form.useForm()
const { fetchData } = useFetch()
const onFinish = () => {
@@ -23,10 +27,16 @@ const DataLogSettingEdit = (props: DashboardSettingEditProps) => {
const formData = {
config: {
url: value.url,
headers: value.headers.filter((item: PartitionDataLogHeaderListFieldType) => item.key).map((item: PartitionDataLogHeaderListFieldType) => ({key:item.key, value:item.value || ''}))
headers: value.headers
.filter((item: PartitionDataLogHeaderListFieldType) => item.key)
.map((item: PartitionDataLogHeaderListFieldType) => ({ key: item.key, value: item.value || '' }))
}
}
fetchData<BasicResponse<{ info: PartitionDataLogConfigFieldType }>>('log/loki', { method: 'POST', body: JSON.stringify(formData), eoParams: {} }).then(response => {
fetchData<BasicResponse<{ info: PartitionDataLogConfigFieldType }>>('log/loki', {
method: 'POST',
body: JSON.stringify(formData),
eoParams: {}
}).then((response) => {
const { code, msg } = response
if (code === STATUS_CODE.SUCCESS) {
message.success(msg || $t('操作成功,即将刷新页面'))
@@ -38,15 +48,26 @@ const DataLogSettingEdit = (props: DashboardSettingEditProps) => {
})
}
useEffect(() => { form.setFieldsValue(data) }, [data])
useEffect(() => {
form.setFieldsValue({
...data,
headers: data?.headers?.length ? data.headers : [
{
key: '',
value: ''
}
],
driver: 'loki'
})
}, [data])
useEffect(() => {
return (form.setFieldsValue({}))
}, []);
return form.setFieldsValue({})
}, [])
return (
<>
<div className="overflow-auto h-full">
<WithPermission access={''} >
<WithPermission access={''}>
<Form
form={form}
className="mx-auto flex flex-col justify-between h-full"
@@ -55,23 +76,29 @@ const DataLogSettingEdit = (props: DashboardSettingEditProps) => {
autoComplete="off"
>
<Form.Item<PartitionDataLogConfigFieldType>
label={$t("请求前缀")}
name="url"
label={$t('数据源类型')}
name="driver"
rules={[{ required: true }]}
>
<Select
showSearch
optionFilterProp="label"
className="w-INPUT_NORMAL"
placeholder={$t(PLACEHOLDER.select)}
options={[{ label: 'Loki', value: 'loki' }]}
></Select>
</Form.Item>
<Form.Item<PartitionDataLogConfigFieldType> label={$t('请求前缀')} name="url" rules={[{ required: true }]}>
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
</Form.Item>
<Form.Item<PartitionDataLogConfigFieldType>
label={$t("HTTP 头部")}
name="headers"
>
<Form.Item<PartitionDataLogConfigFieldType> label={$t('HTTP 头部')} name="headers">
<EditableTable<PartitionDataLogConfigFieldType & { _id: string }>
configFields={PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS}
/>
</Form.Item>
<div className="flex gap-btnbase">
<WithPermission access='system.devops.data_source.edit'>
<WithPermission access="system.devops.data_source.edit">
<Button type="primary" htmlType="submit">
{$t('保存')}
</Button>
@@ -84,7 +111,7 @@ const DataLogSettingEdit = (props: DashboardSettingEditProps) => {
</WithPermission>
</div>
</>
);
)
}
export default DataLogSettingEdit;
export default DataLogSettingEdit
@@ -161,7 +161,7 @@ const PartitionInsideDashboardSetting: FC = () => {
className="overflow-hidden mt-[30px] w-full max-h-full flex flex-col justify-between"
title={
<div>
<span className="text-MAIN_TEXT my-btnybase mr-btnbase"> {$t('数据日志')}</span>
<span className="text-MAIN_TEXT my-btnybase mr-btnbase"> {$t('请求日志')}</span>
{!dataLogLoading && !dataLogData && <Tag color="#f50">{$t('未配置')}</Tag>}
</div>
}
@@ -220,6 +220,11 @@ export function DataLogConfigPreview(x: PartitionDataLogConfigFieldType) {
return (
<div className="flex flex-col gap-[4px] ">
<Row className="">
<Col className="font-bold text-right pr-[4px]">{$t('数据源')}</Col>
{/* 先写死,或许会有选择列表,但现在可以不用 */}
<Col>Loki</Col>
</Row>
<Row className="">
<Col className="font-bold text-right pr-[4px]">{$t('请求前缀')}</Col>
<Col>{x?.url}</Col>
@@ -9,6 +9,7 @@ type AreaChartInfo = {
data: number[]
max: string
min: string
showXAxis?: boolean
}
type ServiceAreaCharProps = {
@@ -24,27 +25,65 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha
const option = {
tooltip: {
trigger: 'axis',
position: function (pt) {
return [pt[0], '10%']
formatter: function (value: any) {
// 如果是数组,取第一个参数的name
const param = Array.isArray(value) ? value[0] : value
console.log('params==', param)
let tooltipContent = `<div style="min-width:140px;padding:8px;">`
const marker = `<span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:${param.color};"></span>`
tooltipContent += `<div style="margin-top: 8px; display: flex; justify-content: space-between; align-items: center;">
<div style="margin-right: 4px;">${marker}</div>${param.name} <div style="font-weight:bold; margin-left: 20px;">${param.value}</div>
</div>`
tooltipContent += '</div>'
return tooltipContent
}
},
title: {
show: false
},
title: [
{
text: '{titleStyle|' + $t(dataInfo.title) + '}\n\n{valueStyle|' + dataInfo.value + '}',
left: '2%',
top: '0',
textStyle: {
rich: {
titleStyle: {
fontSize: 14,
color: '#999',
fontWeight: 'normal',
lineHeight: 20
},
valueStyle: {
fontSize: 32,
color: '#101010',
fontWeight: 500,
lineHeight: 40
}
}
}
}
],
toolbox: {
show: false
},
grid: {
left: '5%',
left: '3%',
right: '3%',
bottom: '5%',
top: '100px',
bottom: '0%',
top: '110px',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: dataInfo.date
data: dataInfo.date,
axisTick: {
show: false
},
axisLine: {
lineStyle: {
color: '#ccc'
}
},
show: false
},
yAxis: {
type: 'value',
@@ -59,7 +98,51 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha
show: false
}
},
dataZoom: [],
// 添加数据缩放组件,实现鼠标放大缩小,后续可能需要
// dataZoom: [
// {
// type: 'inside', // 内置的数据区域缩放组件(使用鼠标滚轮缩放)
// xAxisIndex: 0, // 设置缩放作用在第一个x轴
// filterMode: 'filter',
// start: 0,
// end: 100
// },
// {
// type: 'slider', // 滑动条型数据区域缩放组件
// xAxisIndex: 0,
// filterMode: 'filter',
// height: 20,
// bottom: 0,
// start: 0,
// end: 100,
// handleIcon:
// 'path://M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
// handleSize: '80%',
// handleStyle: {
// color: '#fff',
// shadowBlur: 3,
// shadowColor: 'rgba(0, 0, 0, 0.6)',
// shadowOffsetX: 2,
// shadowOffsetY: 2
// },
// show: false // 默认隐藏底部的滑动条,可以改为 true 显示
// }
// ],
// 添加空状态提示
graphic: !dataInfo.data.length
? [
{
type: 'text',
left: 'center',
top: 'middle',
style: {
text: $t('暂无数据'),
fontSize: 14,
fill: '#999'
}
}
]
: [],
series: [
{
name: dataInfo.title,
@@ -94,23 +177,30 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha
}
setOption(option)
}
// 使用深度监听来确保图表数据更新
useEffect(() => {
if (!dataInfo) return
setChartOption(dataInfo)
}, [dataInfo])
// 直接获取 ECharts 实例并设置选项
const echartsInstance = chartRef.current?.getEchartsInstance()
if (echartsInstance) {
// 清除已有的图表
echartsInstance.clear()
// 重新设置选项
setChartOption(dataInfo)
}
}, [dataInfo, JSON.stringify(dataInfo)])
return (
<div className={`w-full ${customClassNames}`}>
<div className="absolute top-[10px] left-[10px] w-full">
<div className="text-[16px] text-[#999]">{$t(dataInfo?.title || '')}</div>
<div className="relative top-[-6px]">
<span className="text-[30px] font-bold">{dataInfo?.value}</span>
<div className="absolute top-[5px] right-[8%] grid grid-cols-[auto_auto] gap-y-1 justify-items-end">
<div className="relative top-[5px]">
<div className="absolute top-[23px] right-[5%] grid grid-cols-[auto_auto] justify-items-end">
<div className="flex justify-center items-center">
<span className="text-[#ff4683] text-[9px]"></span>
<span className="text-[#FE564D] text-[9px]"></span>
</div>
<span className="ml-1 text-right">{dataInfo?.max}</span>
<div className="flex justify-center items-center">
<span className="text-[#4bdb6a] text-[9px]"></span>
<span className="text-[#27B148] text-[9px]"></span>
</div>
<span className="ml-1 text-right">{dataInfo?.min}</span>
</div>
@@ -12,6 +12,7 @@ export type BarChartInfo = {
color: string
value: number[]
}[]
showXAxis?: boolean
}
type ServiceBarCharProps = {
@@ -20,7 +21,6 @@ type ServiceBarCharProps = {
height?: number
}
const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharProps) => {
const chartRef = useRef<ECharts>(null)
const [option, setOption] = useState<EChartsOption | undefined>({})
@@ -33,6 +33,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
const setChartOption = (dataInfo: BarChartInfo) => {
const isNumberArray = typeof dataInfo.data[0] !== 'object'
const legendData = isNumberArray ? [dataInfo.title] : dataInfo.data.map((item) => item.name)
const hasData = dataInfo.data && dataInfo.data.length > 0
const tooltipFormatter = (params: { name: string; color: string; seriesIndex?: number }) => {
let tooltipContent = `<div style="min-width:140px;padding:8px;">
<div>${isNumberArray ? '' : params.name}</div>`
@@ -63,8 +64,8 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
const option: EChartsOption = {
title: [
{
text: '{titleStyle|' + $t(dataInfo.title) + '}\n{valueStyle|' + dataInfo.value + '}',
left: '4%',
text: '{titleStyle|' + $t(dataInfo.title) + '}\n\n{valueStyle|' + dataInfo.value + '}',
left: '2%',
top: '0',
textStyle: {
rich: {
@@ -75,8 +76,8 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
lineHeight: 20
},
valueStyle: {
fontSize: 25,
color: '#000',
fontSize: 32,
color: '#101010',
fontWeight: 500,
lineHeight: 40
}
@@ -85,10 +86,10 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
}
],
grid: {
left: '5%',
left: '3%',
right: '3%',
bottom: '5%',
top: '100px',
bottom: '0%',
top: '110px',
containLabel: true
},
tooltip: {
@@ -124,12 +125,14 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
lineStyle: {
color: '#ccc'
}
}
},
show: false
},
yAxis: {
type: 'value',
name: '',
min: 0,
minInterval: 1,
splitLine: {
lineStyle: {
type: 'dashed',
@@ -140,6 +143,50 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
formatter: '{value}'
}
},
// 添加数据缩放组件,实现鼠标放大缩小,后续可能需要
// dataZoom: [
// {
// type: 'inside', // 内置的数据区域缩放组件(使用鼠标滚轮缩放)
// xAxisIndex: 0, // 设置缩放作用在第一个x轴
// filterMode: 'filter',
// start: 0,
// end: 100
// },
// {
// type: 'slider', // 滑动条型数据区域缩放组件
// xAxisIndex: 0,
// filterMode: 'filter',
// height: 20,
// bottom: 0,
// start: 0,
// end: 100,
// handleIcon: 'path://M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
// handleSize: '80%',
// handleStyle: {
// color: '#fff',
// shadowBlur: 3,
// shadowColor: 'rgba(0, 0, 0, 0.6)',
// shadowOffsetX: 2,
// shadowOffsetY: 2
// },
// show: false // 默认隐藏底部的滑动条,可以改为 true 显示
// }
// ],
// 添加空状态提示
graphic: !hasData
? [
{
type: 'text',
left: 'center',
top: 'middle',
style: {
text: $t('暂无数据'),
fontSize: 14,
fill: '#999'
}
}
]
: [],
series: isNumberArray
? [
{
@@ -171,10 +218,19 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
setOption(option)
}
// 使用深度监听来确保图表数据更新
useEffect(() => {
if (!dataInfo) return
setChartOption(dataInfo)
}, [dataInfo])
// 直接获取 ECharts 实例并设置选项
const echartsInstance = chartRef.current?.getEchartsInstance()
if (echartsInstance) {
// 清除已有的图表
echartsInstance.clear()
// 重新设置选项
setChartOption(dataInfo)
}
}, [dataInfo, JSON.stringify(dataInfo)])
return (
<div className={`w-full ${customClassNames}`}>
<ECharts ref={chartRef} option={option} style={{ height: height || 400 }} opts={{ renderer: 'svg' }} />
@@ -18,33 +18,30 @@ const Indicator = ({ indicatorInfo }: { indicatorInfo: any }) => {
/** 设置服务指标 */
const setIndicatorList = () => {
const side = indicatorInfo?.serviceKind === 'ai' ? 'aiInside' : 'inside'
setIndicator([
{
title: indicatorInfo?.enableMcp ? 'APIs / Tools' : 'APIs',
link: `/serviceHub/detail/${indicatorInfo?.serviceId}`,
link: `/service/${indicatorInfo?.teamId}/${side}/${indicatorInfo?.serviceId}/route`,
content: indicatorInfo?.apiNum ?? 0
},
{
title: $t('订阅数量'),
link: `/consumer/list/${indicatorInfo?.teamId}`,
link: `/service/${indicatorInfo?.teamId}/${side}/${indicatorInfo?.serviceId}/subscriber`,
content: indicatorInfo?.subscriberNum ?? 0
},
{
title: 'MCP',
link: `/service/${indicatorInfo?.teamId}/${side}/${indicatorInfo?.serviceId}/setting`,
content: (
<>
{/* green */}
<Button
color={indicatorInfo?.enableMcp ? 'green' : 'primary'}
className="w-full rounded-[10px]"
variant="outlined"
onClick={(e) => {
e.stopPropagation()
if (indicatorInfo?.enableMcp) {
window.open(`/serviceHub/detail/${indicatorInfo?.serviceId}`, '_blank')
} else {
navigateTo(`/service/${indicatorInfo?.teamId}/aiInside/${indicatorInfo?.serviceId}/setting`)
}
navigateTo(`/service/${indicatorInfo?.teamId}/${side}/${indicatorInfo?.serviceId}/setting`)
}}
>
{indicatorInfo?.enableMcp ? $t('已开启') : $t('开启 MCP')}
@@ -65,19 +62,23 @@ const Indicator = ({ indicatorInfo }: { indicatorInfo: any }) => {
{indicatorList.map((item, index) => (
<Card
key={index}
className={`flex-1 cursor-pointer shadow-[0_5px_10px_0_rgba(0,0,0,0.05)] rounded-[10px] transition duration-500 hover:shadow-[0_5px_20px_0_rgba(0,0,0,0.15)] hover:scale-[1.02] ${index > 0 ? 'ml-[10px]' : ''}`}
className={`flex-1 cursor-pointer rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`}
classNames={{
body: 'p-[15px]'
body: 'py-[20px] px-[18px]'
}}
onClick={() => {
window.open(item.link)
if (item.link) {
navigateTo(item.link)
}
}}
>
<div className="text-[14px] font-semibold text-gray-400 mb-[15px]">
<div className="text-[14px] text-[#888] mb-[10px]">
{item.title}
{item.link && <Icon icon="uiw:right" width="16" height="16" className="absolute top-[14px] right-[14px]" />}
</div>
<div className={`${index < 2 ? 'text-[40px] font-semibold' : 'block mt-[30px]'}`}>{item.content}</div>
<div className={`${index < 2 ? 'text-[32px] font-medium text-[#101010]' : 'block mt-[30px]'}`}>
{item.content}
</div>
</Card>
))}
</div>
@@ -65,7 +65,7 @@ const RankingList = ({ topRankingList, serviceType }: { topRankingList: RankingL
key={index}
className={`flex-1 h-fit rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`}
classNames={{
body: 'p-[15px] pb-[0px]'
body: 'p-[15px]'
}}
>
<div className="mb-[10px]">
@@ -99,6 +99,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
subscriberNum: serviceOverview.subscriberNum,
teamId: teamId,
enableMcp: serviceOverview.enableMcp,
serviceKind: serviceOverview.serviceKind,
serviceId: serviceId
})
// 设置总数数据
@@ -128,21 +129,24 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
date: serviceOverview.date,
max: serviceOverview.maxResponseTime,
min: serviceOverview.minResponseTime,
type: 'area'
type: 'area',
showXAxis: false
},
// 平均请求
setBarChartInfoData({
title: $t('平均请求数'),
data: serviceOverview.avgRequestPerSubscriberOverview,
value: serviceOverview.avgRequestPerSubscriber,
date: serviceOverview.date
date: serviceOverview.date,
showXAxis: false
}),
// 平均流量消耗
setBarChartInfoData({
title: $t('平均流量'),
data: serviceOverview.avgTrafficPerSubscriberOverview,
value: serviceOverview.avgTrafficPerSubscriber,
date: serviceOverview.date
date: serviceOverview.date,
showXAxis: false
})
])
}
@@ -157,6 +161,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
subscriberNum: serviceOverview.subscriberNum,
teamId: teamId,
enableMcp: serviceOverview.enableMcp,
serviceKind: serviceOverview.serviceKind,
serviceId: serviceId
})
// 设置总数数据
@@ -314,9 +319,9 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
{barChartInfo?.map((item: BarChartInfo, index: number) => (
<Card
key={index}
className={`flex-1 cursor-pointer rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`}
className={`flex-1 rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`}
classNames={{
body: 'p-[15px]'
body: 'py-[15px] px-[0px]'
}}
>
<ServiceBarChar key={index} height={400} dataInfo={item} customClassNames="flex-1"></ServiceBarChar>
@@ -327,22 +332,22 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
{perBarChartInfo?.map((item: any, index: number) => (
<Card
key={index}
className={`flex-1 cursor-pointer rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`}
className={`flex-1 rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`}
classNames={{
body: 'p-[15px]'
body: 'py-[15px] px-[0px]'
}}
>
{item.type === 'area' ? (
<>
<ServiceAreaChart
key={index}
height={250}
height={270}
dataInfo={item}
customClassNames="flex-1 relative"
></ServiceAreaChart>
</>
) : (
<ServiceBarChar key={index} height={250} dataInfo={item} customClassNames="flex-1"></ServiceBarChar>
<ServiceBarChar key={index} height={270} dataInfo={item} customClassNames="flex-1"></ServiceBarChar>
)}
</Card>
))}
@@ -3,15 +3,17 @@ export type BarData = {
value: string
date: string[]
data: any[]
showXAxis?: boolean
}
export const setBarChartInfoData = ({ title, value, data, date }: BarData) => {
export const setBarChartInfoData = ({ title, value, data, date, showXAxis }: BarData) => {
// 首先获取所有的键名(假设所有对象的键名都一样)
if (data.length === 0) {
return {
title,
value,
date,
data: []
data: [],
showXAxis: !!showXAxis
}
}
if (typeof data[0] !== 'object') {
@@ -19,7 +21,8 @@ export const setBarChartInfoData = ({ title, value, data, date }: BarData) => {
title,
value,
date,
data
data,
showXAxis: !!showXAxis
}
}
// 从第一个对象中获取所有键名
@@ -56,6 +59,7 @@ export const setBarChartInfoData = ({ title, value, data, date }: BarData) => {
title,
value,
date,
data: transformedData
data: transformedData,
showXAxis: !!showXAxis
}
}
@@ -10,8 +10,8 @@ export default function DashboardList() {
return (
<>
{dashboardType === 'api' && <DashboardApiList />}
{dashboardType === 'subscriber' && <DashboardProjectList />}
{dashboardType === 'provider' && <DashboardApplicationList />}
{dashboardType === 'service' && <DashboardProjectList />}
{dashboardType === 'consumer' && <DashboardApplicationList />}
</>
)
}
@@ -326,9 +326,9 @@ export default function DashboardTotal() {
{barChartInfo?.map((item: BarChartInfo, index: number) => (
<Card
key={index}
className={`flex-1 cursor-pointer rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`}
className={`flex-1 rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`}
classNames={{
body: 'p-[15px]'
body: 'py-[15px] px-[0px]'
}}
>
<ServiceBarChar key={index} height={400} dataInfo={item} customClassNames="flex-1"></ServiceBarChar>
@@ -339,22 +339,22 @@ export default function DashboardTotal() {
{perBarChartInfo?.map((item: any, index: number) => (
<Card
key={index}
className={`flex-1 cursor-pointer rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`}
className={`flex-1 rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`}
classNames={{
body: 'p-[15px]'
body: 'py-[15px] px-[0px]'
}}
>
{item.type === 'area' ? (
<>
<ServiceAreaChart
key={index}
height={250}
height={270}
dataInfo={item}
customClassNames="flex-1 relative"
></ServiceAreaChart>
</>
) : (
<ServiceBarChar key={index} height={250} dataInfo={item} customClassNames="flex-1"></ServiceBarChar>
<ServiceBarChar key={index} height={270} dataInfo={item} customClassNames="flex-1"></ServiceBarChar>
)}
</Card>
))}
@@ -53,7 +53,7 @@ const PUBLIC_ROUTES: RouteConfig[] = [
key: uuidv4(),
children: [
{
path: 'serviceHub',
path: 'portal',
component: <Outlet />,
key: uuidv4(),
children: [
@@ -151,7 +151,7 @@ const PUBLIC_ROUTES: RouteConfig[] = [
},
{
path: '*',
component: <Navigate to={'/serviceHub/list'} replace />,
component: <Navigate to={'/portal/list'} replace />,
key: uuidv4()
}
]
@@ -171,7 +171,7 @@ servers:
setBreadcrumb([
{
title: $t('API 门户'),
onClick: () => navigate(`/serviceHub/list`)
onClick: () => navigate(`/portal/list`)
},
{ title: service?.name || '-' },
{ title: $t('服务详情') }
@@ -361,7 +361,7 @@ servers:
return (
<div className="pr-[40px]">
<header>
<TopBreadcrumb handleBackCallback={() => navigate(`/serviceHub/list`)} />
<TopBreadcrumb handleBackCallback={() => navigate(`/portal/list`)} />
</header>
<ServiceInfoCard
serviceBasicInfo={{
@@ -254,7 +254,7 @@ export default function ManagementInsideService() {
<Button
type="text"
className="bg-[#7371fc20] hover:bg-[#7371fc19] text-theme"
onClick={() => window.open(`/serviceHub/detail/${item.service.id}`, '_blank')}
onClick={() => window.open(`/portal/detail/${item.service.id}`, '_blank')}
>
{$t('API 文档')}
</Button>