修正发布临时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
+1 -1
View File
@@ -53,7 +53,7 @@ async def ai_lab():
origins = [
"http://localhost:8091", # Vite dev 默认端口
"https://ai.bbitcn.net",
"*", # ⚠️ 生产环境不要用
"*",
]
app.add_middleware(
+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>
+3 -3
View File
@@ -18,7 +18,7 @@ Integrated with the [iconify](https://github.com/iconify/iconify) icon library
You can add new icons in the `packages/icons/src/iconify` directory:
```ts
// packages/icons/src/iconify/index.ts
// packages/icons/src/iconify/open-api.ts
import { createIconifyIcon } from '@vben-core/icons';
export const MdiKeyboardEsc = createIconifyIcon('mdi:keyboard-esc');
@@ -43,10 +43,10 @@ Instead of using Svg Sprite, SVG icons are directly imported,
### Adding New Icons
You can add new icon files `test.svg` in the `packages/icons/src/svg/icons` directory, and then import it in `packages/icons/src/svg/index.ts`:
You can add new icon files `test.svg` in the `packages/icons/src/svg/icons` directory, and then import it in `packages/icons/src/svg/open-api.ts`:
```ts
// packages/icons/src/svg/index.ts
// packages/icons/src/svg/open-api.ts
import { createIconifyIcon } from '@vben-core/icons';
const SvgTestIcon = createIconifyIcon('svg:test');
+3 -3
View File
@@ -160,7 +160,7 @@ When making interface requests through the project's built-in `request` tool, th
:::
Each application has an independent language pack that can override the general language configuration. You can remotely load the corresponding language pack by finding the `src/locales/index.ts` file in the corresponding application and modifying the `loadMessages` method accordingly:
Each application has an independent language pack that can override the general language configuration. You can remotely load the corresponding language pack by finding the `src/locales/open-api.ts` file in the corresponding application and modifying the `loadMessages` method accordingly:
```ts {3-4}
async function loadMessages(lang: SupportedLanguagesType) {
@@ -175,7 +175,7 @@ async function loadMessages(lang: SupportedLanguagesType) {
## Third-Party Language Packs
Different applications may use third-party component libraries or plugins with varying internationalization methods, so they need to be handled differently. If you need to introduce a third-party language pack, you can find the `src/locales/index.ts` file in the corresponding application and modify the `loadThirdPartyMessage` method accordingly:
Different applications may use third-party component libraries or plugins with varying internationalization methods, so they need to be handled differently. If you need to introduce a third-party language pack, you can find the `src/locales/open-api.ts` file in the corresponding application and modify the `loadThirdPartyMessage` method accordingly:
```ts
/**
@@ -212,7 +212,7 @@ Firstly, it is not recommended to remove internationalization, as it is a good d
- Hide the language switching button on the interface, see: [Interface Language Switching Function](#interface-language-switching-function)
- Modify the default language, see: [Configure Default Language](#configure-default-language)
- Disable `vue-i18n` warning prompts, in the `src/locales/index.ts` file, modify `missingWarn` to `false`:
- Disable `vue-i18n` warning prompts, in the `src/locales/open-api.ts` file, modify `missingWarn` to `false`:
```ts
async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
+1 -1
View File
@@ -110,7 +110,7 @@ registry = https://registry.npmmirror.com/
If you encounter errors similar to the following, please check that the full project path (including all parent paths) does not contain Chinese, Japanese, or Korean characters. Otherwise, you will encounter a 404 error for the path, leading to the following issue:
```ts
[vite] Failed to resolve module import "ant-design-vue/dist/antd.css-vben-adminode_modulesant-design-vuedistantd.css". (imported by /@/setup/ant-design-vue/index.ts)
[vite] Failed to resolve module import "ant-design-vue/dist/antd.css-vben-adminode_modulesant-design-vuedistantd.css". (imported by /@/setup/ant-design-vue/open-api.ts)
```
## Console Route Warning Issue
+3 -3
View File
@@ -18,7 +18,7 @@
可在 `packages/icons/src/iconify` 目录下新增图标:
```ts
// packages/icons/src/iconify/index.ts
// packages/icons/src/iconify/open-api.ts
import { createIconifyIcon } from '@vben-core/icons';
export const MdiKeyboardEsc = createIconifyIcon('mdi:keyboard-esc');
@@ -43,10 +43,10 @@ import { MdiKeyboardEsc } from '@vben/icons';
### 新增
可以在 `packages/icons/src/svg/icons` 目录下新增图标文件`test.svg`, 然后在 `packages/icons/src/svg/index.ts` 中引入:
可以在 `packages/icons/src/svg/icons` 目录下新增图标文件`test.svg`, 然后在 `packages/icons/src/svg/open-api.ts` 中引入:
```ts
// packages/icons/src/svg/index.ts
// packages/icons/src/svg/open-api.ts
import { createIconifyIcon } from '@vben-core/icons';
const SvgTestIcon = createIconifyIcon('svg:test');
+3 -3
View File
@@ -160,7 +160,7 @@ export const overridesPreferences = defineOverridesPreferences({
:::
每个应用都有一个独立的语言包,它可以覆盖通用的语言配置,你可以通过远程加载的方式来获取对应的语言包,只需要在对应的应用内,找到 `src/locales/index.ts` 文件,修改 `loadMessages` 方法即可:
每个应用都有一个独立的语言包,它可以覆盖通用的语言配置,你可以通过远程加载的方式来获取对应的语言包,只需要在对应的应用内,找到 `src/locales/open-api.ts` 文件,修改 `loadMessages` 方法即可:
```ts {3-4}
async function loadMessages(lang: SupportedLanguagesType) {
@@ -175,7 +175,7 @@ async function loadMessages(lang: SupportedLanguagesType) {
## 第三方语言包
不同应用内使用的第三方组件库或者插件国际化方式可能不一致,所以需要差别处理。 如果你需要引入第三方的语言包,你可以在对应的应用内,找到 `src/locales/index.ts` 文件,修改 `loadThirdPartyMessage` 方法即可:
不同应用内使用的第三方组件库或者插件国际化方式可能不一致,所以需要差别处理。 如果你需要引入第三方的语言包,你可以在对应的应用内,找到 `src/locales/open-api.ts` 文件,修改 `loadThirdPartyMessage` 方法即可:
```ts
/**
@@ -212,7 +212,7 @@ async function loadDayjsLocale(lang: SupportedLanguagesType) {
- 隐藏界面上的语言切换按钮,见:[界面切换语言功能](#界面切换语言功能)
- 修改默认语言,见:[配置默认语言](#配置默认语言)
- 关闭 `vue-i18n`的警告提示,在`src/locales/index.ts`文件内,修改`missingWarn`为`false`即可:
- 关闭 `vue-i18n`的警告提示,在`src/locales/open-api.ts`文件内,修改`missingWarn`为`false`即可:
```ts
async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
+1 -1
View File
@@ -110,7 +110,7 @@ registry = https://registry.npmmirror.com/
如果出现类似以下错误,请检查项目全路径(包含所有父级路径)不能出现中文、日文、韩文。否则将会出现路径访问 404 导致以下问题
```ts
[vite] Failed to resolve module import "ant-design-vue/dist/antd.css-vben-adminode_modulesant-design-vuedistantd.css". (imported by /@/setup/ant-design-vue/index.ts)
[vite] Failed to resolve module import "ant-design-vue/dist/antd.css-vben-adminode_modulesant-design-vuedistantd.css". (imported by /@/setup/ant-design-vue/open-api.ts)
```
## 控制台路由警告问题
+2 -2
View File
@@ -13,8 +13,8 @@
```json
"exports": {
"./echarts": {
"types": "./src/echarts/index.ts",
"default": "./src/echarts/index.ts"
"types": "./src/echarts/open-api.ts",
"default": "./src/echarts/open-api.ts"
}
}
```
+1 -1
View File
@@ -1,7 +1,7 @@
# push_docker.ps1
# Set version
$env:VERSION = "1.5.8"
$env:VERSION = "1.5.8.1"
# Docker registry/repository
$registry = "docker.bbitcn.net/bbit_ai/ce_vue"