feat(p95-fill): 可填写换源后端接口,快应用导航壳按身份写死,/f/ 模块行消费退役
build / build (push) Successful in 1m47s

任务书 P95 W4/W5/W6。填报入口收回中心后,前端不再从应用模块树里挑 /f/{code} 行算清单。

- My.vue「可填写」:数据源 store.modules → GET /api/form/my/fillable,只画 canFill=true;
  空态文案改「表单发布后按面向人群自动出现在这里」,不再引导去 UserBase 配模块行
- AppContext.vue 整页改版:清单吃 fillable?appCode=,三档按 myTier 渲染
  (填报恒显 + 单表单直落保留 / 数据管理 MANAGE∪ADMIN / 统计 ADMIN);
  GetUserMod?appCode 仍拉一次,唯一用途=交给内嵌 Data.vue 判导出锚点(P76 机制原样),
  空树整页告警与「树里没有 /f/ 行」分支退役——取树失败只等于不画导出按钮(fail-closed)
- fillLink 新增 fillRouteOf(与 buildFillLink 同一条 appCode 规则,点进去与分享出去同址);
  modulePerms 删 fillCodesOfModules(唯一消费者已退役),canExport/anchorRows/hasPage 保留勿伤;
  menuRoute 的 /f/ 解析与门户导航未动(分享链接、P87 探针照旧)
- mock:快应用树改四行终态(删填报行),新增 /api/form/my/fillable 桩覆盖 FILL/MANAGE/ADMIN
- vue-tsc --noEmit 与 vite build 双绿;p87 探针由当前源码重打包后 15/15 复跑绿
This commit is contained in:
chenxing
2026-10-16 12:44:42 +08:00
parent 777273e3a5
commit 48e46e367b
11 changed files with 300 additions and 193 deletions
+8 -15
View File
@@ -82,25 +82,18 @@ const OWN_MODULES: IModule[] = [
];
/**
* 第一个快应用的模块树(方案 §6.2 表的典型配法,T9 演练桩)。
* 第一个快应用的模块树(T9 演练桩,P95 W6 改版为终态形态)。
*
* 只在 `appCode=fc-drill` 时下发,用来在本机把 **P76(导出锚点按应用树判)/ P77(应用内详情形态)/
* V19(快应用独立模块树渲染左导航)** 跑到可实拍的程度——真会话取证要等 H2/H4(执行手册 §2 人工项)。
* 只在 `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: 9201,
modId: 9201,
name: "填报",
parentId: 0,
page: "/f/drill-honour",
level: 1,
icon: "edit",
orderNumber: 1,
usable: true,
children: []
},
{
id: 9202,
modId: 9202,
+34 -1
View File
@@ -18,7 +18,7 @@
* 游标(keyset):`cursor`=上一页末行 answerId(uuid),服务端按它定位后取下一页——
* 这里用 uuid 在数组里的下标模拟,形状与后端一致(前端不感知差异)。
*/
import type { AnswerDetail, AnswerRow, BatchItemResult, FilterField, ListView, MyAnswerViewItem, StatsView } from "@/types/data";
import type { AnswerDetail, AnswerRow, BatchItemResult, FilterField, FillableForm, ListView, MyAnswerViewItem, StatsView } from "@/types/data";
const ok = (data: any) => ({ code: 0, msg: "", data });
const bizErr = (code: number, msg: string) => ({ code, msg, data: null });
@@ -317,6 +317,39 @@ export default [
return ok(results);
} },
// ==================== 可填写入口清单(P95 W3/W4,/my/fillable) ====================
/**
* 三行种子刻意覆盖三层模型的可观测差异(冒烟脚本 p95-fill-scope-smoke.sh 就断言这三件事):
* ① FILL:学生面向、canFill=true → 「可填写」画卡;
* ② MANAGE:学生面向、canFill=false(教师那种"只能代填")→ 「可填写」<b>不</b>画卡,
* 但快应用上下文的数据管理 tab 要靠它出现;
* ③ ADMIN:教师面向、canFill=true + 最高层 → 统计 tab 只由它决定。
* appCode 传值时按该应用收窄(缺省=全部应用),与后端 FillableFormService 同一口径。
*/
{ url: "/api/form/my/fillable", method: "get", response: ({ query }: any) => {
const rows: FillableForm[] = [
{ code: "honor_apply", name: "三好学生申报", appCode: APP, audience: 1,
deadline: "2026-12-31 23:59:59", fillLimit: 1, groupTag: null, canFill: true, myTier: "FILL" },
{ code: "home_visit", name: "家访登记(教师代填)", appCode: APP, audience: 1,
deadline: null, fillLimit: 0, groupTag: "五年级", canFill: false, myTier: "MANAGE" },
{ code: "teacher_survey", name: "教师问卷", appCode: APP, audience: 2,
deadline: "2026-11-30 23:59:59", fillLimit: 0, groupTag: null, canFill: true, myTier: "ADMIN" },
{ code: "drill-honour", name: "荣誉申报(演练应用)", appCode: "fc-drill", audience: 1,
deadline: "2026-10-31 23:59:59", fillLimit: 0, groupTag: null, canFill: true, myTier: "FILL" },
{ code: "drill_roster", name: "花名册(演练应用,教师代填)", appCode: "fc-drill", audience: 1,
deadline: null, fillLimit: 0, groupTag: null, canFill: false, myTier: "MANAGE" }
];
// 排序沿后端口径:deadline 升序、不限截止在后、同档按 code
const app = String(query?.appCode || "").trim();
const hit = app ? rows.filter((r) => r.appCode === app) : rows;
const sorted = [...hit].sort((a, b) => {
const da = a.deadline || "9999";
const db = b.deadline || "9999";
return da === db ? a.code.localeCompare(b.code) : da.localeCompare(db);
});
return ok(sorted);
} },
// ==================== 我的填写两视图(V18) ====================
{ url: "/api/form/my/submit-list", method: "get", response: () => {
const items: MyAnswerViewItem[] = seed