feat(front): 网关台账前端——台账/路由/目标三页+重载注册表
- 与 notice 同形态:hash 路由 + TablePageAlovaNew 分页 + alova 信封分支 - API 层手写(文档端点仅容器网内可达,api:gen 无通路),键名对齐后端 operationId - 审计修复:outDir 回 dist 根(CI zip 根须含 html)、main.ts 显式引入 @/api 挂载全局 Apis、 保存失败重拉回滚、路由页补只读 timeout/bodyLimit 列、详情弹窗 backendCode 恒空说明
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div class="main-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="search-wrap fix">
|
||||
<span class="tip">路由注册表(open_api_route):治理旋钮行内编辑,保存后网关热重载,无须重启。</span>
|
||||
<Button type="primary" class="fr" @click="refresh">刷新</Button>
|
||||
</div>
|
||||
|
||||
<TablePageAlovaNew ref="tableRef" :dataApi="tableApi" :columns="columns" :tableHeight="tableHeight">
|
||||
<template #visibility="{ row }">
|
||||
<Select v-model="row.visibility" size="small" style="width: 110px">
|
||||
<Option value="INTERNAL">INTERNAL</Option>
|
||||
<Option value="EXTERNAL">EXTERNAL</Option>
|
||||
</Select>
|
||||
</template>
|
||||
<template #authMode="{ row }">
|
||||
<Select v-model="row.authMode" size="small" style="width: 120px">
|
||||
<Option value="SIGNED">SIGNED</Option>
|
||||
<Option value="ANONYMOUS">ANONYMOUS</Option>
|
||||
</Select>
|
||||
</template>
|
||||
<template #cacheTtlSeconds="{ row }">
|
||||
<Input v-model="row.cacheTtlSeconds" type="number" size="small" style="width: 80px" />
|
||||
</template>
|
||||
<template #cacheScope="{ row }">
|
||||
<Select v-model="row.cacheScope" size="small" style="width: 100px">
|
||||
<Option value="CALLER">CALLER</Option>
|
||||
<Option value="GLOBAL">GLOBAL</Option>
|
||||
</Select>
|
||||
</template>
|
||||
<template #rateLimitQps="{ row }">
|
||||
<Input v-model="row.rateLimitQps" type="number" size="small" style="width: 80px" />
|
||||
</template>
|
||||
<template #enabled="{ row }">
|
||||
<i-switch v-model="row.enabled" size="small" />
|
||||
</template>
|
||||
<template #remark="{ row }">
|
||||
<Input v-model="row.remark" size="small" placeholder="-" />
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<Button type="primary" size="small" :loading="savingId === row.id" @click="save(row)">保存</Button>
|
||||
</template>
|
||||
</TablePageAlovaNew>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import useCurrentInstance from '@/utils/hook/useCurrentInstance';
|
||||
import TablePageAlovaNew from '@/components/TablePageAlovaNew.vue';
|
||||
|
||||
defineOptions({ name: 'RouteList' });
|
||||
|
||||
const { $utils } = useCurrentInstance();
|
||||
|
||||
const tableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
|
||||
const savingId = ref<number | null>(null);
|
||||
|
||||
const tableApi = (params: { pageIndex: number; pageSize: number }) =>
|
||||
Apis.GatewayAdmin.routes({ params, cacheFor: null });
|
||||
|
||||
const tableHeight = ref($utils.useTableHeight({ isOperat: false, isPage: true, isSearch: true }));
|
||||
|
||||
const columns = [
|
||||
{ title: 'id', key: 'id', width: 70, align: 'center' },
|
||||
{ title: 'target', key: 'targetCode', width: 120, ellipsis: true, tooltip: true },
|
||||
{ title: 'path', key: 'pathPattern', minWidth: 200, ellipsis: true, tooltip: true },
|
||||
{ title: 'method', key: 'httpMethod', width: 90, align: 'center' },
|
||||
{ title: 'visibility', slot: 'visibility', width: 130 },
|
||||
{ title: 'authMode', slot: 'authMode', width: 140 },
|
||||
{ title: 'cacheTtl(s)', slot: 'cacheTtlSeconds', width: 105 },
|
||||
{ title: 'cacheScope', slot: 'cacheScope', width: 120 },
|
||||
{ title: 'qps', slot: 'rateLimitQps', width: 90 },
|
||||
{ title: 'timeout(ms)', key: 'timeoutMs', width: 105, align: 'right' },
|
||||
{ title: 'bodyLimit(mb)', key: 'requestBodyLimitMb', width: 115, align: 'right' },
|
||||
{ title: '启用', slot: 'enabled', width: 80, align: 'center' },
|
||||
{ title: '备注', slot: 'remark', minWidth: 160 },
|
||||
{ title: '操作', slot: 'action', width: 90, align: 'center', fixed: 'right' as const },
|
||||
];
|
||||
|
||||
function refresh() {
|
||||
tableRef.value?.refreshData();
|
||||
}
|
||||
|
||||
/** 只提交治理字段(target/pattern/method 是路由身份,后端不接受改动) */
|
||||
async function save(row: any) {
|
||||
savingId.value = row.id;
|
||||
try {
|
||||
const d: any = await Apis.GatewayAdmin.updateRoute({
|
||||
pathParams: { id: row.id },
|
||||
data: {
|
||||
visibility: row.visibility,
|
||||
authMode: row.authMode,
|
||||
cacheTtlSeconds: Number(row.cacheTtlSeconds) || 0,
|
||||
cacheScope: row.cacheScope,
|
||||
rateLimitQps: Number(row.rateLimitQps) || 0,
|
||||
enabled: row.enabled,
|
||||
remark: row.remark,
|
||||
},
|
||||
});
|
||||
Message.success(`已保存,注册表加载 ${d?.loaded ?? '-'} 条路由`);
|
||||
} catch (e: any) {
|
||||
Message.error(e?.message || '保存失败');
|
||||
// 失败不留假状态:重拉列表,行 UI 回滚到服务端真值
|
||||
tableRef.value?.refreshData();
|
||||
} finally {
|
||||
savingId.value = null;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-wrap {
|
||||
line-height: 32px;
|
||||
|
||||
.tip {
|
||||
color: #848c98;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user