116 lines
20 KiB
Markdown
116 lines
20 KiB
Markdown
# 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 已定此纪律)。
|
||
|
||
—— 全文完 ——
|
||
|