Files

116 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# T5 执行说明 · 前端·壳与管理端框架
> 任务书 T5(v1.1)|执行手册 §1 模板组合「前端 = fork notice/front」+ §2.1(一个 SPA 承载全部形态)+ §2.2(模块行表与 path 空行口径)|方案 v1.4 §3.3(页面即组件、`self != top` 剥壳)+ §7.3(《云海子应用前端接入规范》v1.11)+ §9 V22
> 主体改动 = **front 仓**(`form-center/form-center-front`);service 仓两处 T4 验收遗留小改;YschTheme 仓 consumers.json 登记。
> 本轮不自创设计:值形状、控件、接口口径全部沿用契约,前端只做壳与骨架。
---
## 1. 做了什么
### 1.1 notice/front 基线整体迁入并剥领域(不是改造 T1 裸壳)
T1 留的 `front` 只有 `App.vue + main.ts + 仅 vue 依赖`。本轮把 notice/front 的**壳基础设施**落进来并剥净领域,形成 47 个源文件(另 25 个 svg 资产)的工程:
| 迁入件 | 来源 | 说明 |
| --- | --- | --- |
| `index.html` | Notice 现行版 | env-config 同步引导 + **A 类 loader(§2.1 原文照抄,未改一字)**;title 走 `createHtmlPlugin` 注入「表单中心」 |
| `vite.config.ts` | Notice 现行版 | vue / svg-icons / vite-plugin-mock / auto-import / html / UnoCSS 六插件 + `base:"."`(P11 已就位)+ **port 8089(P6)** + `/api`→8086、`/static`→中央通道(`YH_THEME_PROXY_TARGET` 可覆盖)代理 + 产物恒 `dist` 根(zip 根含 index.html,§7 坑) |
| `src/utils/alova/{alova-core,alova-iview}.ts` | Notice | 信封解包(`code==0`→data、`302`→挂起 Promise 跳 CAS)、`yschapirequest/yschreferer` 头、800ms 延迟 Spin |
| `src/components/layout.vue` | **Notice refined 版(13afb22)** | 菜单 icon 兜底 `icon-list` + 面包屑首页图标行内基线流 `-0.125em`(规范 §4 两行)+ Logo `yh-logo-url` 平台键优先/本仓兜底 + `yh-appearance-updated` 监听 + 无配色切换器(§3 红线) |
| `src/utils/hook/useElementHeight.ts`、`src/components/TablePageAlovaNew.vue` | Notice/flow-center 同构 | 通道撑满高度模型(§4.3 三硬约束)+ `.ysch-spin` loading 插槽形态(§4 表格加载行) |
| `src/utils/tableActions.ts` | **UserBase 同构源** | `actionColumn/normalizeActions/splitActions/actionCell`,显式 `mode`(§5.1)+ `disabledWhen` 置灰保位(§5.2 红线) |
| `src/utils/{index,menuRoute,mitt}.ts`、`src/utils/directive/*`、`src/assets/styles/*`、`src/assets/icons/*` | Notice | `Utils` 类(旧 `operatingHandle` 保留为薄封装,§5.4)、main.scss 含 `.op-cell`/`.table-spin-load`/固定层事件/省略号 Tooltip 四段 |
| `src/components/{UserAvatar.vue,utils/avatar.ts}` | **YschTheme `npm run sync` 下发** | 与通道源 MD5 逐字节一致(`9ac72f8b`/`1fa7163c`),sync --check 报「全部对齐」 |
**剥除清单**(任务书「剥 notice 全部领域 views 与 mobiapi」):`views/notice/*` 8 页、`api/module/{notice,approval,cc}.ts`、`assets/mock/noticeapi.ts`、`assets/config/menu.ts`(本地菜单兜底=假象,与后端「不保留本地 MENUS 兜底」同口径,一并剥)、`utils/alova/alova-vant.ts`(**mobiapi 腿**,指向 `/mobiapi` 的移动端实例)、`NoticeUpload/ThemeSwitcher/GradeClassSel/BschSubjectSelect/UserCheckModal/MyTree*` 等领域或切换器组件、`utils/{axios,diagramImg,bschSsoRedirect,flowLayout…}` 与 index.html 的 **ztree+jquery 库引用**(树件随 T8 需要时按平台件再引)。重量依赖 `@wangeditor`、`pdfjs-dist`、`@open-file-viewer`、`vuedraggable`、`axios`、`lodash`、`@alova/wormhole` 未装(各自属 T6/T7 的编辑/预览/拖拽需求,届时显式加)。
### 1.2 左导航(GetUserMod)与 path 空跳过
- **`src/utils/menuNav.ts`(新增,本轮唯一的导航判定实现)**:纯函数 + 解析器注入(`buildNavTree(modList, resolve)`),三条规则写在文件头——① **跳过判据取平台原始 `path` 是否为空**(不取解析结果:靠名字/modId 猜出路由再显示就是把权限锚点画成死菜单);② 有可渲染子节点的分组行保留为**分组头**(不可点),子节点全被跳则**整组不渲染**;③ 原始 path 非空但解析不出已注册路由的行**照常显示**、点击时 `Message.warning` 点名配置错误(不静默少一条菜单)。
- 高亮/面包屑按 `flattenNav` 对照(分组头不入表),`firstNavPage` 决定登录后的默认落地,`isSingleNav` 承 Notice 的「单模块独占」语义。
- `App.vue`:拿 `GetUserInfo + GetUserMod` → `buildNavTree` → 交 `layout.vue` 渲染;**失败只降成"导航为空",不造兜底树**(后端注释同口径)。DEV 下 `console.table` 同时打原始树与过滤后导航,冒烟可直接对比。
### 1.3 `api/module/base.ts` 形态与 appCode 参
`BASE="/api/base"` 的 `getUserInfo()` / `getUserMod(appCode?)`(**参数名按 P2 定稿 appCode**)。文件头写明:path 空行只由后端 `DataScopeService` 扫描消费、接口不过滤,前端只在导航层跳过。**P3 口径**:H4 未上线时该请求返回空树,页面即空态,不回落本应用树(mock 侧同一条判定已实现并在冒烟 D 段断言)。
一处必要偏离:Notice 的 `new Method<T>("GET", …)` 在 alova 3.5.5 类型下**不合法**(`Method` 的第一泛型位是 `AlovaGenerics` 而非响应类型,strict vue-tsc 报 TS2344),本端改 `alovaInstance.Get<T>()` ——同一个 Method 对象、同一套拦截器与信封解包,行为等价(P49)。
### 1.4 路由骨架:一个 SPA 四形态(手册 §2.1)
`createWebHashHistory`(Notice/flow-center 同族),路由表即 `/admin/{forms,data,stats,dicts}`、`/f/:code`、`/app/:appCode[/:view]`、`/my`,`/` = 入口页、`/:pathMatch(.*)*` → `/`(外部深链不白屏)。产物部署在 `/form-center/front/`,故手册写的 `/form-center/f/{code}` = ctx + `#/f/{code}`。
- **`meta.modId` 全部不写**(规范 §4「modId 全量对齐后端 GetUserMod,禁臆造」):本应用模块行除 `fc:scope:all` 外 customId 均未配(手册 §2.2 表为「—」),后端 `resolveModId` 因此回退 `app_module` 主键 id,该值跨环境不稳定,写进路由必然失配;高亮与面包屑按 path 匹配。
- 占位页 7 个(`views/admin/*` 4 + `Fill` + `AppContext` + `My` + `Entry`),统一走 `components/PageStub.vue` 的 `page-wrapper.yh-fill > main-container` 骨架 + 常驻 `Alert type=info show-icon`(§4.2 无 closable/banner)。**占位页刻意不挂 `.table-region`**——没有 Table 却铺一个"应当被量高"的容器,会留下假范式(§4.3 三硬约束的成立前提是有 Table 喂回高度)。零领域逻辑、零接口调用(`Fill`/`AppContext` 只把 `code`/`appCode` 与形态显示出来)。
### 1.5 `self != top` 剥壳(方案 §3.3)
`App.vue` 顶层判 `window.self !== window.top` → 嵌入态裸渲染 `<router-view>`:不画 layout、**不发两个鉴权请求**(嵌入上下文取不到本应用会话,失败会弹错误并画出带壳页),并内联旁路 `_base.scss` 的 `body{min-width:1200px}`(740px Drawer 里不旁路就整体右偏+右裁,flow-center 同款手法)。显式 `?mode=` 闭集留给 T7,本轮只做 `self!=top` 这一条兜底。
### 1.6 主题通道登记(YschTheme 仓)
`consumers.json` 新增 `form-center` 条目(`path:"../form-center/front"`、`target:"pc"`、`files` 只登记 avatar 两件——按 §3 红线不建配色切换器,故不登记 `theme.ts`/`ThemeSwitcher.vue`;ctx=/form-center/ 写进 note);同步更新 `tests/scripts.spec.ts` 的「消费端清单显式可控」断言(清单加一条 + form-center files 显式断言)。`npm run sync` → form-center 两件报「一致」;`npx vitest run` → **8 文件 84 例全绿**,其中 `iview-version.spec` 即「消费端 lock 实装版本 === 通道 devDependencies 精确版本」的版本锁测试,form-center 通过=**1.3.24 锁红不再失守**。
### 1.7 service 仓两处顺带(T4 验收遗留,不喧宾夺主)
- **勘误③ 真落**:`docs/T3-report.md` §2 的 V32 行「**两个不同教师**」后补注「(实为两个 roleId=7 操作者——教师(1) 按 P21 无代填权)」。已核源码:`AnswerSubmitIT.onceLimitIsPerSubjectWhenSubjectPresent` 用的是 `user(10001, SCH, 7)` 与 `user(10002, SCH, 7)`,注释属实。T4 报告 §1.6 声称已改、实测未改,本轮落到文本。
- **P47 收紧**:`FormModuleKeys` 的 customId 扫描改为**只收 path 空节点**(新方法 `anchorCustomIds`,`collect` 单处分支;旧 `customIds` 无其他调用方,一并收敛不留死代码),`DataScopeService.hasScopeAllAnchor` 切过去。补测 `DataScopeServiceTest.customIdOnNavRowDoesNotUpgradeScope`:带 path 的导航行误配 `fc:scope:all` → 仍 `SELF_SUBMITTED`;同一用户同一值改挂 path 空行 → 立刻 `ALL`(证明收窄生效、不是扫描整体失效)。配套给 `InMemoryModuleTreeSource` 加 `grantPathWithCustomId` 桩方法。**契约未改**(§8 的「锚点行形态」注记 T4 已补)。
---
## 2. 自验证据(逐条)
取证脚本:**`form-center/local-debug/t5-shell-smoke.sh`**(13 断言,ASCII 锚点,JSON 交 node 解析不在 shell grep 中文)。终态运行结果 **`13 pass / 0 fail / 1 skip`**。
| 项 | 判据 | 证据 |
| --- | --- | --- |
| `npm run build` | 必须过 | ✅ `✓ built in 5.54s`(脚本 A 段 `PASS build`) |
| `npm run type-check` | 必须过(`vue-tsc --noEmit`,strict + `noUnusedLocals/Parameters`) | ✅ 0 错误(脚本 A 段 `PASS type-check`)。Notice 基线在本机 `vue-tsc` 是坏的(`store/index.ts` 无 `IModule`、`Basics.d.ts` 写 `page: !number`),本轮按 T1~T4「本地门禁 + CI 只证构建链」的口径把 type-check 做成硬门禁,故对迁入件做了最小整形(P49/P50) |
| **V22** build 通过 | 方案 §9 原文 | ✅ 上一行 |
| **V22** consumers.json 已登记 | 规范 §2.3 | ✅ 脚本 B 段 `PASS consumers.json 已登记 form-center(ctx=/form-center/)`;`npm run sync` 报 `一致 form-center src/utils/avatar.ts / UserAvatar.vue`、`[sync] 全部对齐`;`npx vitest run` 84/84 绿 |
| **V22** view-ui-plus 锁 1.3.24 | 规范 §1/§2.3 | ✅ `package.json` 声明 `"1.3.24"`(无 `^`)+ `package-lock.json` 实装 `1.3.24`(脚本 B 段)+ 通道 `iview-version.spec` 通过 + `npm ls view-ui-plus` → `1.3.24` |
| **V22** 本地无 layout.css/icons.css | 规范 §2.2 回收步 | ✅ 脚本 B 段 `PASS no-local-layout-css-icons-css`(`find src -name layout.css -o -name icons.css` 为空);`main.scss` 无相关 import;`dist/index.html` 保留 `yh-theme-init.js` loader 与 `api/env-config` 引导、`%VITE_THEME_HOST%` 未被烧值 |
| **V22** 控件目录含 `app.vue` 槽位 | — | ⏭ 任务书已注明:**app.vue 槽位一条留 T6 验**(控件目录随设计器产生,本轮无控件) |
| 登录出壳 + 左导航(菜单树=手册 §2.2 表) | 任务书自验 | **降级取证**(P48,原因见 §3):`npm run dev:mock` + in-app browser a11y 快照——顶栏 `Logo|分隔线|表单中心|Mock 管理员|退出`、左导航**恰 5 项**(表单设计/数据管理/统计/字典管理/我的填写)、**「数据全量」(path 空) 未出现**、面包屑 `首页 > 表单中心 > 表单设计`、URL 自动落 `#/admin/forms`(首个可跳转项)。截图不可用(in-app browser 无可见 viewport),以结构化快照为据 |
| path 空跳过 / 分组收敛 / 无授权仅「我的填写」 | 手册 §2.2 验证口径 | ✅ 脚本 C 段:`esbuild` 单独转译 `utils/menuNav.ts` 后在 node 里断言——6 行树(含 1 行 path 空)→ 导航 5 项且锚点不在;`[我的填写, 锚点]` → 1 项且 `isSingleNav=true`;子项全锚点的分组整组不渲染、分组头不入高亮表 |
| 外指 appCode 空树不回落 | P2/P3 | ✅ 脚本 D 段:mock 侧与后端同一条判定(`appCode=some-other-app` → `data=[]`);前端 `getUserMod(appCode)` 是唯一入口、无任何「失败即取本应用树」的代码路径 |
| `self != top` 剥壳 | 方案 §3.3 | ✅ 同源 iframe 探针(dev:mock 页内动态挂 740px iframe 指 `#/f/DEMO_PROBE`):顶层 `.header-container/.left-menu/.path-nav` 各 1,iframe 内**各 0**、`self!==top=true`、`body.style.minWidth="0px"`、正文显示「形态 = 页面块(self != top,已剥壳)」 |
| P6 本地端口 | 手册 §5 | ✅ front dev=8089(`vite.config.ts`)、service=8086(`/api` 代理 target) |
| P11 壳的平台约定补齐 | 手册 §5 | ✅ loader/alova/router/auto-import/svg-icons/env-config 引导/`base:"./"` 全落,T1 留在 `vite.config.ts` 的 TODO 注释已删 |
| **service 两处小改** | 本地 `mvn test` 门禁 | ✅ `mvn -B -o clean package` → **BUILD SUCCESS,Tests run: 178, Failures: 0, Errors: 0**(T4 基线 177 + 本轮 P47 新增 1 条) |
---
## 3. 问题单(本轮登记,编号接续 P47;供执行手册 §5 裁决)
| # | 日期 | 批次 | 问题 | 本轮取值(最保守解读) | 是否回文档修订 |
| --- | --- | --- | --- | --- | --- |
| P48 | 10-14 | T5 | 任务书要求的「登录态冒烟(CAS/UserBase 本地起 → 真会话出壳 + 菜单树=§2.2 表)」不可执行 | **按 P1 先例降级取证**,两条独立事实:①本机 CAS(8443)/UserBase(8060) 未运行(`curl` 均无响应),Redis 亦未起;②**更本质的缺口**——本地 `bsch_userbase` 里 `application_info` 中 `app_code='form-center'` 的行数=**0**、`app_module` 无任何 form-center 模块行(查得 `select count(*) … where a.app_code='form-center'` → 0,`app_module` 按 app_id 分组无对应 id),即 H2(UserBase 建中心应用行 + §2.2 模块行 + 组授权)未做。即便把三件服务起齐,`GetUserMod` 也只能返回空树,拿不到 §2.2 那棵树。补证=脚本 C 段纯函数断言 + D 段 mock 树 + 浏览器结构化快照(§2 表)。**H2 完成后 T8/T9 需真会话复跑一次** | 否(缺口在外部数据,非设计) |
| P49 | 10-14 | T5 | Notice 的 `api/module` 形态 `new Method<T>("GET", …)` 与 alova 3.5.5 的类型不兼容(`Method` 第一泛型位是 `AlovaGenerics`),strict `vue-tsc` 恒报 TS2344;flow-center/open-gateway 同病(它们的 `npm run type-check` 从没跑绿过,规范 §8 也写「vue-tsc 损坏勿依赖」) | 本端改 `alovaInstance.Get<T>(url, {params})`:同一个 Method 对象、同一套 `beforeRequest/responded` 与信封解包,行为等价、类型正确。**T6~T8 的 api/module 文件沿此写法**(其余形态:`BASE` 常量 + class + `export const xxxApi` 不变)。不改 Notice 已验收工程 | 否(工程写法,非契约) |
| P50 | 10-14 | T5 | 四端同构件 `utils/tableActions.ts` 落第五端(本端 type-check 是门禁) | 照 open-gateway 先例:顶部补 `// @ts-nocheck` + 4 行说明,**正文与同构源逐字节一致(MD5 `973393684783c740399b8eb9e97369bc`,与 UserBase/flow-center 全等)**,strict 下两处告警均无害(`h` 被形参遮蔽、`it.on()` 已在 normalize 闭包绑定)。规范 §8「与四端 MD5 对齐」的判据按"正文对齐 + 端顶补丁"执行,同 open-gateway 一轮的既有先例 | 否 |
| P51 | 10-14 | T5 | 新端的操作列形态(§5.1 要求「工程级恒定」,Notice=icon / UserBase=text)本轮没有列表页可定 | 暂按同族 center 应用取 **text 恒定**(flow-center/svc-hall/open-gateway 皆 text),`mode` 显式声明、缺省 text;**T8 出第一个真实列表页时若用户要 icon 形态,改的是各页 `mode` 参数而非工具件** | 否 |
| P52 | 10-14 | T5 | 剥领域后的「回补时机」未写:index.html 的 ztree+jquery、`@wangeditor`/`pdfjs-dist`/`@open-file-viewer`/`vuedraggable`/`axios`/`lodash` 均未随 fork 带入 | 一律**按需再引**(设计器 T6、附件预览与拖拽 T6/T7、组树选择器 T8),不提前铺空依赖:本轮 `npm ci` 518 包、构建 5.5s,若照搬 notice 全量依赖会显著拖慢 CI 且产物里多份未用图形资产(§4 表格加载红线:应用侧零图形资产)。回补时在对应批次报告里点名 | 否 |
| P53 | 10-14 | T5 | 入口页 `/` 的落地语义方案未写:Notice 把 `/` 做成真实业务页(已收到),flow-center 用 `redirect` | `/` = 真实入口页(无模块时的可诊断空态),登录后由壳 `replace` 到首个可跳转项;兜底 `/:pathMatch(.*)*` → `/` 而非某个具体管理页(**无授权用户不该被送到他进不去的页面**)。若用户希望 `/` 恒落「我的填写」,改 `routes.ts` 一行 | 否 |
另记两条**非问题**事实,供接手的人少踩:①`local-debug/` 不在任何仓库工作树内(`form-center` 根无 .git,仓库根是 `service/` 与 `front/`),本轮新增的 `t5-shell-smoke.sh` **不入库**(沿 T1~T4 同一事实);②YschTheme 仓**无 git 远端**(`git remote -v` 空),consumers.json/tests 的登记改动只落本机工作区,未 commit、未 push,通道发布仍走该仓 `release.mjs`/`release.py` 人工动作。
---
## 4. 推送与 CI
| 仓 | 提交 | 推送 | CI run | 结论 |
| --- | --- | --- | --- | --- |
| form-center-front | `fdd9aaa`(81 条路径:73 新增 + 7 修改 + 1 删除(T1 的 `src/env.d.ts` 并入 `src/vite-env.d.ts`),逐条 `git add`) | `cebc32f..fdd9aaa main -> main` | [run 1496](http://192.168.10.160:3000/form-center/form-center-front/actions/runs/1496)(head_sha `fdd9aaa`,run_number=2) | **status=completed / conclusion=success**(Gitea Actions API 实测 HTTP 200) |
| form-center-service | `2c68d6b`(6 条路径:`docs/T3-report.md` 勘误③、`docs/T5-report.md`、`FormModuleKeys.java`、`DataScopeService.java`、`DataScopeServiceTest.java`、`InMemoryModuleTreeSource.java`) | `c0a0df4..2c68d6b main -> main` | [run 1497](http://192.168.10.160:3000/form-center/form-center-service/actions/runs/1497)(head_sha `2c68d6b`,run_number=10) | **completed / success** |
| YschTheme | consumers.json + tests/scripts.spec.ts 两文件改动 | **无远端可推**(`git remote -v` 为空) | — | 登记落本机工作区,未 commit;通道发布仍是该仓 `release.mjs`/`release.py` 人工动作(规范 §7 坑表「通道应用实名 yschtheme/web」) |
- 暂存方式:两仓都**按显式路径逐条 `git add`**,提交前 `git status` 核对无残留未跟踪项、无 `add -A`、无敏感文件;front 的 `node_modules/`、`dist/`、`*.log` 由 `.gitignore` 排除(`.gitignore` 未改),`src/auto-imports.d.ts` 按 Notice 先例**入库**(`npm ci` 后首次 `vue-tsc` 需要它)。
- 取证方式:沿用 T4——`git credential fill` 会拉起 GCM 图形框(本会话非交互直接超时),改从 `~/.git-credentials` 取 `192.168.10.160` 这条已存凭据调 `GET /api/v1/repos/.../actions/runs/{id}`,凭据不回显、不落盘、不入库。
- **前端 CI 只证构建链**(node:20 容器 `npm ci` + `npm run build`,同后端「CI 不跑单测」先例):`type-check` 与 `local-debug/t5-shell-smoke.sh` 是**本地门禁**,本轮两者均绿(§2)。
- `local-debug/t5-shell-smoke.sh` **不入库**(沿 T1~T4 同一事实:`form-center` 根无 `.git`,两个仓库根分别是 `service/` 与 `front/`,该目录在两侧工作树之外;手册 §4.2 的「脚本入 local-debug」按目录本身落,入库需另建脚本仓或调整仓库根,不在本轮擅自动)。
- 本节回填本身是一次 docs-only 提交(只改本文,不动任何构建输入),其后同流水线再跑一次;该 run 的结果只在本轮收尾说明里报,**不再追加自指回填提交**(T4 已定此纪律)。
—— 全文完 ——