修正发布临时open-api模块

This commit is contained in:
BBIT-Kai
2026-07-28 09:42:05 +08:00
parent 38efd8ef1a
commit 97dc9fa82e
14 changed files with 177 additions and 78 deletions
+2 -1
View File
@@ -1,6 +1,7 @@
export * from './dept';
export * from './dict';
export * from './menu';
export * from './open-api';
export * from './role';
export * from './user';
export * from './sys';
export * from './user';
@@ -69,6 +69,17 @@ const routes: RouteRecordRaw[] = [
},
component: () => import('#/views/manager/menu/index.vue'),
},
{
name: 'open-api',
path: '/manager/open-api',
meta: {
authority: ['manager'],
icon: 'mdi:api',
title: 'API管理',
keepAlive: true,
},
component: () => import('#/views/manager/open-api/index.vue'),
},
],
},
];
@@ -1,18 +0,0 @@
import type { RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
component: () => import('#/views/open-api/index.vue'),
meta: {
authority: ['manager'],
icon: 'lucide:waypoints',
keepAlive: true,
order: 3,
title: '开放接口中心',
},
name: 'OpenApiCenter',
path: '/open-api/center',
},
];
export default routes;
@@ -1,8 +1,9 @@
<script lang="ts" setup>
import type { EchartsUIType } from '@vben/plugins/echarts';
import type { Dayjs } from 'dayjs';
import type { OpenApiCenterApi } from '#/api/open-api';
import type { EchartsUIType } from '@vben/plugins/echarts';
import type { OpenApiCenterApi } from '#/api/manager/open-api';
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
@@ -36,7 +37,7 @@ import {
getOpenApiUsage,
updateOpenApiConsumer,
updateOpenApiEndpoint,
} from '#/api/open-api';
} from '#/api/manager/open-api';
const RangePicker = DatePicker.RangePicker;
const activeTab = ref('overview');
@@ -149,7 +150,12 @@ const consumerColumns = [
{ key: 'identity', title: '调用方', width: 260 },
{ dataIndex: 'customId', key: 'customId', title: 'Custom ID', width: 180 },
{ dataIndex: 'callCount', key: 'callCount', title: '累计调用', width: 120 },
{ dataIndex: 'endpointCount', key: 'endpointCount', title: '接口数', width: 90 },
{
dataIndex: 'endpointCount',
key: 'endpointCount',
title: '接口数',
width: 90,
},
{ dataIndex: 'recentIp', key: 'recentIp', title: '最近 IP', width: 150 },
{ key: 'lastSeenAt', title: '最后调用', width: 170 },
{ key: 'status', title: '状态', width: 100 },
@@ -434,12 +440,12 @@ async function saveEndpoint() {
}
endpointSaving.value = true;
try {
if (editingEndpointId.value) {
await updateOpenApiEndpoint(editingEndpointId.value, { ...endpointForm });
} else {
await createOpenApiEndpoint({ ...endpointForm });
}
message.success(editingEndpointId.value ? '接口资产已更新' : '接口资产已创建');
await (editingEndpointId.value
? updateOpenApiEndpoint(editingEndpointId.value, { ...endpointForm })
: createOpenApiEndpoint({ ...endpointForm }));
message.success(
editingEndpointId.value ? '接口资产已更新' : '接口资产已创建',
);
endpointModalOpen.value = false;
await refreshAll();
} finally {
@@ -537,7 +543,10 @@ onMounted(refreshAll);
</div>
<Button ghost size="large" @click="refreshAll">
<template #icon>
<IconifyIcon :class="{ spinning: loading }" icon="lucide:refresh-cw" />
<IconifyIcon
:class="{ spinning: loading }"
icon="lucide:refresh-cw"
/>
</template>
刷新数据
</Button>
@@ -597,7 +606,10 @@ onMounted(refreshAll);
class="metric-card"
>
<div class="metric-head">
<div class="metric-icon" :style="{ background: `${item.accent}16`, color: item.accent }">
<div
class="metric-icon"
:style="{ background: `${item.accent}16`, color: item.accent }"
>
<IconifyIcon :icon="item.icon" />
</div>
<span class="metric-label">{{ item.label }}</span>
@@ -614,7 +626,11 @@ onMounted(refreshAll);
<Tabs v-model:active-key="activeTab" class="center-tabs">
<Tabs.TabPane key="overview">
<template #tab>
<span class="tab-label"><IconifyIcon icon="lucide:chart-no-axes-combined" />运行总览</span>
<span class="tab-label"
><IconifyIcon
icon="lucide:chart-no-axes-combined"
/></span
>
</template>
<div class="overview-grid">
<section class="chart-panel chart-panel-wide">
@@ -658,9 +674,17 @@ onMounted(refreshAll);
:key="item.key"
class="caller-row"
>
<span class="rank-index" :class="{ podium: index < 3 }">{{ index + 1 }}</span>
<span class="rank-index" :class="{ podium: index < 3 }">{{
index + 1
}}</span>
<div class="caller-avatar">
<IconifyIcon :icon="item.key.startsWith('ip:') ? 'lucide:globe-2' : 'lucide:building-2'" />
<IconifyIcon
:icon="
item.key.startsWith('ip:')
? 'lucide:globe-2'
: 'lucide:building-2'
"
/>
</div>
<div class="caller-copy">
<strong>{{ item.name }}</strong>
@@ -668,7 +692,10 @@ onMounted(refreshAll);
</div>
<b>{{ formatNumber(item.value) }}</b>
</div>
<div v-if="!overview.topConsumers.length" class="empty-state">
<div
v-if="overview.topConsumers.length === 0"
class="empty-state"
>
<IconifyIcon icon="lucide:radio-tower" />
<span>等待 Kong 推送首批调用数据</span>
</div>
@@ -679,7 +706,9 @@ onMounted(refreshAll);
<Tabs.TabPane key="endpoints">
<template #tab>
<span class="tab-label"><IconifyIcon icon="lucide:boxes" />接口资产</span>
<span class="tab-label"
><IconifyIcon icon="lucide:boxes" />接口资产</span
>
</template>
<div class="table-toolbar">
<div>
@@ -702,7 +731,9 @@ onMounted(refreshAll);
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<div class="asset-name">
<div class="asset-icon"><IconifyIcon icon="lucide:webhook" /></div>
<div class="asset-icon">
<IconifyIcon icon="lucide:webhook" />
</div>
<div>
<strong>{{ record.name }}</strong>
<span>{{ record.category }}</span>
@@ -714,22 +745,42 @@ onMounted(refreshAll);
</template>
<template v-else-if="column.key === 'path'">
<div class="path-cell">
<Tag :color="methodColor(record.httpMethod)">{{ record.httpMethod }}</Tag>
<Tag :color="methodColor(record.httpMethod)">
{{ record.httpMethod }}
</Tag>
<Tooltip :title="record.pathTemplate">
<code>{{ record.pathTemplate }}</code>
</Tooltip>
</div>
</template>
<template v-else-if="column.key === 'lastCalledAt'">
<span class="muted-text">{{ formatTime(record.lastCalledAt) }}</span>
<span class="muted-text">{{
formatTime(record.lastCalledAt)
}}</span>
</template>
<template v-else-if="column.key === 'status'">
<Tag :color="record.monitorEnabled && record.status === 'enabled' ? 'success' : 'default'">
{{ record.monitorEnabled && record.status === 'enabled' ? '监控中' : '已停用' }}
<Tag
:color="
record.monitorEnabled && record.status === 'enabled'
? 'success'
: 'default'
"
>
{{
record.monitorEnabled && record.status === 'enabled'
? '监控中'
: '已停用'
}}
</Tag>
</template>
<template v-else-if="column.key === 'action'">
<Button size="small" type="link" @click="editEndpointRecord(record)">编辑</Button>
<Button
size="small"
type="link"
@click="editEndpointRecord(record)"
>
编辑
</Button>
</template>
</template>
</Table>
@@ -737,7 +788,9 @@ onMounted(refreshAll);
<Tabs.TabPane key="consumers">
<template #tab>
<span class="tab-label"><IconifyIcon icon="lucide:users-round" />调用方</span>
<span class="tab-label"
><IconifyIcon icon="lucide:users-round" />调用方</span
>
</template>
<div class="table-toolbar">
<div>
@@ -757,23 +810,37 @@ onMounted(refreshAll);
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'identity'">
<div class="asset-name">
<div class="asset-icon consumer-icon"><IconifyIcon icon="lucide:building-2" /></div>
<div class="asset-icon consumer-icon">
<IconifyIcon icon="lucide:building-2" />
</div>
<div>
<strong>{{ record.displayName || record.username || '未命名调用方' }}</strong>
<strong>{{
record.displayName || record.username || '未命名调用方'
}}</strong>
<span>{{ record.username }}</span>
</div>
</div>
</template>
<template v-else-if="column.key === 'lastSeenAt'">
<span class="muted-text">{{ formatTime(record.lastSeenAt) }}</span>
<span class="muted-text">{{
formatTime(record.lastSeenAt)
}}</span>
</template>
<template v-else-if="column.key === 'status'">
<Tag :color="record.status === 'active' ? 'success' : 'default'">
<Tag
:color="record.status === 'active' ? 'success' : 'default'"
>
{{ record.status === 'active' ? '活跃' : '停用' }}
</Tag>
</template>
<template v-else-if="column.key === 'action'">
<Button size="small" type="link" @click="editConsumerRecord(record)">编辑</Button>
<Button
size="small"
type="link"
@click="editConsumerRecord(record)"
>
编辑
</Button>
</template>
</template>
</Table>
@@ -781,7 +848,9 @@ onMounted(refreshAll);
<Tabs.TabPane key="usage">
<template #tab>
<span class="tab-label"><IconifyIcon icon="lucide:table-properties" />使用统计</span>
<span class="tab-label"
><IconifyIcon icon="lucide:table-properties" />使用统计</span
>
</template>
<div class="table-toolbar usage-toolbar">
<div>
@@ -813,12 +882,19 @@ onMounted(refreshAll);
<template v-else-if="column.key === 'endpoint'">
<div class="usage-endpoint">
<strong>{{ record.endpointName }}</strong>
<span>{{ serviceName(record.serviceCode) }} · {{ record.category }}</span>
<span
>{{ serviceName(record.serviceCode) }} ·
{{ record.category }}</span
>
</div>
</template>
<template v-else-if="column.key === 'caller'">
<div class="caller-cell">
<Tag :color="record.callerType === 'consumer' ? 'blue' : 'orange'">
<Tag
:color="
record.callerType === 'consumer' ? 'blue' : 'orange'
"
>
{{ record.callerType === 'consumer' ? 'Consumer' : '匿名' }}
</Tag>
<span>{{ record.callerName }}</span>
@@ -840,18 +916,32 @@ onMounted(refreshAll);
<Form class="modal-form" layout="vertical">
<div class="form-grid">
<FormItem label="接口名称" required>
<Input v-model:value="endpointForm.name" placeholder="例如:蓝字发票详情" />
<Input
v-model:value="endpointForm.name"
placeholder="例如:蓝字发票详情"
/>
</FormItem>
<FormItem label="业务分类" required>
<Input v-model:value="endpointForm.category" placeholder="例如:票通开票" />
<Input
v-model:value="endpointForm.category"
placeholder="例如:票通开票"
/>
</FormItem>
<FormItem label="所属服务" required>
<Select v-model:value="endpointForm.serviceCode" :options="serviceOptions" />
<Select
v-model:value="endpointForm.serviceCode"
:options="serviceOptions"
/>
</FormItem>
<FormItem label="HTTP Method">
<Select
v-model:value="endpointForm.httpMethod"
:options="['*', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE'].map((value) => ({ label: value, value }))"
:options="
['*', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE'].map((value) => ({
label: value,
value,
}))
"
/>
</FormItem>
</div>
@@ -863,13 +953,22 @@ onMounted(refreshAll);
</FormItem>
<div class="form-grid">
<FormItem label="Kong Service ID">
<Input v-model:value="endpointForm.kongServiceId" placeholder="可选" />
<Input
v-model:value="endpointForm.kongServiceId"
placeholder="可选"
/>
</FormItem>
<FormItem label="Kong Route ID">
<Input v-model:value="endpointForm.kongRouteId" placeholder="可选" />
<Input
v-model:value="endpointForm.kongRouteId"
placeholder="可选"
/>
</FormItem>
<FormItem label="负责人">
<Input v-model:value="endpointForm.owner" placeholder="部门或负责人" />
<Input
v-model:value="endpointForm.owner"
placeholder="部门或负责人"
/>
</FormItem>
<FormItem label="接口状态">
<Select
@@ -904,7 +1003,10 @@ onMounted(refreshAll);
>
<Form class="modal-form" layout="vertical">
<FormItem label="展示名称">
<Input v-model:value="consumerForm.displayName" placeholder="客户、系统或项目名称" />
<Input
v-model:value="consumerForm.displayName"
placeholder="客户、系统或项目名称"
/>
</FormItem>
<FormItem label="状态">
<Select
@@ -920,7 +1022,10 @@ onMounted(refreshAll);
</FormItem>
<div v-if="editingConsumer" class="consumer-reference">
<span>Kong Consumer</span>
<code>{{ editingConsumer.username }} · {{ editingConsumer.kongConsumerId }}</code>
<code
>{{ editingConsumer.username }} ·
{{ editingConsumer.kongConsumerId }}</code
>
</div>
</Form>
</Modal>