feat(t10-admin): 管理员体系前端——应用设置双 tab+单表单管理员+选人组件移植
build / build (push) Successful in 3m8s

- AppSettings.vue 应用设置页(基础/字典两 tab);修复 P86:Tabs 写 :value 而受控 prop 是 modelValue,致 ?tab=dicts 深链静默失效,改后深链实拍生效
- UserCheckModal.vue 自 notice 移植(view-ui-plus+ztree,sources 注入式数据源);public/libs 收录 jquery+ztree 本地件(index.html 引用)
- api/module/admin.ts + mock/adminapi.ts + types/admin.ts;AdminListPanel/FormAdminModal 组件
- routes/menuNav/menuRoute/modulePerms 适配管理员菜单与权限位;tree-icon 图标资源
This commit is contained in:
chenxing
2026-10-15 20:25:39 +08:00
parent 14d45f7e3c
commit d8f1a1fbfa
30 changed files with 1377 additions and 33 deletions
+64
View File
@@ -0,0 +1,64 @@
/**
* 管理员两表的维护接口(后端 `AdminDomainController`,T10 方案 §5.3)。
*
* 六端点分两组守卫:校级管理员摊 **应用设置行** `/admin/app`(角色设置 tab 与字典 API 同一行),
* 单表单管理员摊 **表单设计行** `/admin/forms`(入口在设计器与表单列表)。
*
* 三处与直觉不同:
* 1. **写操作直接回操作后的全量列表**(不分页),前端不必再发一次 GET;
* 2. **删除按库内行 id**(方案 §5.3 的端点形态),后端还会按 `(id, 本次域)` 复核归属;
* 3. **`schId` 只在超管显式切目标域时带**,校级传别的值即被 FormAdminGuard 拒(同数据面口径)。
*/
import { alovaInstance } from "@/utils/alova/alova-iview";
import type { AdminAddRequest, AdminRow } from "@/types/admin";
const BASE = "/api/form/admin";
type Scope = { schId?: number };
function paramsOf(params: Record<string, unknown>): Record<string, unknown> {
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(params)) {
if (v === undefined || v === null || v === "") continue;
out[k] = v;
}
return out;
}
class AdminApi {
/** 校级表单管理员全量列表 */
schoolAdmins(scope: Scope = {}) {
return alovaInstance.Get<AdminRow[]>(`${BASE}/school-admins`, { params: paramsOf({ ...scope }) });
}
/** 批量添加(幂等:已在表里的人不重复建行,也不报错) */
addSchoolAdmins(body: AdminAddRequest) {
return alovaInstance.Post<AdminRow[]>(`${BASE}/school-admins`, body);
}
/** 删除(防呆:删到剩 0 由后端按业务规则拒) */
removeSchoolAdmin(id: number, scope: Scope = {}) {
return alovaInstance.Delete<AdminRow[]>(`${BASE}/school-admins/${id}`, {
params: paramsOf({ ...scope })
});
}
/** 单表单管理员全量列表 */
formAdmins(formUuid: string, scope: Scope = {}) {
return alovaInstance.Get<AdminRow[]>(`${BASE}/forms/${formUuid}/admins`, {
params: paramsOf({ ...scope })
});
}
addFormAdmins(formUuid: string, body: AdminAddRequest) {
return alovaInstance.Post<AdminRow[]>(`${BASE}/forms/${formUuid}/admins`, body);
}
removeFormAdmin(formUuid: string, id: number, scope: Scope = {}) {
return alovaInstance.Delete<AdminRow[]>(`${BASE}/forms/${formUuid}/admins/${id}`, {
params: paramsOf({ ...scope })
});
}
}
export const adminApi = new AdminApi();
+11 -3
View File
@@ -1,14 +1,14 @@
import { alovaInstance } from "@/utils/alova/alova-iview";
import type { IModule } from "@/types/Basics";
import type { SchoolTreeNode } from "@/types/admin";
/**
* 基础平台接口(notice/front 的 api/module 形态 + flow-center base.ts 同构):
* 用户信息与已授权模块列表(左导航数据源)。
*
* 模块行在 UserBase 按本应用(app_code=form-center)配置,path 须逐字对上
* router/routes.ts(手册 §2.2 表);<b>path 空的行是纯权限锚点</b>(如 customId
* fc:scope:all),只由后端 DataScopeService 扫描授权集合消费,前端按 utils/menuNav.ts
* 的规则跳过渲染——接口本身不过滤,前端拿到的仍是全量授权树。
* router/routes.ts(手册 §2.2 表 v1.5 五行版)。数据范围升级位不再挂在模块行上:
* T10 起由后端管理员两表(school_admin / form_admin)承载,前端只在 /admin/app 的角色设置 tab 维护它。
*
* 写法与 Notice 的一处差异:Notice 用 `new Method<T>(...)`,而 alova 3.5 的 Method 第一个
* 泛型位是 AlovaGenerics(不是响应类型),strict vue-tsc 下报 TS2344;本端改用实例方法
@@ -34,6 +34,14 @@ class BaseApi {
params: appCode ? { appCode } : undefined
});
}
/**
* 本校组织树(教师版)——选人弹窗 UserCheckModal 的 loader,形态同 notice。
* 没有 schId 入参:域只从会话推导,因此候选天然限在本校树里(跨校的人在接口层就给不出来)。
*/
getSchoolAll() {
return alovaInstance.Get<SchoolTreeNode[]>(`${BASE}/GetSchoolAll`);
}
}
export const baseApi = new BaseApi();