From 531f7641fb1a0d770e2c853f4a90a951305ed84c Mon Sep 17 00:00:00 2001 From: chenxing Date: Tue, 20 Oct 2026 14:30:28 +0800 Subject: [PATCH] =?UTF-8?q?t16:=E5=BF=AB=E5=BA=94=E7=94=A8=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E8=A1=A8=E5=85=A5=E5=8F=A3=E2=80=94=E2=80=94=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E8=AE=BE=E7=BD=AE=E7=AC=AC=E4=B8=89=E4=B8=AA=20tab?= =?UTF-8?q?=EF=BC=88=E6=B3=A8=E5=86=8C/=E6=94=B9=E5=90=8D/=E5=A4=8D?= =?UTF-8?q?=E5=88=B6=E5=85=A5=E5=8F=A3=E9=93=BE=E6=8E=A5/=E5=88=A0?= =?UTF-8?q?=E9=99=A4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - api/module/apps.ts:C17 四端点,寻址一律按 appCode(库内 id 不外露,V5)。 - types/data.ts 增 AppRow(四业务字段 + entryUrl);无角色/授权字段(V59 的界面侧表达)。 - views/admin/Apps.vue:注册(C10 格式与保留字做即时预检,但保存失败以后端 msg 为准并留在弹窗里)、 改名(code 不随改,输入框禁用)、复制应用入口(后端给站内路径,前端补 origin,生产/本地都能点开)、 删除(在用禁删与"编码不复用"两类拒绝一律原样提示,前端不复制规则,同 AdminListPanel 口径)。 页面上没有任何"给这个应用配谁能用"的入口——那是有意的。 - AppSettings.vue:tab 值改闭集判定(roles/apps/dicts),?tab=apps 可深链; 入口本身仍由 capabilities.schoolAdmin 决定(S3 第三支路),不做"进了页再藏 tab"。 门禁:npx vue-tsc --noEmit exit 0;npm run build ✓ built in 12.16s。 --- src/api/module/apps.ts | 36 ++++++ src/types/data.ts | 16 +++ src/views/admin/AppSettings.vue | 25 ++-- src/views/admin/Apps.vue | 209 ++++++++++++++++++++++++++++++++ 4 files changed, 279 insertions(+), 7 deletions(-) create mode 100644 src/api/module/apps.ts create mode 100644 src/views/admin/Apps.vue diff --git a/src/api/module/apps.ts b/src/api/module/apps.ts new file mode 100644 index 0000000..6dbea27 --- /dev/null +++ b/src/api/module/apps.ts @@ -0,0 +1,36 @@ +/** + * 快应用注册表 CRUD(契约 v2 C17/§5.2,T16,挂应用设置 · 快应用 tab)。 + * + * 三处与直觉不同: + * 1. **寻址按 `appCode`,不是库内 id**(V5:自增 id 绝不外露)——所以改名/删除的 URL 段是 slug 本身; + * 2. **`schId` 只有超管切目标域时才带**,校管理员传别的值即被 `FormAdminGuard` 拒(同两表端点口径); + * 3. **入口链接由后端生成**(`entryUrl` 是站内路径,按后端自己的 context-path 组段), + * 前端只补 `origin`——生产得到 `/form-center/front/#/app/{code}/`,本地 dev 没有 context 段, + * 得到 `/#/app/{code}/`,两处复制出来的链接都能直接点开。 + */ +import { alovaInstance } from "@/utils/alova/alova-iview"; +import type { AppRow } from "@/types/data"; + +const BASE = "/api/form/admin/apps"; + +/** 清单:本校 ∪ 平台级(软删行不在内) */ +export function listApps(schId?: number) { + const params: Record = {}; + if (schId !== undefined && schId !== null) params.schId = schId; + return alovaInstance.Get(BASE, { params }).send(); +} + +/** 建(app_code + name;无审核,D14) */ +export function createApp(body: { appCode: string; name: string }) { + return alovaInstance.Post(BASE, body).send(); +} + +/** 改名——code 不随改(它是入口链接与 oapi/MQ 的域段) */ +export function renameApp(appCode: string, name: string) { + return alovaInstance.Put(`${BASE}/${encodeURIComponent(appCode)}`, { name }).send(); +} + +/** 软删(名下有未删除表单时后端按 CUSTOM(5) 拒,msg 指点先迁先删) */ +export function deleteApp(appCode: string) { + return alovaInstance.Delete(`${BASE}/${encodeURIComponent(appCode)}`).send(); +} diff --git a/src/types/data.ts b/src/types/data.ts index 9b6a6f7..2ba267e 100644 --- a/src/types/data.ts +++ b/src/types/data.ts @@ -171,6 +171,22 @@ export interface Capabilities { schoolAdmin: boolean; } +/** + * 快应用注册行(契约 v2 C17/§5.2,`GET /api/form/admin/apps`,T16)。 + * + * 四个业务字段 + 中心生成的入口链接;**没有角色/授权列**(V59 红线:谁能用哪个应用一律由 + * 会话在该应用表单上的 ALL/SELF 档现算,注册表不存授权痕迹)。 + */ +export interface AppRow { + /** 全局唯一人读 slug(C10 格式;保留字 form-center 不可注册),发布后不随改 */ + appCode: string; + name: string; + /** 归属租户域;0=平台级(仅超管可建) */ + schId: number; + /** 应用入口链接(站内路径,§5.2"由中心生成")——复制后贴去 UserBase 建应用 */ + entryUrl: string; +} + /** 范围名 → 展示文案;未知名原样显示(后端加枚举值不破坏前端) */ export function scopeLabel(name: string): string { return DATA_SCOPE_LABELS[name] ?? name; diff --git a/src/views/admin/AppSettings.vue b/src/views/admin/AppSettings.vue index fff378b..7cbefd9 100644 --- a/src/views/admin/AppSettings.vue +++ b/src/views/admin/AppSettings.vue @@ -1,28 +1,36 @@ + + + +