t14:前端模块行退役——左导航三支路自解析、AppContext 重构为通用壳两视图
- App.vue:引导只剩 GetUserInfo,之后并行取 fillable/managed/capabilities 三支构造导航
(取不到按空集/false 走,fail-closed,不造兜底菜单树);摘 GetUserMod 调用与 setModules
- store:modules/IModule 状态与 setModules 删除,改存三支路数据 + managesForm() 判"这张表我管不管得动"
- AppContext.vue:重写为通用壳两视图(§5.1)——管理员视图=数据列表+复制填报路径
(添加/导入 T18 接进这一档),填报者视图=仅填报 tab;判定只看 managed 是否非空,
壳零自有权限逻辑;GetUserMod?appCode= 拉目标应用树那一路与 :modules prop 链整条拆除
- Data.vue:导出按钮改"该表单 ALL 档(managed 成员)",中心与快应用两种上下文从此同一条判据;
AnswerDetail.vue 的管理动作区同判据;utils/modulePerms.ts(canExport/hasPage)删除
- utils/menuNav.ts:buildNavTree 与"从树渲染菜单"三条规则删除,只留导航数据结构与
高亮/面包屑/默认跳转;menuRoute 的 /f/{code} 形态识别按任务书保留(分享直链/历史链接)
- mock:baseapi 去 GetUserMod 与两棵模块树;dataapi 补 /my/managed 与 /my/capabilities 桩
This commit is contained in:
+5
-151
@@ -2,150 +2,16 @@
|
||||
* 表单中心本地 Mock(vite-plugin-mock,仅 `npm run dev:mock` 即 --mode mock 生效)。
|
||||
*
|
||||
* 背景与授权口径:执行手册 §5 P1 的降级取证先例——本地 CAS/UserBase 不可用时,
|
||||
* 以本文件的模块树验证「壳渲染 + 左导航 + path 空节点跳过」,并把结论记入问题单。
|
||||
* 用本文件的桩验证「壳渲染 + 自解析左导航」,并把结论记入问题单。
|
||||
*
|
||||
* 形状严格对齐后端 FormBaseController.toFrontendTree 的下发字段
|
||||
* (id/modId/name/parentId/page/level/icon/orderNumber/usable/children,信封 {code,msg,data}),
|
||||
* 模块行取执行手册 §2.2 的 v1.5 现行表(中心管理员四行 + 我的填写,共五行)。
|
||||
* 契约 v2 §2.4(T14)之后本桩只剩两把基础端点:`GetUserInfo` 与 `GetSchoolAll`。
|
||||
* 原 `GetUserMod` 与两棵模块树桩(中心五行 / 快应用演练行)随模块行整条退役删除——
|
||||
* 左导航改由三支路(`my/fillable`、`my/managed`、`my/capabilities`)自解析,
|
||||
* 那三支的桩在 `dataapi.ts` 里。
|
||||
*/
|
||||
import type { IModule } from "@/types/Basics";
|
||||
|
||||
const ok = (data: any) => ({ code: 0, msg: "", data });
|
||||
|
||||
/** 中心应用(form-center)自身的模块树 */
|
||||
const OWN_MODULES: IModule[] = [
|
||||
{
|
||||
id: 9101,
|
||||
modId: 9101,
|
||||
name: "表单设计",
|
||||
parentId: 0,
|
||||
page: "/admin/forms",
|
||||
level: 1,
|
||||
icon: "word",
|
||||
orderNumber: 1,
|
||||
usable: true,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 9102,
|
||||
modId: 9102,
|
||||
name: "数据管理",
|
||||
parentId: 0,
|
||||
page: "/admin/data",
|
||||
level: 1,
|
||||
icon: "category",
|
||||
orderNumber: 2,
|
||||
usable: true,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 9103,
|
||||
modId: 9103,
|
||||
name: "统计",
|
||||
parentId: 0,
|
||||
page: "/admin/stats",
|
||||
level: 1,
|
||||
icon: "tongji",
|
||||
orderNumber: 3,
|
||||
usable: true,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 9104,
|
||||
modId: 9104,
|
||||
name: "应用设置",
|
||||
parentId: 0,
|
||||
page: "/admin/app",
|
||||
level: 1,
|
||||
icon: "zidingyi",
|
||||
orderNumber: 4,
|
||||
usable: true,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 9105,
|
||||
modId: 9105,
|
||||
name: "我的填写",
|
||||
parentId: 0,
|
||||
page: "/my",
|
||||
level: 1,
|
||||
// 平台未配置图标 → layout 兜底 icon-list(规范 §4 / Notice 13afb22)
|
||||
icon: "",
|
||||
orderNumber: 5,
|
||||
usable: true,
|
||||
children: []
|
||||
}
|
||||
// v1.5(T10)起中心应用就这五行:字典管理行撤(迁入 /admin/app 的页内 tab)、
|
||||
// "数据全量"path 空锚点行撤(UserBase 侧 customId 权限锚点协议退役,数据范围升级改由后端管理员两表承载)。
|
||||
// 本地要复现旧六行版的登录态(V46 的"不再出现字典管理导航项")请用 t10 冒烟脚本里的临时树覆盖,
|
||||
// 不要把遗留行写回这份 mock——它是现行配置的镜像。
|
||||
];
|
||||
|
||||
/**
|
||||
* 第一个快应用的模块树(T9 演练桩,P95 W6 改版为终态形态)。
|
||||
*
|
||||
* 只在 `appCode=fc-drill` 时下发,用来在本机把 **P76(导出锚点按应用树判)/ P77(应用内详情形态)**
|
||||
* 跑到可实拍的程度——真会话取证要等 H2/H4(执行手册 §2 人工项)。
|
||||
* 其余外指 appCode 仍旧返回空树:那是 H4 未上线时的既定口径(P3),桩不能替平台把没上线的能力假装成有。
|
||||
*
|
||||
* <b>P95 起这棵树里没有「填报」行了</b>:`/f/{code}` 模块行不再承担填报授权
|
||||
* (入口由中心按三层模型解析,见 `/api/form/my/fillable` 桩),本页的树只剩两种职责——
|
||||
* 数据查看/统计两个页面行,和 path 空的导入/导出按钮锚点(P76 判的就是这两行)。
|
||||
* 与 `service/sql/h2_userbase_app_and_modules.sql` 的快应用段同一份终态。
|
||||
*/
|
||||
const DRILL_APP_MODULES: IModule[] = [
|
||||
{
|
||||
id: 9202,
|
||||
modId: 9202,
|
||||
name: "数据查看",
|
||||
parentId: 0,
|
||||
page: "/app/fc-drill/data",
|
||||
level: 1,
|
||||
icon: "category",
|
||||
orderNumber: 2,
|
||||
usable: true,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 9203,
|
||||
modId: 9203,
|
||||
name: "统计",
|
||||
parentId: 0,
|
||||
page: "/app/fc-drill/stats",
|
||||
level: 1,
|
||||
icon: "tongji",
|
||||
orderNumber: 3,
|
||||
usable: true,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
// 1.5 预留位:path 空锚点(方案 §6.2 表第四行)——不进导航
|
||||
id: 9204,
|
||||
modId: 9204,
|
||||
name: "导入",
|
||||
parentId: 0,
|
||||
page: "",
|
||||
level: 1,
|
||||
icon: "",
|
||||
orderNumber: 4,
|
||||
usable: true,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
// 按钮级权限锚点:导出按钮显隐按它判(V26 第一半句,P76 修的就是"判的是哪棵树")
|
||||
id: 9205,
|
||||
modId: 9205,
|
||||
name: "导出",
|
||||
parentId: 0,
|
||||
page: "",
|
||||
level: 1,
|
||||
icon: "",
|
||||
orderNumber: 5,
|
||||
usable: true,
|
||||
children: []
|
||||
}
|
||||
];
|
||||
|
||||
export default [
|
||||
{
|
||||
url: "/api/base/GetUserInfo",
|
||||
@@ -163,18 +29,6 @@ export default [
|
||||
school: { id: 3260, name: "云海附属中学" }
|
||||
})
|
||||
},
|
||||
{
|
||||
url: "/api/base/GetUserMod",
|
||||
method: "get",
|
||||
// 与后端同一条判定:appCode 指向别的应用 → 空树(H4 未上线口径,P3:前端不得回落本应用树)。
|
||||
// 唯一例外 fc-drill=T9 快应用演练桩(模块树见 DRILL_APP_MODULES),用于本机实拍 P76/P77/V19
|
||||
response: ({ query }: any) => {
|
||||
const target = String(query?.appCode ?? "").trim();
|
||||
if (target === "fc-drill") return ok(DRILL_APP_MODULES);
|
||||
if (target && target.toLowerCase() !== "form-center") return ok([]);
|
||||
return ok(OWN_MODULES);
|
||||
}
|
||||
},
|
||||
{
|
||||
// 选人弹窗的组织树桩(T10):形状=后端 SchoolModels.Node(isParent=true 是组、false 是人员叶子)。
|
||||
// id 即 UserBase 用户主键,提交给 /school-admins 与 /forms/{uuid}/admins 的就是它。
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
* 游标(keyset):`cursor`=上一页末行 answerId(uuid),服务端按它定位后取下一页——
|
||||
* 这里用 uuid 在数组里的下标模拟,形状与后端一致(前端不感知差异)。
|
||||
*/
|
||||
import type { AnswerDetail, AnswerRow, BatchItemResult, FilterField, FillableForm, ListView, MyAnswerViewItem, StatsView } from "@/types/data";
|
||||
import type { AnswerDetail, AnswerRow, BatchItemResult, FilterField, FillableForm, ListView, ManagedForm, MyAnswerViewItem, StatsView } from "@/types/data";
|
||||
|
||||
const ok = (data: any) => ({ code: 0, msg: "", data });
|
||||
const bizErr = (code: number, msg: string) => ({ code, msg, data: null });
|
||||
@@ -349,6 +349,33 @@ export default [
|
||||
return ok(sorted);
|
||||
} },
|
||||
|
||||
// ==================== 菜单自解析另两支路(契约 v2 §2.4/C3,T14) ====================
|
||||
/**
|
||||
* 我管理的表单(C3):行形状=fillable 行 + `role` 来源标记(creator/form_admin),
|
||||
* 另带 `formUuid`/`status`(管理与定义动作都按 uuid 寻址,问题单 P120)。
|
||||
* mock 里把上面三行里的两张给成"我管的":一张创建人、一张表单管理员、一张两者都是。
|
||||
*/
|
||||
{ url: "/api/form/my/managed", method: "get", response: ({ query }: any) => {
|
||||
const rows: ManagedForm[] = [
|
||||
{ formUuid: FORM_UUID, status: 1, code: "honor_apply", name: "三好学生申报", appCode: APP,
|
||||
audience: ["STUDENT"], deadline: "2026-12-31 23:59:59", fillLimit: 1, groupTag: null,
|
||||
role: ["creator", "form_admin"] },
|
||||
{ formUuid: FORM_UUID.replace(/.$/, "2"), status: 1, code: "teacher_survey", name: "教师问卷",
|
||||
appCode: APP, audience: ["TEACHER"], deadline: "2026-11-30 23:59:59", fillLimit: 0,
|
||||
groupTag: null, role: ["form_admin"] },
|
||||
{ formUuid: FORM_UUID.replace(/.$/, "3"), status: 0, code: "home_visit", name: "家访登记(草稿)",
|
||||
appCode: "fc-drill", audience: ["STUDENT", "TEACHER"], deadline: null, fillLimit: 0,
|
||||
groupTag: "五年级", role: ["creator"] }
|
||||
];
|
||||
const app = String(query?.appCode || "").trim();
|
||||
return ok(app ? rows.filter((r) => r.appCode === app) : rows);
|
||||
} },
|
||||
/**
|
||||
* 能力位:mock 固定给"是校管理员",好让"系统设置/表单设计"两支在本地实拍得出来。
|
||||
* 真实取值来自后端 `FormAccessPolicy.isSchoolAdmin`(超管恒 true)。
|
||||
*/
|
||||
{ url: "/api/form/my/capabilities", method: "get", response: () => ok({ schoolAdmin: true }) },
|
||||
|
||||
// ==================== 我的填写两视图(V18) ====================
|
||||
{ url: "/api/form/my/submit-list", method: "get", response: () => {
|
||||
const items: MyAnswerViewItem[] = seed
|
||||
|
||||
Reference in New Issue
Block a user