feat(app): 应用设置页(107)+ 流程定义/编辑器应用下拉改读白名单显示应用名
- AppSetting.vue:tab1 应用管理(学校池同步说明+开关 i-switch);tab2 科室管理占位(Part E) - Defs.vue 筛选下拉与表格应用列显示 appName;编辑器归属应用下拉去 allow-create、限启用集合 - flow.ts 增 appList/appEnable(defAppKeys 保留 deprecated)
This commit is contained in:
@@ -126,6 +126,17 @@ class FlowApi {
|
|||||||
defAppKeys() {
|
defAppKeys() {
|
||||||
return get(`${ROOT}/admin/def/appkeys`);
|
return get(`${ROOT}/admin/def/appkeys`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 管理面:应用设置(2026-10-10) ====================
|
||||||
|
|
||||||
|
/** 应用管理列表(flw_app 白名单;附带学校池静默同步,UserBase 不可达降级本地行) */
|
||||||
|
appList() {
|
||||||
|
return get(`${ROOT}/admin/app/list`);
|
||||||
|
}
|
||||||
|
/** 开关某应用是否对接流程(停用后 defSave 归属应用校验拦截新引用) */
|
||||||
|
appEnable(id: number | string, enabled: boolean) {
|
||||||
|
return post(`${ROOT}/admin/app/enable`, { id, enabled });
|
||||||
|
}
|
||||||
defGet(id: number | string) {
|
defGet(id: number | string) {
|
||||||
return get(`${ROOT}/admin/def/get`, { id });
|
return get(`${ROOT}/admin/def/get`, { id });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,14 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: () => import("@/views/flow/admin/Routers.vue"),
|
component: () => import("@/views/flow/admin/Routers.vue"),
|
||||||
meta: { title: "条件路由", modId: 106 },
|
meta: { title: "条件路由", modId: 106 },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// 应用设置(2026-10-10 四议题):tab1 应用管理(flw_app 白名单,学校池同步);
|
||||||
|
// tab2 科室管理(Part E 填充)。菜单需在 UserBase 侧配置同 path 的 app_module(custom_id=107)
|
||||||
|
path: "/admin/app-setting",
|
||||||
|
name: "flow_admin_app_setting",
|
||||||
|
component: () => import("@/views/flow/admin/AppSetting.vue"),
|
||||||
|
meta: { title: "应用设置", modId: 107 },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// 办件进度嵌入页(iframe 免签嵌入,App.vue 对 /embed/* 前缀旁路 CAS 与 layout):
|
// 办件进度嵌入页(iframe 免签嵌入,App.vue 对 /embed/* 前缀旁路 CAS 与 layout):
|
||||||
// 不挂菜单、无 parentId(子应用 Drawer 内打开,非管理面导航);query.token 必填
|
// 不挂菜单、无 parentId(子应用 Drawer 内打开,非管理面导航);query.token 必填
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// 应用设置(2026-10-10 四议题):tab1 应用管理(flw_app 白名单 + 学校池同步 + 开关);
|
||||||
|
// tab2 科室管理(Part E 填充,本版本占位)。
|
||||||
|
// 应用开通过程在 UserBase 管理端完成(学校组开通);本页只决定"已开通的应用里哪些对接流程"。
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import { Message } from "view-ui-plus";
|
||||||
|
import MyConfirm from "@/components/MyConfirm";
|
||||||
|
import { flowApi } from "@/api/module/flow";
|
||||||
|
import Utils from "@/utils";
|
||||||
|
|
||||||
|
const tab = ref("apps");
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const rows = ref<any[]>([]);
|
||||||
|
const tableHeight = ref(Utils.useTableHeight({ isOperat: true }));
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ title: "序号", type: "index", width: 70, align: "center" },
|
||||||
|
{ title: "应用", key: "appName", minWidth: 200 },
|
||||||
|
{ title: "appKey", key: "appKey", width: 120, align: "center" },
|
||||||
|
{
|
||||||
|
title: "对接流程",
|
||||||
|
width: 120,
|
||||||
|
align: "center",
|
||||||
|
render: (h: any, p: any) =>
|
||||||
|
h("i-switch", {
|
||||||
|
props: { value: p.row.enabled === 1, size: "large", loading: p.row._switching },
|
||||||
|
on: { "on-change": (v: boolean) => onToggle(p.row, v) },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "状态",
|
||||||
|
width: 90,
|
||||||
|
align: "center",
|
||||||
|
render: (h: any, p: any) =>
|
||||||
|
h("Tag", { props: { color: p.row.enabled === 1 ? "green" : "gray" } },
|
||||||
|
() => (p.row.enabled === 1 ? "已开启" : "已停用")),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
rows.value = ((await flowApi.appList().send()) as any[]) || [];
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onToggle(row: any, enabled: boolean) {
|
||||||
|
row._switching = true;
|
||||||
|
try {
|
||||||
|
await flowApi.appEnable(row.id, enabled).send();
|
||||||
|
row.enabled = enabled ? 1 : 0;
|
||||||
|
Message.success(enabled ? `已开启「${row.appName}」` : `已停用「${row.appName}」`);
|
||||||
|
} catch (e: any) {
|
||||||
|
Message.error(e?.message || "操作失败");
|
||||||
|
// 失败回滚开关显示(重新拉取)
|
||||||
|
load();
|
||||||
|
} finally {
|
||||||
|
row._switching = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRefresh() {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(load);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="main-container">
|
||||||
|
<Tabs v-model="tab">
|
||||||
|
<TabPane label="应用管理" name="apps">
|
||||||
|
<div class="fix mb-12">
|
||||||
|
<Button class="fl" type="primary" size="large" @click="onRefresh">刷新(同步学校池)</Button>
|
||||||
|
<span class="fl" style="color: #808695; font-size: 12px; line-height: 40px; margin-left: 10px">
|
||||||
|
应用清单来自本校已开通应用(UserBase 学校组开通,含学校别名);开启后可用于流程归属与筛选。
|
||||||
|
新开通/改名在 UserBase 管理端操作后回本页刷新即可
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Table :columns="columns" :data="rows" :loading="loading" class="link-table table-bottom-box"
|
||||||
|
:height="tableHeight" no-data-text="本校暂无已开通应用(请先在用户中心开通)" />
|
||||||
|
</TabPane>
|
||||||
|
<TabPane label="科室管理" name="depts" disabled>
|
||||||
|
<div style="padding: 40px; text-align: center; color: #808695">科室管理建设中(即将上线)</div>
|
||||||
|
</TabPane>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -23,15 +23,16 @@ const sourceText = ref("");
|
|||||||
const sourceTitle = ref("");
|
const sourceTitle = ref("");
|
||||||
// 操作按钮区 + 表格,无搜索、无分页
|
// 操作按钮区 + 表格,无搜索、无分页
|
||||||
const tableHeight = ref(Utils.useTableHeight({ isOperat: true }));
|
const tableHeight = ref(Utils.useTableHeight({ isOperat: true }));
|
||||||
// 按应用筛选(flw_def.app_key,2026-10-10):下拉数据源来自 /def/appkeys
|
// 按应用筛选(flw_def.app_key,2026-10-10):下拉数据源来自应用设置白名单(flw_app enabled=1)
|
||||||
const appKeys = ref<string[]>([]);
|
const apps = ref<{ appKey: string; appName: string }[]>([]);
|
||||||
|
const appMap = ref<Record<string, string>>({});
|
||||||
const filterApp = ref("");
|
const filterApp = ref("");
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: "序号", type: "index", width: 70, align: "center" },
|
{ title: "序号", type: "index", width: 70, align: "center" },
|
||||||
{ title: "流程", key: "name", minWidth: 180 },
|
{ title: "流程", key: "name", minWidth: 180 },
|
||||||
{ title: "编码", key: "code", width: 160 },
|
{ title: "编码", key: "code", width: 160 },
|
||||||
{ title: "应用", key: "appKey", width: 110, align: "center", render: (h: any, p: any) => h("span", p.row.appKey || "-") },
|
{ title: "应用", key: "appKey", width: 130, align: "center", render: (h: any, p: any) => h("span", p.row.appKey ? (appMap.value[p.row.appKey] || p.row.appKey) : "-") },
|
||||||
{ title: "版本", key: "version", width: 70, align: "center" },
|
{ title: "版本", key: "version", width: 70, align: "center" },
|
||||||
{ title: "状态", width: 90, align: "center", render: (h: any, p: any) => {
|
{ title: "状态", width: 90, align: "center", render: (h: any, p: any) => {
|
||||||
const c = p.row.status === 1 ? "green" : p.row.status === 0 ? "orange" : "gray";
|
const c = p.row.status === 1 ? "green" : p.row.status === 0 ? "orange" : "gray";
|
||||||
@@ -68,12 +69,18 @@ async function load() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 应用清单(筛选下拉):flw_def 已标注 ∪ 已注册回调的应用,失败降级为空下拉 */
|
/** 应用清单(筛选下拉):应用设置白名单(flw_app enabled=1),失败降级为空下拉 */
|
||||||
async function loadAppKeys() {
|
async function loadAppKeys() {
|
||||||
try {
|
try {
|
||||||
appKeys.value = (await flowApi.defAppKeys().send()) || [];
|
const rows: any[] = (await flowApi.appList().send()) || [];
|
||||||
|
const enabled = rows.filter((r) => r.enabled === 1);
|
||||||
|
apps.value = enabled.map((r) => ({ appKey: String(r.appKey), appName: r.appName || String(r.appKey) }));
|
||||||
|
const map: Record<string, string> = {};
|
||||||
|
rows.forEach((r) => { if (r.appKey != null) map[String(r.appKey)] = r.appName || String(r.appKey); });
|
||||||
|
appMap.value = map;
|
||||||
} catch {
|
} catch {
|
||||||
appKeys.value = [];
|
apps.value = [];
|
||||||
|
appMap.value = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +182,7 @@ onMounted(() => {
|
|||||||
<Button class="fl" type="primary" size="large" @click="onNew">新建流程</Button>
|
<Button class="fl" type="primary" size="large" @click="onNew">新建流程</Button>
|
||||||
<Select v-model="filterApp" clearable filterable placeholder="按应用筛选" class="fl"
|
<Select v-model="filterApp" clearable filterable placeholder="按应用筛选" class="fl"
|
||||||
style="width: 200px; margin-left: 8px" @on-change="load">
|
style="width: 200px; margin-left: 8px" @on-change="load">
|
||||||
<Option v-for="k in appKeys" :key="k" :value="k">{{ k }}</Option>
|
<Option v-for="a in apps" :key="a.appKey" :value="a.appKey">{{ a.appName }}</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ const sourceStatus = ref(0);
|
|||||||
const sourceVersion = ref(0);
|
const sourceVersion = ref(0);
|
||||||
/** 归属应用(flw_def.app_key;按应用筛选维度,空=未分配) */
|
/** 归属应用(flw_def.app_key;按应用筛选维度,空=未分配) */
|
||||||
const appKey = ref("");
|
const appKey = ref("");
|
||||||
/** 应用清单下拉(本校已标注 ∪ 已注册回调),拉取失败降级为仅手输 */
|
/** 应用清单下拉(应用设置白名单 flw_app enabled=1,显示应用名),拉取失败降级为仅当前值 */
|
||||||
const appKeys = ref<string[]>([]);
|
const appOptions = ref<{ appKey: string; appName: string }[]>([]);
|
||||||
|
|
||||||
const model = reactive<EditorModel>(createEmptyModel());
|
const model = reactive<EditorModel>(createEmptyModel());
|
||||||
/** 科室映射聚合(mapList):仅用于下拉选项与"无启用办理人"提示 */
|
/** 科室映射聚合(mapList):仅用于下拉选项与"无启用办理人"提示 */
|
||||||
@@ -177,9 +177,12 @@ async function load(): Promise<void> {
|
|||||||
await loadDepts();
|
await loadDepts();
|
||||||
await loadRouters();
|
await loadRouters();
|
||||||
try {
|
try {
|
||||||
appKeys.value = ((await flowApi.defAppKeys().send()) as string[]) || [];
|
const rows: any[] = (await flowApi.appList().send()) || [];
|
||||||
|
appOptions.value = rows
|
||||||
|
.filter((r) => r.enabled === 1)
|
||||||
|
.map((r) => ({ appKey: String(r.appKey), appName: r.appName || String(r.appKey) }));
|
||||||
} catch {
|
} catch {
|
||||||
appKeys.value = [];
|
appOptions.value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryId = route.query.id ? Number(route.query.id) : null;
|
const queryId = route.query.id ? Number(route.query.id) : null;
|
||||||
@@ -484,9 +487,8 @@ onUnmounted(() => window.removeEventListener("beforeunload", onBeforeUnload));
|
|||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
应用
|
应用
|
||||||
<Select v-model="appKey" placeholder="归属应用,可空" style="width: 150px"
|
<Select v-model="appKey" placeholder="归属应用,可空" style="width: 170px" clearable filterable>
|
||||||
clearable filterable allow-create>
|
<Option v-for="a in appOptions" :key="a.appKey" :value="a.appKey">{{ a.appName }}</Option>
|
||||||
<Option v-for="k in appKeys" :key="k" :value="k">{{ k }}</Option>
|
|
||||||
</Select>
|
</Select>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user