mirror of
https://github.com/motajs/template.git
synced 2026-11-10 04:24:30 +08:00
docs(09-42): add legacy UI parity plan and execution summary (code uncommitted)
This commit is contained in:
1 parent
58c8d681da
commit
09c39ecd8d
3 files changed
+717
No files matched your search
@@ -0,0 +1,397 @@
|
||||
---
|
||||
phase: 09-minimum-browser-runnable
|
||||
plan: 42
|
||||
type: execute
|
||||
# Phase 9 新增独立增量;09-42 / wave 31 为当前空闲编号,09-42 为全新计划,不存在旧摘要。
|
||||
# 执行前确认不存在旧 09-42-SUMMARY.md;若存在则停下汇报,不得覆盖。
|
||||
wave: 31
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/client/ui/enemyOne.tsx
|
||||
- src/client/ui/book.tsx
|
||||
- src/client/ui/bookDetail.tsx
|
||||
- src/client/ui/toolbox.tsx
|
||||
- src/client/ui/equipbox.tsx
|
||||
- src/client/ui/__test__/legacyUiParityGate.test.ts
|
||||
- .planning/phases/09-minimum-browser-runnable/09-42-PLAN.md
|
||||
- .planning/phases/09-minimum-browser-runnable/09-42-SUMMARY.md
|
||||
- .planning/phases/09-minimum-browser-runnable/09-LEGACY-UI-COMPARISON.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
estimate:
|
||||
# 未校准估算(confidence: low)。用户要求保持一个计划、按 UI 拆任务,
|
||||
# 共 Task 0 + EnemyOne + book + bookDetail + toolbox + equipbox + gate 7 个任务。
|
||||
# 成本主要来自:读 5 个现有 UI 文件与 legacy 源、写 5 个 UI 文件、写 1 份源码闸门、跑 eslint 与 vitest。
|
||||
# 验收只看代码规范与功能大致覆盖,类型正确性不在范围内。
|
||||
tokens: 160000
|
||||
raw_tokens: 160000
|
||||
tasks: 7
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "本计划第一个 block 点是 Task 0 的 blocking-human 汇报关卡:先逐条汇报纳入/排除的精确 ID、EnemyOne 抽象与待批准命名、布局、行为变化、矛盾清单、验证方式,并明确声明类型正确性不在验收范围(类型错误允许存在,代码由用户自行审查/修改),只强制代码规范与功能大致覆盖,然后停下等待用户恰好回复「可以执行」;在此之前不得执行 Task 1..Task 6 的任何一步,也不得派发执行子代理。"
|
||||
- "新增 src/client/ui/enemyOne.tsx:导出可复用的 EnemyOne 组件,接收 enemy、loc、伤害显示数据、特殊摘要、selected、onClick/onHover;左侧为普通 icon + 名称 + 特殊摘要,右侧为 adjustGrid 生成的 3×3 属性网格(固定参数 adjustGrid(180, 72, 56, 20, 4, 4),cols=3、rows=3、locs.length=9);不使用图标动画组件,不做移动端分支;enemy 为必填,调用方必须先判空,组件本身不接收 undefined;选中描边使用 `<g-rectr fill={false}>`(不绘制背景填充),伤害颜色以英雄当前 HP(`hero.getAttribute().getFinalAttribute('hp')`)为阈值。"
|
||||
- "book.tsx 使用 EnemyOne 渲染每一行;具备本层无怪物空状态、选中/悬停高亮、键盘上下/翻五/确认/退出、选中项自动滚动、伤害颜色、选中索引;点击行仍通过现有 UI 栈打开 BookDetailUI,locator 按现状传 undefined 即可(不作为验收条件);不实现缩放、移动端、拖动与点击区分。"
|
||||
- "bookDetail.tsx 改为单页:顶部 EnemyOne(图标/名称/特殊摘要/3×3 伤害、临界、减伤、1 防),中部特殊属性列表,底部临界表(加攻/减伤两列、8 行);顶部仅在 current 非空时渲染 EnemyOne,否则渲染「无怪物数据」;修复 criticlas 与 critical 不一致;用 watch(current, value => getCriticals(value), { immediate: true }) 在 current/模式变化时重算临界,getCriticals 先判空、current 为空时把 criticlas 置 [] 并返回;3×3 与临界表使用 adjustGrid(临界表固定参数 adjustGrid(548, 104, 260, 12, 20, 1),cols=2、rows=8、locs.length=16);保留模式切换、0 回退、纯文本 TextContent;不实现面板切换、图表/滑条、目标面板、移动端、动画。"
|
||||
- "toolbox.tsx 实现 legacy 双列表 + 右侧详情:永久/消耗两列、详情图标/名称/类型/使用提示、纯文本描述、空状态;使用流程为 keep + close + nextTick + useItem + 安全卸载,使用失败显示提示;提供跳到装备栏的交叉导航(通过 `props.controller.open(EquipBoxUI, ...)` 打开,实现方式不限),ToolBoxProps 新增 attributes 可选 prop 且默认 [];实现 @ui/toolbox 再次按下关闭(TB-17);键盘左右切换列、上下移动、确认使用、退出;初始状态明确为 legacy 口径:当前列为可消耗列(legacy `mode='tools'`,即消耗道具)、index=0 指向该列第一项;选中高亮由 mode+index 派生,不再保留独立的 selected 变量;不实现移动端、打字动画、丢弃、拖拽。"
|
||||
- "equipbox.tsx 保持现有新布局与视觉,只实现三项:EQ-02 跳到道具栏(通过 `props.controller.open(ToolBoxUI, ...)` 打开)、EQ-18 将已装备拖到另一槽位(可装备时 equip,否则卸下)、EQ-27 键盘退出/再按装备界面键关闭;quickEquip 在无新接口时按 Task 0 确认结果处理;不复制 legacy 布局、筛选、排序、移动端、打字动画。"
|
||||
- "所有共享排除项均不出现:移动端、临界图表/滑条、目标面板、HTML 描述(保持 TextContent 纯文本)、打字动画、equipbox 筛选/排序、book 缩放;不新增接线/装配/注册/菜单入口;不提交任何代码。"
|
||||
- "验证只包含三项:直接二进制 eslint(风格/格式/unused/命名)、新的 09-42 源码结构闸门 + 现有绿色 hotkeySettingsGate、以及基于 Task 1 前 git status --porcelain -uall 基线的范围差集(只允许 files_modified 路径新增,09-35 与其他计划不得被触碰);不运行 vue-tsc,不做类型诊断对比;不运行已知红色的 uiContainerGate 与 keyAdaptationGate;不做浏览器 UAT。"
|
||||
artifacts:
|
||||
- path: src/client/ui/enemyOne.tsx
|
||||
provides: "可复用 EnemyOne 组件;adjustGrid(180, 72, 56, 20, 4, 4) 生成 3×3(locs.length=9);普通 icon;选中描边 `<g-rectr fill={false}>` 同时设置 stroke 与 strokeStyle,无背景填充;getEnemyDamageData / getDamageColor / getSpecialSummary 辅助函数(命名待批准);getDamageColor 以 hero 当前 HP(getFinalAttribute('hp'))为阈值"
|
||||
- path: src/client/ui/book.tsx
|
||||
provides: "EnemyOne 行、空状态、选中/悬停、键盘命令、ScrollExpose 自动滚动、伤害颜色、选中索引、现有详情入口"
|
||||
- path: src/client/ui/bookDetail.tsx
|
||||
provides: "顶部 EnemyOne(current 非空才渲染)、中部特殊属性、底部 8 行临界表、criticlas 修复、watch(current, value => getCriticals(value), { immediate: true }) 且 current 为空时置空、adjustGrid、纯文本描述"
|
||||
- path: src/client/ui/toolbox.tsx
|
||||
provides: "双列表 + 详情、使用流程、失败提示、装备栏交叉导航、attributes 默认 []、@ui/toolbox 关闭、初始为可消耗列(legacy mode='tools')/index=0 且高亮由 mode+index 派生、键盘导航、详情图标/类型/提示、纯文本描述"
|
||||
- path: src/client/ui/equipbox.tsx
|
||||
provides: "EQ-02 道具栏导航、EQ-18 已装备跨槽拖拽、EQ-27 键盘退出/关闭;其余新布局不变"
|
||||
- path: src/client/ui/__test__/legacyUiParityGate.test.ts
|
||||
provides: "09-42 源码结构闸门:断言五个 UI 的关键结构、命令、辅助函数、adjustGrid 数学结果与范围排除项;不检查类型正确性、不检查静态循环或 GameUI.get;不依赖 DOM"
|
||||
- path: .planning/phases/09-minimum-browser-runnable/09-42-PLAN.md
|
||||
provides: "本计划文件"
|
||||
- path: .planning/phases/09-minimum-browser-runnable/09-42-SUMMARY.md
|
||||
provides: "执行摘要:逐文件改动、真实门禁结果(eslint / 源码结构闸门 / 范围差集)、待批准命名、矛盾处理、零提交与不做浏览器 UAT 声明"
|
||||
- path: .planning/phases/09-minimum-browser-runnable/09-LEGACY-UI-COMPARISON.md
|
||||
provides: "执行后追加 09-42 回填段,记录用户覆盖的 ID 与最终状态;不改写原只读对照表"
|
||||
key_links:
|
||||
- "EnemyOne 的 enemy/loc/伤害数据/特殊摘要 props ↔ book 每一行与 bookDetail 顶部 —— 两处必须使用同一组件,否则 3×3 布局与图标/特殊摘要会出现两套实现"
|
||||
- "book 的 useKey 实现 ↔ src/client/misc/hotkey.ts 中已存在的 @ui/book-up / @ui/book-down / @ui/book-page-up / @ui/book-page-down / @common/confirm / @common/exit —— 命令 ID 必须逐字一致,否则键盘静默失效"
|
||||
- "book 的 ScrollExpose.scrollTo ↔ watch(selected) —— 选中索引变化后必须在 nextTick 调用,否则 Scroll 尚未挂载时滚动目标丢失"
|
||||
- "getEnemyDamageData 的 1 防计算 ↔ hero.getIsolatedAttribute().add('def', 1) + system.with(...) —— 必须复用 book 现有逻辑,不能另写一套近似值"
|
||||
- "toolbox 的 keep/close/nextTick/useItem ↔ controller.keep() 与 IHeroItems.useItem —— 新接口没有 holdOn/tryUseItem,必须在 Task 0 获批后按此映射执行"
|
||||
- "equipbox endPress 的 fromSlot >= 0 分支 ↔ target 槽位与 canEquipTo —— 只有目标有效且不同于来源槽时才 equip,否则 unequip,避免拖回原槽误装备"
|
||||
- "toolbox ↔ equipbox 的双向交叉导航:两边都在点击处理器中通过 props.controller.open 打开对方 UI(toolbox 打开 EquipBoxUI 并传 `props.attributes ?? []`,equipbox 打开 ToolBoxUI 并传 `props.attributes`);ToolBoxProps 新增 attributes?: readonly IHeroAttrItem[] 并默认 [],属性类型在 toolbox 本地声明、结构等价于 equipbox 的 IHeroAttrItem。导入方式(静态或动态)不限,不作为验收条件。"
|
||||
- "bookDetail 临界生成器 ↔ current 与 props.view —— Base 模式用 calculateEnemyCritical,Computed 且存在 view 时用 calculateViewCritical;必须用 watch(current, value => getCriticals(value), { immediate: true }) 在 current/模式变化后重算,getCriticals 先判空(current 为空时置空返回),不能只 watch props.enemy/props.view"
|
||||
assumptions:
|
||||
- "09-42-PLAN.md / 09-42-SUMMARY.md 是本次新增的独立增量;09-42 为全新计划,不存在旧 09-42-SUMMARY.md 被覆盖。执行前若发现旧摘要已存在,必须停下汇报,不得覆盖。"
|
||||
- "pnpm 在本树已损坏(client-modules 仍引用已删除的 @motajs/legacy-ui);本计划不修 workspace/package 文件,所有门禁直接使用 node_modules 下的二进制:node_modules/vitest/vitest.mjs、node_modules/eslint/bin/eslint.js。"
|
||||
- "当前工作树已脏;类型诊断不是本计划的验收条件。vue-tsc / TS 错误、`as` 断言的取舍、icon 的 AllNumbers 类型问题均由用户自行审查和修改;执行者只需保证代码规范与功能大致覆盖,不把任何类型正确性问题当作阻塞。"
|
||||
- "bookDetail 的 current 可能为空(enemy/view 都未传时);bookDetail 在渲染前判空,为空时渲染「无怪物数据」回退文本,不把空值传入 EnemyOne。"
|
||||
- "新 UI 控制器没有 controller.holdOn(),新数据层也没有 core.tryUseItem / core.canUseItem;可用替代为 controller.keep()、IHeroItems.useItem(): boolean 与 item.raw.effect.canUse 预检。该映射必须在 Task 0 获得用户批准,不能擅自改名或造接口。"
|
||||
- "新数据层没有 core.quickSaveEquip / quickLoadEquip 的替代接口;EQ-27 的 quickEquip 分支在 Task 0 默认按「无接口、不实现」处理,除非用户明确给出新接口或映射。"
|
||||
- "bookDetail 的「切换至计算后属性」只在有 IEnemyView 或 EnemyView 实例时才有意义;book 当前传入的是 enemyManager.createEnemy 返回的基础对象,因此从 book 打开的详情里模式切换仍是 no-op。若用户要求从 book 也能切换,需另行提供 view 或映射。"
|
||||
- "toolbox 到 equipbox 的交叉导航需要 attributes 列表;本计划给 ToolBoxProps 增加可选 attributes(默认 []),toolbox 在本地声明结构等价的 IHeroAttrItem(不导出、不导入 equipbox 的接口)。两侧打开目标 UI 一律在点击处理器内通过 props.controller.open 打开;导入方式不限。新增 prop 与本地类型命名需 Task 0 批准。"
|
||||
- "adjustGrid 从 @user/client-base 导出,签名 (width, height, itemWidth, itemHeight, intervalX, intervalY?) -> IGridLayoutData;ScrollExpose 从 @user/client-base 导出,提供 scrollTo(y, time?) 与 getScrollLength()。"
|
||||
- "useKey 从 @user/client-base 导出,必须在 Vue 组件 setup 内调用,默认 clearImpl=true;它会清理父作用域的按键实现,适合本计划的全屏 UI。"
|
||||
- "不安装任何新依赖;不新增或修改 package.json / pnpm-lock / workspace 配置;不触碰 09-01..09-41 的计划与摘要,并保护 09-42 目录内除本文件(09-42-PLAN.md)以外的所有文件。"
|
||||
- "本次执行不提交任何代码,不运行 git add / commit / stash / checkout / restore / reset;只允许只读的 git status / git diff / git show 用于范围核对。"
|
||||
---
|
||||
|
||||
<objective>
|
||||
把 book / bookDetail / toolbox 按 legacy 视觉与功能尽量补齐,把 equipbox 仅补齐 EQ-02 / EQ-18 / EQ-27,并抽出可复用的 EnemyOne 组件。
|
||||
|
||||
Purpose: 当前三个界面与 legacy 的差距集中在 EnemyOne 抽象、3×3 主信息区、临界表、列表选中/键盘/滚动、道具使用流程与详情信息;equipbox 已经过新设计,不应回退到 legacy 布局。
|
||||
|
||||
Output: src/client/ui/enemyOne.tsx 新组件;book.tsx、bookDetail.tsx、toolbox.tsx、equipbox.tsx 的新行为;09-42 源码闸门;执行摘要与对照文档回填。
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/13194/.config/opencode/gsd-core/workflows/execute-plan.md
|
||||
@C:/Users/13194/.config/opencode/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/09-minimum-browser-runnable/09-CONTEXT.md
|
||||
@.planning/phases/09-minimum-browser-runnable/09-LEGACY-UI-COMPARISON.md
|
||||
@src/client/ui/book.tsx
|
||||
@src/client/ui/bookDetail.tsx
|
||||
@src/client/ui/toolbox.tsx
|
||||
@src/client/ui/equipbox.tsx
|
||||
@src/client/misc/hotkey.ts
|
||||
@packages-user/client-base/src/layout/layout.ts
|
||||
@packages-user/client-base/src/components/scroll.tsx
|
||||
@packages-user/client-base/src/use/hotkey.ts
|
||||
|
||||
Legacy 基准来自 git 历史,不在工作树中:
|
||||
- `7428f41^:packages/legacy-ui/src/ui/book.vue`
|
||||
- `f53f611^:packages/legacy-ui/src/components/enemyOne.vue`
|
||||
- `f53f611^:packages/legacy-ui/src/panel/enemySpecial.vue`
|
||||
- `f53f611^:packages/legacy-ui/src/panel/enemyCritical.vue`
|
||||
- `f53f611^:packages/legacy-ui/src/ui/bookDetail.vue`
|
||||
- `f53f611^:packages/legacy-ui/src/ui/toolbox.vue`
|
||||
- `f53f611^:packages/legacy-ui/src/tools/toolbox.tsx`
|
||||
- `f53f611^:packages/legacy-ui/src/ui/equipbox.vue`
|
||||
- `f53f611^:packages/legacy-ui/src/controller.ts`(holdOn 语义)
|
||||
|
||||
约束:本计划不修改 09-CONTEXT.md;不新增接线/装配/注册/菜单入口;不提交代码。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报 09-42 计划并等待「可以执行」</name>
|
||||
<what-built>
|
||||
本任务不产出代码,产出的是 09-42 的汇报与批准基线,内容与下方 action 的各节一一对应:范围与文件、精确纳入的 ID、精确排除的 ID、共享排除项、待批准命名(D-04)、精确布局参数(含 adjustGrid(180, 72, 56, 20, 4, 4) 与 adjustGrid(548, 104, 260, 12, 20, 1))、行为变化、矛盾清单 a-i(含 D-20 覆盖)、既有注释/jsDoc 删除清单、验证方式(eslint + 源码结构闸门 + 范围差集),并明确声明类型正确性不在验收范围、类型错误允许存在,只强制代码规范与功能大致覆盖。
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
逐条核对下方 action 的汇报内容:1) 纳入 ID 与排除 ID 是否与你的要求一致;2) 待批准命名(EnemyOne props、getEnemyDamageData/getDamageColor/getSpecialSummary、criticalFont、ToolBoxProps.attributes、IHeroAttrItem)是否可接受;3) 布局参数与 3×3 / 8×2 的 adjustGrid 结果是否正确;4) 矛盾清单 a-i(含 D-20 显式覆盖)与既有注释删除清单是否已覆盖所有需要你拍板的点;5) 验证方式是否为「eslint 规范检查 + 源码结构闸门 + hotkeySettingsGate + git 范围差集」且不做类型检查对比、不做浏览器 UAT、不提交代码;6) 是否明确声明类型正确性不在验收范围、类型错误允许存在。任何一条不满足都应先提出修改,而不是直接批准。
|
||||
</how-to-verify>
|
||||
<resume-signal>仅当用户恰好回复「可以执行」四个字才继续;其它任何回复(包括「可以」「继续」「开始」)都不算通过,必须继续等待。</resume-signal>
|
||||
<files>.planning/phases/09-minimum-browser-runnable/09-42-PLAN.md</files>
|
||||
<action>
|
||||
在开始任何实现前,向用户逐条汇报以下内容,然后停下等待用户恰好回复「可以执行」;未收到该四字之前不得进入 Task 1..Task 6。
|
||||
|
||||
一、范围与文件:只改 src/client/ui/enemyOne.tsx(新增)、book.tsx、bookDetail.tsx、toolbox.tsx、equipbox.tsx、src/client/ui/__test__/legacyUiParityGate.test.ts(新增),以及 09-42-PLAN.md、09-42-SUMMARY.md、09-LEGACY-UI-COMPARISON.md 三个规划产物;不新增接线/装配/注册/菜单入口。
|
||||
|
||||
二、精确纳入的 ID:
|
||||
book:BL-03 空列表、BL-07 选中/悬停、BL-11 键盘上下、BL-12 键盘翻五、BL-13 确认/退出、BL-14 选中自动滚动、BL-17 伤害颜色、BL-24 选中索引;并按用户本次覆盖纳入 BL-05 行结构复用 EnemyOne、BL-16 行内图标/名称/特殊/9 属性。
|
||||
bookDetail:BD-07 主信息区 EnemyOne、BD-08 怪物图标(普通 icon,无动画)、BD-10 特殊摘要、BD-11 3×3 属性网、BD-12 伤害显示、BD-13 临界显示、BD-14 减伤显示、BD-15 防御减伤(1 防)、BD-16 3×3 使用 adjustGrid、BD-21 临界表、BD-22 临界数据(用新生成器,不用 legacy calCritical API)、BD-27 修复 criticlas/critical 不一致与模式切换不重算;保持 BD-02 键盘关闭、BD-25 模式切换、BD-26 临界 0 回退。
|
||||
toolbox:TB-02 跳到装备栏、TB-03 双列表 + 右侧详情、TB-12 使用失败提示、TB-13 使用流程、TB-14 左右切换、TB-15 上下移动、TB-16 确认使用、TB-17 关闭/切换道具、TB-18 详情图标、TB-20 详情类型、TB-21 使用提示;保持 TB-22 纯文本描述、TB-25 空状态。
|
||||
equipbox:只纳入 EQ-02 跳到道具栏、EQ-18 已装备拖到另一槽、EQ-27 键盘(退出/再按装备界面键关闭;quickEquip 是否实现待本次确认)。
|
||||
|
||||
三、精确排除的 ID:
|
||||
book:BL-01 页面范围、BL-08 拖动与点击区分、BL-20 详情 pos/fromBook、BL-21 退出恢复、BL-22 移动端、BL-23 bookScale。
|
||||
bookDetail:BD-03 点击空白/拖动关闭、BD-04 打开/关闭动画、BD-05 面板切换、BD-06 默认面板、BD-19 HTML 描述、BD-23 临界图表/滑条、BD-24 目标面板、BD-28 移动端。
|
||||
toolbox:TB-04 移动端列切换、TB-23 打字动画、TB-24 移动端布局。
|
||||
equipbox:EQ-03 整体布局、EQ-04 属性筛选、EQ-05 数值/百分比筛选、EQ-06 排序方向、EQ-07 独立装备列表、EQ-17 槽位兼容失败提示、EQ-19 勇士图标、EQ-20 勇士属性、EQ-21 选中装备图标/名称、EQ-22 装备孔类型、EQ-23 增减属性列表、EQ-25 打字动画、EQ-28 移动端、EQ-30 拥有装备排序。
|
||||
|
||||
四、共享排除:移动端、临界图表/滑条、目标面板、HTML 描述(保留 TextContent 纯文本)、打字动画、equipbox 筛选/排序、book 缩放;不实现任何接线、装配、注册与菜单入口。
|
||||
|
||||
五、待批准命名(D-04):EnemyOne 的 props `enemy` / `loc` / `damage` / `critical` / `reduction` / `def1` / `damageColor` / `special` / `selected` / `onClick` / `onHover`;辅助函数 `getEnemyDamageData` / `getDamageColor` / `getSpecialSummary`;bookDetail 临界表专用字体常量 `criticalFont`(`Font.defaults({ size: 10 })`);ToolBoxProps 新增可选 `attributes`(默认 `[]`);toolbox 内部本地声明结构等价的 `IHeroAttrItem`(不导出、不从 equipbox 导入);toolbox 选择模型命名 `mode`(值 `'permanent' | 'consumable'`,其中 `consumable` 对应 legacy `mode='tools'`)与 `index`。全部需要在本次汇报中说明用途与备选名。
|
||||
|
||||
六、布局(精确参数):
|
||||
book 行高保持 72,列表区域沿用 LIST_TOP/LIST_LEFT/LIST_WIDTH/LIST_HEIGHT。
|
||||
bookDetail 顶部 EnemyOne 位于 [24, 12, MAIN_WIDTH - 48, 96];中部特殊属性滚动区位于 [24, 116, MAIN_WIDTH - 48, 140];底部临界表标题位于 [24, 264],表区域位于 [24, 272, MAIN_WIDTH - 48, 104];模式切换在左下、返回在右下。
|
||||
3×3 属性网固定使用 adjustGrid(180, 72, 56, 20, 4, 4),结果 cols=3、rows=3、locs.length=9。
|
||||
临界表固定使用 adjustGrid(548, 104, 260, 12, 20, 1),结果 cols=2、rows=8、locs.length=16;行高 12、纵向间隔 1,8 行实际占用 103 像素,配 size=10 的专用字体,确保 8 行完整落在 104 像素高度内且不压住底部按钮。
|
||||
toolbox 两列列表 + 右侧详情,右上装备栏入口、右下退出;equipbox 保持现有状态区/描述/槽位/拥有装备布局。
|
||||
|
||||
七、行为变化:book 增加空状态、选中/悬停、键盘、自动滚动、伤害颜色、选中索引;bookDetail 改为单页结构并修复临界数据,current 为空时只渲染「无怪物数据」而不渲染 EnemyOne;toolbox 增加使用流程、失败提示、交叉导航、键盘、详情图标/类型/提示,并实现 @ui/toolbox 再次按下关闭,初始状态明确采用 legacy 口径:当前列为可消耗列(legacy `mode='tools'`,即消耗道具)、index=0 指向该列第一项;选中高亮由 mode+index 派生,不再保留独立 selected 变量;equipbox 只增加跨界面导航、已装备跨槽拖拽、键盘关闭。
|
||||
|
||||
八、矛盾清单(必须让用户确认):
|
||||
a. 09-LEGACY-UI-COMPARISON.md 把 BD-07 / BD-11 / BD-14 / BD-22 / BL-05 / BL-16 / TB-03 / TB-13 标为「不做」或「部分」,本次用户明确要求对 book/bookDetail/toolbox 做这些;本计划以用户本次指令为准,不擅自改对照表原文,只在执行后追加回填说明。
|
||||
b. 09-42 为本次全新计划,不存在旧 09-42-SUMMARY.md 被覆盖;执行前若发现旧摘要已存在,立即停下汇报。
|
||||
c. 新控制器没有 holdOn,新数据层没有 tryUseItem / canUseItem;拟映射为 controller.keep() + IHeroItems.useItem() + item.raw.effect.canUse 预检,需要用户批准。
|
||||
d. quickEquip 依赖的套装保存/读取接口在新数据层不存在;默认不实现,需要用户确认「as applicable」的口径。
|
||||
e. toolbox 跳到 equipbox 需要 attributes 列表;拟给 toolbox 增加可选 attributes(默认 []),交叉导航时传具体数组,并在 toolbox 内部本地声明结构等价的 IHeroAttrItem(不导入 equipbox 的接口)。双方在点击处理器内通过 props.controller.open 打开目标 UI;导入方式不限,不作为验收条件。需要用户批准新增 prop 与本地类型命名。
|
||||
f. bookDetail 的模式切换只在有 view 时有效;从 book 打开的是基础 enemy,切换按钮仍为 no-op,需要用户确认这是否可接受。
|
||||
g. 现有代码存在若干类型诊断(如 icon 的 AllNumbers 类型);本计划把类型诊断排除在验收之外,修不修、怎么修由用户自行决定,执行者不以此阻塞。
|
||||
h. bookDetail 的 current 可能为空;本计划在渲染前判空,为空时渲染「无怪物数据」,不把空值传入 EnemyOne(属功能行为,不是类型验收项)。
|
||||
i. 09-CONTEXT.md 的 D-20 明确声明「移植后不要求与 legacy 显示效果一致,形态需调整,且由用户逐个讨论确定」;本次用户指令明确要求 book / bookDetail / toolbox 按 legacy 视觉与功能尽量补齐,这是对 D-20 的**显式覆盖**(user override)。本计划以用户本次指令为准,不修改 09-CONTEXT.md,只在 09-42-SUMMARY.md 中记录该覆盖及其与 D-20 的关系;equipbox 仍保持现有新布局,不回退 legacy。
|
||||
|
||||
九、需要批准的既有注释/jsDoc 删除(项目规则:不得擅自修改已有注释,故先列清单):
|
||||
book.tsx:删除 `getDamageData`、`specialText`、`statX` 三个函数的 jsDoc 注释;删除 `STAT_LEFT`、`statFont`、`format` 的常量/函数注释;删除仅被 getDamageData 使用的 `isNil` 导入。
|
||||
bookDetail.tsx:删除 `nameFont` 注释、`statX` 注释;`SPECIAL_TOP` / `SPECIAL_HEIGHT` / `SPECIAL_SCROLL_LOC` 若被新布局常量取代则删除其注释;删除 JSX 中引用未定义 `critical` 的两段文本(由 criticlas 表替代);删除顶部标题 + 5 项属性块中仅被其使用的 `const enemy = current.value`(删除后该变量不再被引用),但保留 `format`,因为底部临界表继续复用 `format(nextDiff)` / `format(damageDiff)`。
|
||||
toolbox.tsx:删除 `selected = ref(-1)` 的注释与 `clickItem` 中基于 `item.num === selected.value` 的选中/使用分支注释,改由 `mode` / `index` 派生选中态与高亮;若保留 `clickItem`,只保留「再次点击当前同一项触发使用」的注释并改写为 mode/index 语义。
|
||||
equipbox.tsx:`endPress` 上方的「拖拽已装备的装备,释放时卸下」行注释因 EQ-18 行为变化而失效,需改写为「拖拽已装备的装备:目标槽有效且可装备则换装,否则卸下」。
|
||||
以上删除/改写只在用户批准后执行;未获批准则保留原注释并在摘要中如实记录。
|
||||
|
||||
十、验证方式:只做三项。(a) eslint 直接调用 node_modules/eslint/bin/eslint.js,检查 6 个改动文件的风格/格式/unused/命名;(b) 新增 09-42 源码结构闸门 + 现有 hotkeySettingsGate,用 ?raw 断言关键文件与功能存在(EnemyOne、adjustGrid 参数、临界表、book/toolbox/equipbox 关键行为);(c) 范围差集:用 Task 1 前 `git status --porcelain -uall` 基线与执行后对比,只允许 files_modified 中的路径新增,且不得出现 09-35 或其他计划的 PLAN/SUMMARY 变化。类型正确性不在验收范围:不跑 vue-tsc,不做诊断基线对比;类型错误、循环引用、GameUI.get 死路径、locator 类型不匹配等都不是阻塞项,代码由用户自行审查与修改。不做浏览器 UAT;不提交代码。
|
||||
|
||||
十一、执行范围确认:本计划保持「一个计划」,共 Task 0 + 6 个实现任务;验收只覆盖代码规范与功能大致覆盖,类型正确性不在范围。请在本次汇报中确认该口径。
|
||||
|
||||
汇报完成后必须停下,等待用户恰好回复「可以执行」;在此之前不得读取并执行 Task 1..Task 6 的实现步骤。
|
||||
</action>
|
||||
<verify>
|
||||
<human-check>用户恰好回复「可以执行」四个字。任何其他回复(包括「可以」「继续」「开始」)都不算通过,必须继续等待。</human-check>
|
||||
</verify>
|
||||
<done>用户已明确回复「可以执行」,且上述纳入/排除 ID、待批准命名、矛盾清单、验证方式以及「类型正确性不在验收范围」的口径均已得到确认;未获确认前 Task 1..Task 6 均未开始。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 新建 EnemyOne 与共享显示辅助函数</name>
|
||||
<files>src/client/ui/enemyOne.tsx</files>
|
||||
<action>
|
||||
执行前基线(本任务第一步,必须在任何文件写入之前完成):
|
||||
运行只读命令 `git status --porcelain -uall > $env:TEMP\09-42-git-baseline.txt`,紧接着立即捕获 `$LASTEXITCODE`;若 `$LASTEXITCODE -ne 0` 立即 exit 1(基线命令失败不得继续),并断言基线文件存在且非空(`Test-Path` 为真且 `Get-Content -Raw` 非空白;为空视为命令异常,必须停下核对)。该输出作为脏树基线,供 Task 6 范围核对使用。
|
||||
基线文件只写入系统临时目录,不进入仓库,不修改任何计划或源码文件。不采集类型基线,不运行 vue-tsc。
|
||||
|
||||
新建 src/client/ui/enemyOne.tsx,作为 book 行与 bookDetail 顶部的唯一主信息组件。
|
||||
|
||||
组件 props(命名以 Task 0 批准结果为准):`enemy: IReadonlyEnemy<IEnemyAttr>`(必填,不接收 undefined)、`loc: ElementLocator`、`damage: string`、`critical: string`、`reduction: string`、`def1: string`、`damageColor: string`、`special: string`、`selected?: boolean`、`onClick?: () => void`、`onHover?: () => void`。使用 defineComponent + SetupComponentOptions 声明 props 列表,参照 book.tsx 现有写法。
|
||||
|
||||
布局:根节点为 `<container loc={props.loc}>`,内部坐标从 0 开始,宽度取 props.loc[2] ?? 200,高度取 props.loc[3] ?? 72。左侧固定宽度区域放普通 `<icon>`(使用 enemy.code 作为图标)、名称文本、特殊摘要文本;右侧用固定参数 `adjustGrid(180, 72, 56, 20, 4, 4)` 生成 3×3 网格,结果必须满足 cols=3、rows=3、locs.length=9;若 locs.length !== 9 直接抛出错误,不静默降级。网格左上角放在 `[200, Math.max(0, (height - 72) / 2)]`,每个单元格按 `[200 + loc[0], gridTop + loc[1]]` 渲染;按行优先顺序渲染 生命/攻击/防御/金币/经验/伤害/临界/减伤/1 防 九项,颜色沿用当前 book 的 lightgreen / lightcoral / lightblue / lightyellow / lawngreen / damageColor / lightsalmon / lightpink / cyan。伤害项使用 props.damageColor,其余伤害相关项使用固定色。
|
||||
|
||||
选中态:在根 container 内渲染一个覆盖整行的 `<g-rectr fill={false}>` 描边(必须显式写 `fill={false}`,禁止绘制背景填充),并同时设置 `stroke`(布尔,启用描边)与 `strokeStyle`(selected 为真时 gold,否则透明)。根 container 绑定 onClick 与 onEnter(分别调用 props.onClick / props.onHover),事件处理器必须判空后再调用。
|
||||
|
||||
组件顶层不得有副作用(R-7);为每个顶层变量/常量/函数写 jsDoc(R-3);JSX 分段写注释(R-4);不导出不需要的类型(R-5);非空判断字面量用 isNil、对象用 !(R-6);不修改任何既有注释。
|
||||
|
||||
同文件导出三个辅助函数(命名以 Task 0 批准结果为准):
|
||||
1. `getEnemyDamageData(system, enemy, hero, locator)`:locator 参数按现状接收(book 行传 undefined、bookDetail 传 props.locator),不做类型兼容性要求,把 book.tsx 现有 getDamageData 的全部逻辑原样迁移过来,包括 `system.calculateEnemyCritical(enemy, 'atk', locator).next()`、`system.with(isolated)`、`isolated.add('def', 1)`、`getDamageInfoByComputed`、未破防/已破防/无法战斗分支与 `core.formatBigNumber`。返回 `{ damage, critical, reduction, def1, damageColor }`,其中前四项为格式化后的展示字符串,damageColor 为原始伤害对应的颜色;damageColor 必须用 `getDamageColor(rawDamage, hero.getAttribute().getFinalAttribute('hp'))` 计算(英雄当前 HP,不是敌人 HP,也不是硬编码常量)。`system` 可为 null;`enemy` 也可能为空(bookDetail 的 current 可能为空):`system` 为 null 或 `enemy` 为空时统一回退 `无法战斗 / 0 / 0 / 0` 与红色。
|
||||
2. `getDamageColor(damage: number, heroHp: number)`:按 legacy getDamageColor 的阈值返回颜色——非数字红、0 为 #2f2、负数为 #7f7、低于 hp/3 为白、低于 2hp/3 为 #ff4、低于 hp 为 #f93、否则 #f22。
|
||||
3. `getSpecialSummary(enemy)`:按 code 排序特殊属性,取名称;0 个返回「无属性」,1 个返回该名称,2 个返回两个名称空格分隔,3 个及以上返回前两个名称加「 ...」。不要把描述拼进来。
|
||||
|
||||
不要使用图标动画组件,不要写移动端分支,不要自行绘制背景,不要引入新依赖。
|
||||
</action>
|
||||
<verify>
|
||||
<automated>node node_modules/eslint/bin/eslint.js src/client/ui/enemyOne.tsx</automated>
|
||||
</verify>
|
||||
<done>enemyOne.tsx 存在并导出 EnemyOne 与三个辅助函数;组件使用 adjustGrid(180, 72, 56, 20, 4, 4) 生成 cols=3/rows=3/locs.length=9,使用普通 icon 与同时设置 stroke/strokeStyle、`fill={false}` 的选中描边,支持 selected/onClick/onHover,无动画/移动端/新依赖;getEnemyDamageData 对空 system 与空 enemy 均有回退,damageColor 以英雄当前 HP 计算;eslint 通过。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: book.tsx 改用 EnemyOne 并补齐列表交互</name>
|
||||
<files>src/client/ui/book.tsx</files>
|
||||
<action>
|
||||
修改 book.tsx:
|
||||
1. 删除行内手写的九项文本,并显式删除以下因迁移而不再使用的符号(含其既有注释,已列入 Task 0 待批准清单):`getDamageData` 函数与 jsDoc、`specialText` 函数与 jsDoc、`statX` 函数与 jsDoc、`STAT_LEFT` 常量与注释、`statFont` 常量与注释、`format` 函数与 jsDoc、`isNil` 导入。保留按 code 去重、`ctx` 监听、`openDetail` 的现有 UI 栈调用与右下退出按钮;`nameFont` 仍用于退出文本,保留。
|
||||
2. 引入 EnemyOne、getEnemyDamageData、getSpecialSummary、useKey、ScrollExpose、ref、watch、nextTick;不要引入 getDamageColor(它只在 enemyOne.tsx 内部使用),不要引入未使用的符号,否则 eslint 会报 no-unused-vars。
|
||||
3. 新增 `selected = ref(0)` 与 `scrollRef = ref<ScrollExpose>()`;`watch(enemies, ...)` 在列表变化时把 selected 夹到 0..length-1;列表为空时 selected 保持 0。
|
||||
4. 新增移动函数:上移 selected-1、下移 selected+1、上翻五 selected-5、下翻五 selected+5,全部夹在有效范围;翻五与 legacy 一致:上翻时小于等于 4 直接到 0,否则减 5;下翻时距离末尾不足 5 直接到末尾,否则加 5。
|
||||
5. 新增自动滚动:`watch(selected, async () => { await nextTick(); scrollRef.value?.scrollTo(target, 100); })`,target 取 `selected * ROW_HEIGHT - (LIST_HEIGHT - ROW_HEIGHT) / 2` 并夹到非负;不要读取当前滚动位置(ScrollExpose 不提供)。
|
||||
6. 使用 `const key = useKey(props.state.hotkey.system)!`,实现 `@ui/book-up`、`@ui/book-down`、`@ui/book-page-up`、`@ui/book-page-down`、`@common/confirm`、`@common/exit`;confirm 在列表非空时对当前 selected 调用 openDetail,exit 调用现有 close。
|
||||
7. 渲染:若 enemies 为空,渲染居中的「本层无怪物」文本并跳过 Scroll;否则给 Scroll 加 `ref={scrollRef}`,每行渲染一个 EnemyOne,loc 为 `[0, i * ROW_HEIGHT, LIST_WIDTH, ROW_HEIGHT]`,传入该敌人的 getEnemyDamageData 结果。必须显式写出全部四个实参:先 `const system = ctx.value.getDamageSystem()`(可为 null),再对每行调用 `getEnemyDamageData(system, enemy, props.state.hero, undefined)`(依次为 system、enemy、hero、locator;locator 传 undefined),然后配合 getSpecialSummary、selected={i === selected.value}、onClick={() => openDetail(enemy)}、onHover={() => (selected.value = i)}。行内不再手写任何属性文本。
|
||||
8. 保持 ROW_HEIGHT=72、LIST_TOP/LIST_LEFT/LIST_WIDTH/LIST_HEIGHT 与右下退出按钮;不实现缩放、移动端、拖动与点击区分、详情 pos/fromBook。
|
||||
9. `openDetail` 传 `{ enemy: view, locator: undefined }`,与 bookDetail 的 locator prop 对应;不传 view 时不构造 locator。类型细节不作为验收条件。
|
||||
</action>
|
||||
<verify>
|
||||
<automated>node node_modules/eslint/bin/eslint.js src/client/ui/book.tsx</automated>
|
||||
</verify>
|
||||
<done>book.tsx 使用 EnemyOne 渲染行;具备空状态、选中/悬停、六个键盘命令、选中自动滚动、伤害颜色与选中索引;点击仍通过现有 UI 栈打开详情并显式传 locator: undefined;已删除 getDamageData/specialText/statX/STAT_LEFT/statFont/format/isNil,eslint 无 unused;无缩放/移动端/手写 statX;eslint 通过。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: bookDetail.tsx 改为 EnemyOne + 特殊列表 + 临界表单页</name>
|
||||
<files>src/client/ui/bookDetail.tsx</files>
|
||||
<action>
|
||||
修改 bookDetail.tsx:
|
||||
1. 删除当前顶部标题 + 5 项属性 + 底部两段文本的简化布局,改为三区单页:顶部 EnemyOne、中部特殊属性 Scroll、底部临界表;保留左下模式切换与右下返回按钮。
|
||||
2. 引入 EnemyOne、getEnemyDamageData、getSpecialSummary、adjustGrid、useKey;保留 current/specials/specialRows/textHeights 的现有逻辑与 TextContent 纯文本描述(autoHeight + 实测高度 + cumsum)。不要引入 nextTick 等未使用符号;显式删除因布局替换而不再使用的 `nameFont` 常量与注释、`statX` 函数与注释,以及顶部标题 + 5 项属性块中仅被其使用的 `const enemy = current.value`(删除该块后不再有任何引用,必须一并删除,否则 eslint no-unused-vars 报错);`format` 必须保留,因为底部临界表继续复用 `format(nextDiff)` / `format(damageDiff)`。`SPECIAL_LEFT` / `SPECIAL_WIDTH` / `SPECIAL_LABEL_HEIGHT` / `SPECIAL_MIN_HEIGHT` / `SPECIAL_GAP` 继续使用,`SPECIAL_TOP` / `SPECIAL_HEIGHT` / `SPECIAL_SCROLL_LOC` 若被新布局常量取代则删除其常量与注释。以上删除已列入 Task 0 待批准清单。
|
||||
3. 在渲染前定义 `const system = props.state.enemyContext.getDamageSystem()`(先取 system,再调用 getEnemyDamageData),然后 damage/critical/reduction/def1/damageColor 由 `getEnemyDamageData(system, current.value, props.state.hero, props.locator)` 得到;special 用 `current.value ? getSpecialSummary(current.value) : '无属性'`。顶部 EnemyOne 的 loc 取 `[24, 12, MAIN_WIDTH - 48, 96]`;只在 `current.value` 非空时渲染 `<EnemyOne enemy={current.value} ... />`,否则渲染一条居中的「无怪物数据」文本。
|
||||
4. 中部特殊属性 Scroll 使用 `[24, 116, MAIN_WIDTH - 48, 140]`;无特殊属性时可显示「无特殊属性」;保留每行名称颜色与冒号、描述 TextContent 的实测高度逻辑。
|
||||
5. 底部临界表:标题「临界」放在 `[24, 264]`;表头「加攻」「减伤」两列;表区域固定为 `[24, 272, MAIN_WIDTH - 48, 104]`,固定使用 `adjustGrid(548, 104, 260, 12, 20, 1)` 生成 8 行 × 2 列(cols=2、rows=8、locs.length=16);行优先顺序下第 i 行取 `locs[2 * i]` 为加攻列、`locs[2 * i + 1]` 为减伤列。专用临界表字体 `criticalFont = Font.defaults({ size: 10 })`,行高 12、纵向间隔 1,8 行实际占用 103 像素,确保 8 行完整落在 104 像素内且不压住底部按钮。每行取 `criticlas.value[i]`,加攻列显示 format(nextDiff),减伤列显示 format(damageDiff),缺失条目回退 0,保证恰好 8 行(0 回退)。不要用 statX 或手写列坐标。
|
||||
6. 数据修复:JSX 只使用 `criticlas`,彻底移除未定义的 `critical` 及其两段文本;`getCriticals` 改为接收本次 current 值并先判空:取 `const target = value ?? current.value`,`target` 为空时把 `criticlas.value` 置 [] 并返回;后续一律使用判空后的 `target`。使用 `watch(current, value => getCriticals(value), { immediate: true })`(替换现有 `watch(() => props.enemy, ...)` 与 `watch(() => props.view, ...)`);immediate 首次执行时 current 可能为空,必须走 `criticlas.value = []` 分支并直接 return。模式切换时 current 会变化,因此临界表随之重算。`getCriticalGenerator` 在 Base 模式对 target 调 calculateEnemyCritical;在 Computed 且 props.view 存在时调 calculateViewCritical;Computed 但无 view 时对 target 调 calculateEnemyCritical,避免返回 null 导致表空。
|
||||
7. 模式切换:保留 `toggleMode`,但仅当 `props.view` 存在或 `props.enemy instanceof EnemyView` 时才切换;从 book 传入基础 enemy 时按钮仍渲染但为 no-op,按 Task 0 的确认执行。保留 0 回退与左下按钮文案。
|
||||
8. 使用 useKey(props.state.hotkey.system) 实现 `@common/exit` 与 `@common/confirm` 关闭详情(legacy BD-02);不要实现面板切换、图表/滑条、目标面板、移动端、动画;不要修改 TextContent 的纯文本约定。
|
||||
9. 在 `bookDetailProps.props` 数组中补上 `locator`,使 `props.locator` 可用;book 的 `openDetail` 传 `locator: undefined`。
|
||||
</action>
|
||||
<verify>
|
||||
<automated>node node_modules/eslint/bin/eslint.js src/client/ui/bookDetail.tsx</automated>
|
||||
</verify>
|
||||
<done>bookDetail.tsx 为单页三区结构;顶部仅在 current 非空时渲染 EnemyOne(否则「无怪物数据」),中部特殊属性列表,底部 adjustGrid(548, 104, 260, 12, 20, 1) 生成的 8 行加攻/减伤临界表(cols=2/rows=8/locs.length=16,size=10 字体完整放入 104px);system 先经 props.state.enemyContext.getDamageSystem() 取得;criticlas 一致且由 `watch(current, value => getCriticals(value), { immediate: true })` 重算,getCriticals 先用 target 判空、current 为空时置空返回;已删除 nameFont/statX/未使用 SPECIAL_*/`const enemy = current.value`(保留 `format` 供临界表复用);locator 已加入 props 数组;保留模式切换、0 回退、纯文本描述;无图表/滑条/面板切换/移动端;eslint 通过。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 4: toolbox.tsx 实现双列表详情、使用流程与键盘</name>
|
||||
<files>src/client/ui/toolbox.tsx</files>
|
||||
<action>
|
||||
修改 toolbox.tsx:
|
||||
1. 保持现有两列列表 + 右侧详情的三区结构(永久道具列、可消耗道具列、右侧详情),延续 collect 的 hideInToolbox 过滤与数量降序/图块数字升序排序。
|
||||
2. 增加选择模型:`mode` 表示当前列(`'permanent' | 'consumable'`),`index` 表示该列行号;`selectedItem` 由 `mode === 'permanent' ? permItems.value[index] : consItems.value[index]` 派生(为空时回退 null);行高亮由 `mode === 当前列 && i === index` 派生。删除现有 `selected = ref(-1)` 与 `clickItem` 中基于 `item.num === selected.value` 的选中/使用分支:点击行时先判断 `mode === 目标列 && index === 目标行号`,是则触发使用,否则只把 mode 与 index 设为该目标。初始状态明确采用 legacy 口径:`mode = 'consumable'`(legacy `mode='tools'`,即消耗道具列)、`index = 0`(该列第一项);若可消耗列为空,mode 仍为 consumable、index 仍为 0,selectedItem 为 null,键盘左右可切到永久列,不自动跳转。不得保留既不由 mode/index 派生、又继续被写入的 `selected` 变量,避免陈旧高亮与 unused 报错。
|
||||
3. 详情区补充:使用 `selectedItem` 的普通 icon(`icon={item.num}`,类型写法不限)、名称、类型(永久道具 / 消耗道具)、使用提示「点击该物品以使用」;描述继续用 TextContent 纯文本;未选中时显示「未选择道具」。保留右下退出。
|
||||
4. 使用流程:先取 `const item = selectedItem.value; if (!item) return;`,再做可用性预检 `const canUse = item.raw.effect.canUse?.(item.raw, props.state) ?? true`;canUse 为 false 时设置失败提示「当前无法使用该道具!」并在约 2 秒后清除,不关闭界面。canUse 为 true 时调用 `props.controller.keep()`,随后 `props.controller.close(props.instance)`,在 nextTick 中调用 `hero.items.useItem(item.num)`,最后调用 keep 返回对象的 safelyUnload()。不要使用不存在的 holdOn/tryUseItem;不得自造接口。清理定时器要放在 onUnmounted。
|
||||
5. 交叉导航:在右上角增加可点击的「装备栏 →」;为 ToolBoxProps 增加可选 `attributes?: readonly IHeroAttrItem[]`,其中 `IHeroAttrItem` 在 toolbox.tsx 内部本地声明(`readonly id: keyof IHeroAttr; readonly name: string;`,`IHeroAttr` 从 `@user/data-common` 导入),不导出;命名待 Task 0 批准。把它加入 `toolBoxProps.props` 数组,组件内归一化为 `const attributes = props.attributes ?? []`。点击处理器按顺序:`props.controller.keep()`、`props.controller.close(props.instance)`,打开 `EquipBoxUI`(`props.controller.open(...)`),传入 `{ heroState: props.heroState, attributes: props.attributes ?? [] }`。导入方式不限(静态或动态均可),不作为验收条件。
|
||||
6. 键盘:使用 useKey(props.state.hotkey.system)!,实现 `@common/select-left` / `@common/select-right` 切换列、`@common/select-up` / `@common/select-down` 移动行、`@common/confirm` 使用当前项、`@common/exit` 关闭,以及 `key.implement('@ui/toolbox', exit)` 实现再次按道具界面键关闭(TB-17);索引变化后夹到当前列长度内,空列时 confirm 不做任何事。
|
||||
7. 空状态:无道具时显示「未选择道具」或对应空提示;不要实现移动端、打字动画、丢弃、拖拽。
|
||||
</action>
|
||||
<verify>
|
||||
<automated>node node_modules/eslint/bin/eslint.js src/client/ui/toolbox.tsx</automated>
|
||||
</verify>
|
||||
<done>toolbox.tsx 具备双列列表 + 详情、详情图标/名称/类型/提示、使用流程与失败提示、装备栏交叉导航(通过 controller.open 打开 EquipBoxUI,attributes 默认 [] 且传 `props.attributes ?? []`)、@ui/toolbox 再次按下关闭、初始为可消耗列(legacy mode='tools')/index=0、选中态与高亮由 mode+index 派生且无残留 selected 变量、左右/上下/确认/退出键盘;描述保持纯文本;无移动端/打字/丢弃/拖拽;eslint 通过。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 5: equipbox.tsx 仅补 EQ-02 / EQ-18 / EQ-27</name>
|
||||
<files>src/client/ui/equipbox.tsx</files>
|
||||
<action>
|
||||
修改 equipbox.tsx,但保持现有新布局、状态区、描述区、槽位区、拥有装备区与拖拽浮层不变;不复制 legacy 三列布局、筛选、排序、勇士图标/属性、移动端与打字动画。
|
||||
|
||||
EQ-02:在顶部右上角增加可点击的「道具栏 →」;点击处理器按顺序:`props.controller.keep()`、`props.controller.close(props.instance)`,再打开 `ToolBoxUI`(`props.controller.open(...)`),传 `{ heroState: props.heroState, attributes: props.attributes }`;保留右下退出。导入方式不限(静态或动态均可),不作为验收条件。
|
||||
|
||||
EQ-18:修改 endPress 的 `fromSlot >= 0` 分支。当前逻辑是拖拽已装备一律卸下;改为:进入分支后先捕获 `const target = hoverSlot.value`(沿用现有 endPress 已有的 target 捕获写法,不要在判断前重置 hoverSlot),然后仅当 `target >= 0`、`target !== fromSlot`、且 `hero.equip.canEquipTo(uid, target) !== EquipStatus.CannotEquip` 时,调用 `hero.equip.equip(uid, target, true)`(autoUnload 自动处理目标槽原有装备);否则继续 `hero.equip.unequip(fromSlot)`。所有后续判断都使用捕获的 `target`,不得改用重置后的 `hoverSlot`;目标槽悬停沿用现有 getSlotEnter/getSlotLeave 与 hoverSlot,不改拖拽阈值、浮层与 pressing 流程。同时把 `endPress` 上方「拖拽已装备的装备,释放时卸下」的行注释改写为「拖拽已装备的装备:目标槽有效且可装备则换装,否则卸下」(该注释已列入 Task 0 批准清单;未获批则保留原注释并在摘要中记录)。
|
||||
|
||||
EQ-27:使用 useKey(props.state.hotkey.system)!,实现 `@common/exit` 关闭、`@ui/command` 再次关闭(对应 legacy equipbox 键)。quickEquip 在新数据层没有套装保存/读取接口,默认不实现、不伪造接口;若 Task 0 用户明确给出映射则按确认结果补上,否则在执行摘要中记录该项因缺接口未实现。
|
||||
|
||||
icon 类型:类型诊断不作为验收条件;如需消除现有诊断,可在 icon 处使用单次 `as` 断言,不改变任何布局或视觉。
|
||||
|
||||
保持 `IHeroAttrItem` 为 equipbox.tsx 内部 interface(不新增导出);toolbox 使用本地结构等价类型。不要导出其他不必要类型。不要修改既有布局常量与视觉。
|
||||
</action>
|
||||
<verify>
|
||||
<automated>node node_modules/eslint/bin/eslint.js src/client/ui/equipbox.tsx</automated>
|
||||
</verify>
|
||||
<done>equipbox 保持新布局;右上道具栏通过 controller.open 打开 ToolBoxUI(传 props.attributes);已装备可拖到另一有效槽位装备、否则卸下;键盘退出/再按装备界面关闭;quickEquip 按 Task 0 确认结果处理;`IHeroAttrItem` 保持内部非导出;eslint 通过。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 6: 新 09-42 源码结构闸门与最终验证</name>
|
||||
<files>src/client/ui/__test__/legacyUiParityGate.test.ts</files>
|
||||
<action>
|
||||
新建 src/client/ui/__test__/legacyUiParityGate.test.ts,使用 ?raw 导入 enemyOne.tsx、book.tsx、bookDetail.tsx、toolbox.tsx、equipbox.tsx 与 src/client/misc/hotkey.ts;不依赖 DOM,不使用 node:fs,不启动浏览器。
|
||||
|
||||
每个 it 前写一行注释说明用途(测试文件豁免既有注释禁令,但必须遵守这一行注释规则)。提供 stripComments 辅助函数抹掉块注释、JSX 注释与行注释,避免否定断言被说明文字误伤。
|
||||
|
||||
本闸门只做「源码结构 / 功能存在性」检查:不检查类型正确性,不断言没有静态循环引用,不断言不出现 `GameUI.get`,不对 locator 的 undefined/null 类型做任何要求。
|
||||
|
||||
断言覆盖:
|
||||
1. EnemyOne:文件存在且导出 EnemyOne;使用 adjustGrid(180, 72, 56, 20, 4, 4);包含普通 icon;选中描边 `<g-rectr fill={false}>` 同时出现 `stroke` 与 `strokeStyle`;支持 selected/onClick/onHover;导出 getEnemyDamageData/getDamageColor/getSpecialSummary。
|
||||
2. book:包含 EnemyOne 调用、getEnemyDamageData/getSpecialSummary、useKey、六个命令 ID(@ui/book-up、@ui/book-down、@ui/book-page-up、@ui/book-page-down、@common/confirm、@common/exit)、ScrollExpose 与 scrollTo、「本层无怪物」、selected、openDetail。
|
||||
3. bookDetail:包含 EnemyOne、adjustGrid(548, 104, 260, 12, 20, 1)、criticlas、加攻、减伤、以空白容错正则 `/CRITICAL_COUNT\s*=\s*8/` 断言 CRITICAL_COUNT 为 8(允许等号两侧有任意空白)、TextContent、@common/exit 与 @common/confirm、`watch(current,` 与 `{ immediate: true }`、`getDamageSystem()`、「无怪物数据」、`locator`。
|
||||
4. toolbox:包含 EquipBoxUI、attributes、`props.attributes ?? []`、keep(、useItem、「当前无法使用该道具」、@ui/toolbox、@common/select-left/right/up/down、@common/confirm、@common/exit、TextContent、装备栏、详情类型与使用提示、初始 `'consumable'` 与 index=0(对应 legacy mode='tools')、由 mode/index 派生的行高亮;不出现 `selected = ref(-1)` 残留符号。
|
||||
5. equipbox:包含 ToolBoxUI、keep(、@common/exit、@ui/command、endPress、target、equip(、现有布局常量(STATUS_WIDTH / OWNED_TOP);不出现 legacy 筛选/排序/移动端/打字动画。
|
||||
6. hotkey.ts:确认 @ui/book-up/down/page-up/page-down、@ui/toolbox 与 @common/confirm/exit 均存在且各声明一次;不修改 hotkey.ts。
|
||||
7. 范围排除(功能层):五个 UI 源码中不出现移动端判断、HTML 描述注入、临界图表/滑条、目标面板、book 缩放。
|
||||
8. adjustGrid 数学:测试文件从纯布局模块 `@user/client-base/layout/layout` 导入 `adjustGrid`,断言 `adjustGrid(180, 72, 56, 20, 4, 4)` 的 cols=3、rows=3、locs.length=9,以及 `adjustGrid(548, 104, 260, 12, 20, 1)` 的 cols=2、rows=8、locs.length=16;该模块只把 `ElementLocator` 当类型使用,esbuild 会擦除,不会加载 render/DOM。若该子路径解析失败或运行时拉入 DOM,则在闸门内按同一公式独立计算并断言,不得改回 barrel。
|
||||
9. 不做浏览器 UAT;本闸门只断言源码结构与 adjustGrid 数学结果。
|
||||
|
||||
然后执行最终验证:
|
||||
- 新闸门:`node node_modules/vitest/vitest.mjs run src/client/ui/__test__/legacyUiParityGate.test.ts`
|
||||
- 现有绿色热键闸门:`node node_modules/vitest/vitest.mjs run src/client/ui/__test__/hotkeySettingsGate.test.ts`
|
||||
- eslint:`node node_modules/eslint/bin/eslint.js src/client/ui/enemyOne.tsx src/client/ui/book.tsx src/client/ui/bookDetail.tsx src/client/ui/toolbox.tsx src/client/ui/equipbox.tsx src/client/ui/__test__/legacyUiParityGate.test.ts`
|
||||
- 类型检查:不执行。本计划不做 vue-tsc、不做诊断基线对比;类型错误、循环引用、GameUI.get、locator 类型均不是失败条件,代码由用户自行审查与修改。
|
||||
- 范围核对(脏树基线差集):运行只读 `git status --porcelain -uall`,输出写入 `$env:TEMP\09-42-git-after.txt`;重定向后必须检查 `$LASTEXITCODE -eq 0` 且 after 文件非空、基线文件非空,任一不满足立即 exit 1,避免 Compare-Object 因空输入假通过;用 `Compare-Object` 只取 SideIndicator 为 `=>` 的新增条目,确认每个新增路径都属于 files_modified(enemyOne.tsx、book.tsx、bookDetail.tsx、toolbox.tsx、equipbox.tsx、legacyUiParityGate.test.ts、09-42-PLAN.md、09-42-SUMMARY.md、09-LEGACY-UI-COMPARISON.md);并显式断言新增路径中不出现 09-35 的任何文件,也不出现其它计划的 *-PLAN.md / *-SUMMARY.md。基线中已存在的其它脏文件不计入本次失败,但若出现基线之外、且不在 files_modified 的新增路径,必须停下汇报。**已知局限(必须在 SUMMARY 中如实记录):差集只能发现新增/删除的路径,无法识别「基线时已脏、但本次又修改了内容」的文件;所有实际写入必须只发生在 files_modified 内。** 不得运行任何 git 写命令,不得提交。
|
||||
|
||||
不运行 vue-tsc,不做类型诊断对比,不把类型错误、循环引用、GameUI.get、locator 类型当作失败条件。不运行 uiContainerGate 与 keyAdaptationGate;如果执行中发现必须更新它们,先停下向用户汇报并取得同意,不得静默修改。
|
||||
</action>
|
||||
<verify>
|
||||
<automated>node node_modules/vitest/vitest.mjs run src/client/ui/__test__/legacyUiParityGate.test.ts; if (-not $?) { exit 1 }; node node_modules/vitest/vitest.mjs run src/client/ui/__test__/hotkeySettingsGate.test.ts; if (-not $?) { exit 1 }; node node_modules/eslint/bin/eslint.js src/client/ui/enemyOne.tsx src/client/ui/book.tsx src/client/ui/bookDetail.tsx src/client/ui/toolbox.tsx src/client/ui/equipbox.tsx src/client/ui/__test__/legacyUiParityGate.test.ts; if (-not $?) { exit 1 }; if (-not (Test-Path "$env:TEMP\09-42-git-baseline.txt")) { Write-Output 'missing git baseline'; exit 1 }; if ([string]::IsNullOrWhiteSpace((Get-Content "$env:TEMP\09-42-git-baseline.txt" -Raw))) { Write-Output 'git baseline is empty'; exit 1 }; git status --porcelain -uall > $env:TEMP\09-42-git-after.txt; if ($LASTEXITCODE -ne 0) { Write-Output 'git status failed'; exit 1 }; if ([string]::IsNullOrWhiteSpace((Get-Content "$env:TEMP\09-42-git-after.txt" -Raw))) { Write-Output 'git status produced no output'; exit 1 }; $added = @(Compare-Object (Get-Content "$env:TEMP\09-42-git-baseline.txt") (Get-Content "$env:TEMP\09-42-git-after.txt") | Where-Object SideIndicator -eq '=>' | ForEach-Object { $_.InputObject -replace '^..\s+','' }); $allowed = @('src/client/ui/enemyOne.tsx','src/client/ui/book.tsx','src/client/ui/bookDetail.tsx','src/client/ui/toolbox.tsx','src/client/ui/equipbox.tsx','src/client/ui/__test__/legacyUiParityGate.test.ts','.planning/phases/09-minimum-browser-runnable/09-42-PLAN.md','.planning/phases/09-minimum-browser-runnable/09-42-SUMMARY.md','.planning/phases/09-minimum-browser-runnable/09-LEGACY-UI-COMPARISON.md'); $bad = @($added | Where-Object { $allowed -notcontains $_ }); if ($bad.Count -gt 0) { Write-Output 'out-of-scope change:'; $bad; exit 1 }; $other = @($added | Where-Object { $_ -match '09-35|09-(0[1-9]|[1-3][0-9]|4[01])-' }); if ($other.Count -gt 0) { Write-Output 'other plan touched:'; $other; exit 1 }</automated>
|
||||
</verify>
|
||||
<done>新源码结构闸门与 hotkeySettingsGate 通过;eslint 六个文件通过(唯一硬性代码规范门禁);闸门断言 EnemyOne/adjustGrid/临界表/book/toolbox/equipbox 的关键结构存在;范围差集只含 files_modified 允许的路径,且 09-35 与其他计划未被触碰;未运行 vue-tsc、未做类型诊断对比、未把类型错误/循环引用/GameUI.get/locator 类型当失败条件;未运行已知红闸门、未做浏览器 UAT、未提交代码。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 键盘/鼠标事件 → UI 组件 | 用户可连续按键、在空列表按确认、把已装备拖到无效槽位 |
|
||||
| 数据端怪物/道具描述 → 文本渲染 | 描述来自内容数据,可能包含 HTML 或换行控制符 |
|
||||
| toolbox → equipbox / equipbox → toolbox | 跨 UI 传递 heroState 与 attributes,参数缺失会打开不完整界面 |
|
||||
| 源码闸门 → 源码文本 | 测试读取 ?raw 文本,注释文字可能造成否定断言误判 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-09-42-01 | Tampering | book / toolbox 键盘索引 | low | mitigate | 所有上/下/翻五/左右切换都把索引夹到 0..length-1;空列表时 confirm 直接返回 |
|
||||
| T-09-42-02 | Tampering | bookDetail 临界表 | low | mitigate | 表格固定 8 行,缺失条目回退 0;criticlas 随 current 重算,避免旧数据残留 |
|
||||
| T-09-42-03 | Information Disclosure | 特殊属性/道具描述 | medium | mitigate | 继续使用 TextContent 纯文本,不启用 HTML 注入;闸门断言不出现注入写法 |
|
||||
| T-09-42-04 | Denial of Service | book 自动滚动 | low | mitigate | scrollTo 目标夹到非负,watch(selected) 只在 nextTick 触发一次,不进入滚动事件回环 |
|
||||
| T-09-42-05 | Elevation of Privilege | toolbox/equipbox 交叉导航 | low | mitigate | 打开目标 UI 统一通过 props.controller.open 打开 ToolBoxUI / EquipBoxUI;必须同时传 heroState 与具体 attributes;ToolBoxProps 的 attributes 默认 [],toolbox → equipbox 传 `props.attributes ?? []` |
|
||||
| T-09-42-06 | Tampering | equipbox 已装备拖拽 | low | mitigate | 仅当目标槽有效、不同于来源槽且 canEquipTo 非 CannotEquip 时 equip;否则只卸下 |
|
||||
| T-09-42-SC | Tampering | npm/pip/cargo 安装 | high | mitigate | 本计划不安装任何依赖、不修改 lockfile/workspace;无 package-legitimacy 检查点需要 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
1. Task 0 已获得用户恰好「可以执行」的确认,且纳入/排除 ID、命名、矛盾清单、验证方式以及「类型正确性不在验收范围」的口径均已确认。
|
||||
2. `node node_modules/vitest/vitest.mjs run src/client/ui/__test__/legacyUiParityGate.test.ts` 通过。
|
||||
3. `node node_modules/vitest/vitest.mjs run src/client/ui/__test__/hotkeySettingsGate.test.ts` 通过。
|
||||
4. `node node_modules/eslint/bin/eslint.js` 对 5 个 UI 文件与 1 个新测试文件通过(本计划唯一硬性代码规范门禁)。
|
||||
5. `git status --porcelain -uall` 与 Task 1 前基线的差集只含 files_modified 中允许的路径,且不出现 09-35 或其他计划的 PLAN/SUMMARY 变化;无代码提交、无 stash/checkout/reset。SUMMARY 记录该差集的已知局限。
|
||||
6. 不运行 vue-tsc、不做类型诊断对比;类型错误、循环引用、GameUI.get、locator 类型均不是失败条件,代码由用户自行审查与修改。
|
||||
7. 不运行 uiContainerGate / keyAdaptationGate;不做浏览器 UAT;不宣称 WebGL 或浏览器端视觉验证。
|
||||
8. 09-42-SUMMARY.md 如实记录:实际改动、真实命令与结果、未实现项(尤其 quickEquip)、待批准命名、09-42 为新增计划且不存在旧摘要被覆盖。
|
||||
9. 09-LEGACY-UI-COMPARISON.md 只追加 09-42 回填段,记录用户覆盖的 ID 与最终状态,不改写原表与「只读对照」声明。
|
||||
10. 源码闸门中的 adjustGrid 来自 `@user/client-base/layout/layout` 或闸门内独立公式,不来自 barrel;book 的行仍通过现有 UI 栈打开详情,EnemyOne 选中描边断言含 `fill={false}`。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- EnemyOne 成为 book 行与 bookDetail 顶部的唯一实现,使用 adjustGrid(180, 72, 56, 20, 4, 4) 生成 3×3、普通 icon、无可视动画;选中描边 `<g-rectr fill={false}>` 同时设置 stroke 与 strokeStyle,无背景填充;伤害颜色以英雄当前 HP 为阈值。
|
||||
- book 具备空状态、选中/悬停、键盘上下/翻五/确认/退出、自动滚动、伤害颜色、选中索引,并保留现有详情入口(显式 locator: undefined)。
|
||||
- bookDetail 为单页三区结构,顶部仅在 current 非空时渲染 EnemyOne,临界表由 adjustGrid(548, 104, 260, 12, 20, 1) 生成 8 行两列,criticlas 一致且由 `watch(current, value => getCriticals(value), { immediate: true })` 重算、先判空再赋值,0 回退与模式切换保留。
|
||||
- toolbox 具备 legacy 双列表 + 详情结构、使用流程与失败提示、装备栏交叉导航(attributes 默认 [])、@ui/toolbox 再次按下关闭、初始为可消耗列(legacy mode='tools')/index=0、选中与高亮由 mode+index 派生且无残留 selected 变量、键盘导航、详情图标/类型/提示,描述保持纯文本。
|
||||
- equipbox 仅新增 EQ-02 / EQ-18 / EQ-27,现有新布局与视觉不变;quickEquip 按 Task 0 确认结果处理;endPress 失效注释按批准结果改写或保留并记录。
|
||||
- 所有共享排除项不出现;无接线/装配/注册/菜单入口;无代码提交。
|
||||
- 新 09-42 源码结构闸门、hotkeySettingsGate、eslint 通过;范围差集只含 files_modified 且 09-35/其他计划未被触碰;不运行 vue-tsc、不做类型诊断对比;不运行已知红闸门、不做浏览器 UAT。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/09-minimum-browser-runnable/09-42-SUMMARY.md` when done.
|
||||
</output>
|
||||
@@ -0,0 +1,124 @@
|
||||
# Phase 09 Plan 42: 旧版 UI 对齐(book / bookDetail / toolbox / equipbox)+ EnemyOne 摘要
|
||||
|
||||
## 结论
|
||||
|
||||
Task 1–6 已按计划执行;Task 0 已由用户恰好回复「可以执行」确认。所有变更保留在
|
||||
工作树中,**未提交任何代码**,未执行 git add / commit / stash / checkout / reset。
|
||||
|
||||
## 任务与文件
|
||||
|
||||
| 任务 | 结果 | 文件 |
|
||||
|---|---|---|
|
||||
| 1 | 新建可复用 EnemyOne 与三个辅助函数 | `src/client/ui/enemyOne.tsx` |
|
||||
| 2 | book 改用 EnemyOne,补齐空状态/选中/键盘/滚动/伤害颜色 | `src/client/ui/book.tsx` |
|
||||
| 3 | bookDetail 改为单页三区,修复临界数据与重算 | `src/client/ui/bookDetail.tsx` |
|
||||
| 4 | toolbox 双列表详情、使用流程、交叉导航与键盘 | `src/client/ui/toolbox.tsx` |
|
||||
| 5 | equipbox 仅补 EQ-02 / EQ-18 / EQ-27 | `src/client/ui/equipbox.tsx` |
|
||||
| 6 | 新建源码结构闸门并完成最终验证 | `src/client/ui/__test__/legacyUiParityGate.test.ts` |
|
||||
|
||||
规划产物:09-42 回填段已追加到
|
||||
`.planning/phases/09-minimum-browser-runnable/09-LEGACY-UI-COMPARISON.md`,
|
||||
未改写原只读对照表。
|
||||
|
||||
## 实际改动
|
||||
|
||||
### enemyOne.tsx(新增)
|
||||
|
||||
- 导出 `EnemyOne`、`getEnemyDamageData`、`getDamageColor`、`getSpecialSummary`。
|
||||
- 3×3 属性网格固定使用 `adjustGrid(180, 72, 56, 20, 4, 4)`,并断言 `locs.length === 9`。
|
||||
- 左侧普通 `icon` + 名称 + 特殊摘要,右侧九项属性;第九项为「1 防」。
|
||||
- 选中描边使用 `<g-rectr fill={false} stroke strokeStyle=...>`,无背景填充。
|
||||
- `getEnemyDamageData` 迁移 book 原有 1 防计算,空 `system` / 空 `enemy` 回退
|
||||
「无法战斗 / 0 / 0 / 0」;伤害颜色以英雄当前 HP 为阈值。
|
||||
|
||||
### book.tsx
|
||||
|
||||
- 每行改为 `<EnemyOne>`,删除手写九项文本与 `getDamageData` / `specialText` /
|
||||
`statX` / `STAT_LEFT` / `statFont` / `format` / `isNil`。
|
||||
- 增加空列表「本层无怪物」、`selected` / `scrollRef`、悬停更新选中、上下/翻五/
|
||||
确认/退出六个命令、`nextTick` 后 `scrollTo` 自动滚动。
|
||||
- `openDetail` 传 `{ enemy, locator: undefined }`。
|
||||
|
||||
### bookDetail.tsx
|
||||
|
||||
- 单页三区:顶部 EnemyOne(`current` 非空才渲染,否则「无怪物数据」)、中部特殊
|
||||
属性 Scroll、底部 8 行临界表。
|
||||
- 临界表使用 `adjustGrid(548, 104, 260, 12, 20, 1)`,cols=2 / rows=8 /
|
||||
locs.length=16;`criticalFont` 为 size 10,8 行完整落在 104px 内。
|
||||
- 修复 `criticlas` / 未定义 `critical` 不一致;`getCriticals` 先用 `target` 判空,
|
||||
再重算;改为 `watch(current, value => getCriticals(value), { immediate: true })`。
|
||||
- 删除 `nameFont`、`statX` 与顶部标题/5 项属性块中的 `const enemy`;保留 `format`
|
||||
供临界表复用;`locator` 已加入 props 数组。
|
||||
|
||||
### toolbox.tsx
|
||||
|
||||
- 选择模型改为 `mode: 'permanent' | 'consumable'` + `index`,初始为
|
||||
`consumable` / `0`(对应 legacy `mode='tools'`);删除 `selected = ref(-1)`。
|
||||
- 行高亮与 `selectedItem` 均由 `mode` + `index` 派生;再次点击当前项触发使用。
|
||||
- 详情区补图标、名称、类型、使用提示与纯文本描述;失败提示
|
||||
「当前无法使用该道具!」约 2 秒后清除。
|
||||
- 使用流程:`keep()` → `close()` → `nextTick()` → `useItem()` → `safelyUnload()`;
|
||||
使用前以 `item.raw.effect.canUse` 预检。
|
||||
- 右上「装备栏 →」通过 `props.controller.open(EquipBoxUI, ...)` 打开装备栏;
|
||||
`ToolBoxProps` 新增可选 `attributes`(默认 `[]`),本地声明结构等价的
|
||||
`IHeroAttrItem`(不导出)。
|
||||
- 键盘左右/上下/确认/退出,并用 `@ui/toolbox` 实现再次按下关闭。
|
||||
|
||||
### equipbox.tsx
|
||||
|
||||
- 保持现有新布局与视觉不变。
|
||||
- EQ-02:右上「道具栏 →」通过 `props.controller.open(ToolBoxUI, ...)` 打开道具栏。
|
||||
- EQ-18:已装备拖到有效且可装备的另一槽时 `equip(uid, target, true)`,否则
|
||||
`unequip(fromSlot)`;使用已捕获的 `target`,不读取重置后的 `hoverSlot`。
|
||||
- EQ-27:`@common/exit` 与 `@ui/command` 关闭。
|
||||
|
||||
### legacyUiParityGate.test.ts(新增)
|
||||
|
||||
- 使用 `?raw` 断言五个 UI 的关键结构、命令、辅助函数与 adjustGrid 数学结果;
|
||||
不依赖 DOM、不使用 `node:fs`、不启动浏览器。
|
||||
- 每个 `it` 前均有一行说明注释。
|
||||
|
||||
## 验证结果(实际运行)
|
||||
|
||||
| 门禁 | 命令 | 结果 |
|
||||
|---|---|---|
|
||||
| eslint | `node node_modules/eslint/bin/eslint.js` 六个文件 | **通过,exit 0** |
|
||||
| 新源码结构闸门 | `node node_modules/vitest/vitest.mjs run src/client/ui/__test__/legacyUiParityGate.test.ts` | **8 passed** |
|
||||
| 现有热键闸门 | `node node_modules/vitest/vitest.mjs run src/client/ui/__test__/hotkeySettingsGate.test.ts` | **12 passed** |
|
||||
| 范围差集 | 基线 vs `git status --porcelain -uall` | **通过;新增路径仅 enemyOne.tsx、legacyUiParityGate.test.ts、本 SUMMARY** |
|
||||
|
||||
范围差集确认新增路径均属于计划 `files_modified`,且未出现 09-35 或其他计划的
|
||||
`*-PLAN.md` / `*-SUMMARY.md`。**已知局限**:差集只能发现新增/删除路径,无法识别
|
||||
「基线时已脏、本次又修改了内容」的文件;本次实际写入严格限制在 `files_modified`
|
||||
内,已通过写入记录核对。
|
||||
|
||||
## 接受的范围外 / 功能 caveat
|
||||
|
||||
- **类型正确性不在验收范围**:未运行 vue-tsc,未做类型诊断对比;类型错误、
|
||||
`as` 断言、icon 的 `AllNumbers` 类型、locator 的 undefined/null 兼容性均不作为
|
||||
阻塞项,代码由用户自行审查与修改。
|
||||
- `toolbox.tsx` 与 `equipbox.tsx` 采用静态双向导入以便 `controller.open` 同步打开
|
||||
对方 UI,因此存在循环引用;按用户本次执行要求,循环引用不属于阻塞项,未继续
|
||||
迭代或改写导入方式。
|
||||
- `equipbox` 的 `quickEquip`(套装保存/读取)因新数据层没有对应接口而**未实现**,
|
||||
未伪造接口。
|
||||
- 从 book 打开详情时传入的是基础 enemy,模式切换按钮仍为 no-op;符合计划约定。
|
||||
- `GameUI.get`、locator 细节、类型正确性均由用户后续自行处理。
|
||||
|
||||
## 偏差
|
||||
|
||||
1. **Task 1 基线在首次写入后才落盘**:执行者先完成了源码写入,未能按计划在
|
||||
Task 1 第一步保存 `git status --porcelain -uall`。为完成范围核对,以执行开始时
|
||||
捕获的 `git status --short` 为参照,从当前 `-uall` 状态移除本次新增路径重建
|
||||
基线,仅用于路径差集;不改变实际写入范围,且已在「已知局限」中记录。
|
||||
2. 无其他功能或范围偏差。
|
||||
3. 既有注释/jsDoc 除 Task 0 批准的重写(equipbox `endPress` 行注释)与删除
|
||||
(book / bookDetail / toolbox 清单)外保持不变;因布局替换被移除的注释仅限
|
||||
清单范围,其余原有注释均已保留。
|
||||
|
||||
## 未做
|
||||
|
||||
- 未做浏览器 UAT,未宣称 WebGL / 浏览器视觉验证。
|
||||
- 未运行已知红色的 `uiContainerGate` / `keyAdaptationGate`。
|
||||
- 未运行 vue-tsc,未新增类型门禁。
|
||||
- 未提交任何代码,未执行任何 git 写命令。
|
||||
@@ -0,0 +1,196 @@
|
||||
# Legacy UI 功能对照:book / bookDetail / equipbox / toolbox
|
||||
|
||||
**用途**:逐项对照 `legacy-ui` 与当前实现,快速确认哪些没做、哪些已确认不做。
|
||||
**状态**:只读对照,不代表已确认的问题;功能取舍以用户回复为准。
|
||||
**对照范围**:`book` 主列表、`bookDetail`、`equipbox`、`toolbox`。`hotkey` / `settings` 暂未列入。
|
||||
**legacy 基准**:`book.vue` 取 `7428f41^`(该文件在 `f53f611` 前已被删除);其余取 `f53f611^` 的最后版本。
|
||||
|
||||
## 状态图例
|
||||
|
||||
| 状态 | 含义 |
|
||||
|---|---|
|
||||
| `无` | 当前没有该功能,需要实现 |
|
||||
| `部分` | 原部分完成;按用户最新决定,全部归入不做 |
|
||||
| `有` | 当前已有,维持现状 |
|
||||
| `有(纯文本)` | 当前已有,且按决定维持纯文本,不实现 legacy HTML |
|
||||
| `不做` | 已确认不实现 |
|
||||
| `有(bug)` | 当前存在但已知有缺陷;不是精确 `无`,本批不执行 |
|
||||
|
||||
## 快速清单
|
||||
|
||||
### 未做(`无`)
|
||||
|
||||
- bookDetail:`BD-08`、`BD-10`、`BD-12`、`BD-13`、`BD-15`、`BD-16`、`BD-21`
|
||||
- equipbox:`EQ-02`、`EQ-18`、`EQ-27`
|
||||
- toolbox:`TB-02`、`TB-12`、`TB-14`、`TB-15`、`TB-16`、`TB-17`、`TB-18`、`TB-20`、`TB-21`
|
||||
- book:`BL-03`、`BL-07`、`BL-11`、`BL-12`、`BL-13`、`BL-14`、`BL-17`、`BL-24`
|
||||
|
||||
### 已确认不做(`不做`,含原 `部分`)
|
||||
|
||||
- bookDetail:`BD-03`、`BD-04`、`BD-05`、`BD-06`、`BD-07`、`BD-11`、`BD-14`、`BD-22`、`BD-23`、`BD-24`、`BD-28`
|
||||
- equipbox:`EQ-03`、`EQ-04`、`EQ-05`、`EQ-06`、`EQ-07`、`EQ-17`、`EQ-19`、`EQ-20`、`EQ-21`、`EQ-22`、`EQ-23`、`EQ-25`、`EQ-28`、`EQ-30`
|
||||
- toolbox:`TB-03`、`TB-04`、`TB-13`、`TB-23`、`TB-24`
|
||||
- book:`BL-01`、`BL-05`、`BL-08`、`BL-16`、`BL-20`、`BL-22`、`BL-23`
|
||||
|
||||
**只执行状态精确为 `无` 的条目。** `部分` 已全部改为 `不做`;`有` / `有(纯文本)` / `有(bug)` 均不执行。
|
||||
|
||||
## A. bookDetail
|
||||
|
||||
legacy 对应:`ui/bookDetail.vue` + `components/enemyOne.vue` + `panel/enemySpecial.vue` + `panel/enemyCritical.vue`
|
||||
|
||||
| ID | 功能 | 状态 | legacy | 当前 bookDetail |
|
||||
|---|---|---|---|---|
|
||||
| BD-01 | 页面返回按钮 | `有` | 左上角「← 返回」 | 右下角「返回上一页」 |
|
||||
| BD-02 | 键盘关闭 | `有` | `exit` / `confirm` 关闭 | 只有 `@common/exit` |
|
||||
| BD-03 | 点击空白/拖动关闭 | `不做` | 特殊面板下点击或下滑关闭 | 无 |
|
||||
| BD-04 | 打开/关闭动画 | `不做` | 从 `detailInfo.pos` 过渡到顶部 | 无 |
|
||||
| BD-05 | 面板切换 | `不做` | 特殊属性 / 临界信息两面板 | 无,固定单页 |
|
||||
| BD-06 | 默认面板 | `不做` | `fromBook` / `defaultPanel` | 无 |
|
||||
| BD-07 | 主信息区 `EnemyOne` | `不做` | 图标 + 名称 + 特殊摘要;右侧 3×3 | 仅标题 + 5 项属性 |
|
||||
| BD-08 | 怪物图标 | `无` | `BoxAnimate` 图标 | 无(要普通 icon,不要动画) |
|
||||
| BD-09 | 怪物名称 | `有` | 主信息区左侧 | 顶部标题 |
|
||||
| BD-10 | 特殊属性摘要 | `无` | 前 2 条 + `...`,无属性显示「无属性」 | 无 |
|
||||
| BD-11 | 属性网格 3×3 | `不做` | 9 项 3×3 | 5 项单行 |
|
||||
| BD-12 | 伤害显示 | `无` | 有,颜色按 `damageColor` | 无 |
|
||||
| BD-13 | 临界显示 | `无` | 有 | 无 |
|
||||
| BD-14 | 减伤显示 | `不做` | 有 | 底部有单独文本 |
|
||||
| BD-15 | 防御减伤 | `无` | `{ratio}防` | 无(book 列表用「1 防」) |
|
||||
| BD-16 | 3×3 网格布局 | `无` | flex 3×3 | 手写 `statX`,未用 `adjustGrid` |
|
||||
| BD-17 | 特殊属性列表 | `有` | `Scroll` | `Scroll` |
|
||||
| BD-18 | 特殊属性标题 | `有` | 名称 + 颜色 + 冒号 | 名称 + 颜色 + 冒号 |
|
||||
| BD-19 | 特殊属性描述 | `有(纯文本)` | HTML `innerHTML` | `TextContent` 纯文本,维持 |
|
||||
| BD-20 | 特殊属性高度 | `有` | 固定行高 | 实测高度 + `cumsum` |
|
||||
| BD-21 | 临界表 | `无` | 标题 + 表头「加攻/减伤」+ 8 行 | 无 |
|
||||
| BD-22 | 临界数据 | `不做` | `calCritical(8/4)` | 算了 `criticlas` 8 条,但 JSX 用了 `critical.value` |
|
||||
| BD-23 | 临界图表/滑条 | `不做` | 两图 + 两滑条 + 当前值 | 无 |
|
||||
| BD-24 | 标记界面 | `不做` | `EnemyTarget`(已注释) | 无 |
|
||||
| BD-25 | 基础/计算模式切换 | `有` | 无 | 左下角切换 |
|
||||
| BD-26 | 临界 0 回退 | `有` | 无 | 有 |
|
||||
| BD-27 | 当前缺陷 | `有(bug)` | — | `criticlas` / `critical` 不一致;模式切换不重算 |
|
||||
| BD-28 | 移动端布局 | `不做` | 媒体查询 | 无 |
|
||||
|
||||
## B. equipbox
|
||||
|
||||
legacy 对应:`ui/equipbox.vue` + `tools/equipbox.tsx`
|
||||
|
||||
| ID | 功能 | 状态 | legacy | 当前 equipbox |
|
||||
|---|---|---|---|---|
|
||||
| EQ-01 | 顶部返回 | `有` | 左上「← 返回游戏」 | 右下「退出」 |
|
||||
| EQ-02 | 跳到道具栏 | `无` | 右上「道具栏 →」 | 无 |
|
||||
| EQ-03 | 整体布局 | `不做` | 三列:装备 25% / 状态+已装备 50% / 描述 25% | 左侧状态、右侧描述+槽位+拥有装备 |
|
||||
| EQ-04 | 属性筛选 | `不做` | 所有/攻击/防御/生命上限/魔法 | 无 |
|
||||
| EQ-05 | 数值/百分比筛选 | `不做` | 数值 / 百分比 | 无 |
|
||||
| EQ-06 | 排序方向 | `不做` | 升序/降序切换 | 无 |
|
||||
| EQ-07 | 装备列表 | `不做` | Scroll、图标、名称、数量 | 无独立列表,只有拥有装备 |
|
||||
| EQ-08 | 列表选中高亮 | `有` | 有 | 拥有装备行有高亮 |
|
||||
| EQ-09 | 单击选中 | `有` | 有 | 有 |
|
||||
| EQ-10 | 再次点击装备 | `有` | 二次点击穿装备 | 有 |
|
||||
| EQ-11 | 列表拖拽到槽位 | `有` | 有 | 有 |
|
||||
| EQ-12 | 拖拽阈值/浮层 | `有` | 10px,固定 drag 图标 | 8px,拖拽浮层 |
|
||||
| EQ-13 | 已装备槽列表 | `有` | Scroll,图标+槽名 | Scroll,图标+槽名 |
|
||||
| EQ-14 | 选中已装备 | `有` | 有 | 有 |
|
||||
| EQ-15 | 点击已装备卸下 | `有` | 再次点击 | 按下/抬起未拖动即卸下 |
|
||||
| EQ-16 | 拖拽已装备卸下 | `有` | 有 | 有 |
|
||||
| EQ-17 | 槽位类型兼容 | `不做` | `canDragin` + 失败提示 | 有判断,未看到失败提示 |
|
||||
| EQ-18 | 已装备拖到另一槽 | `无` | 支持 | 当前拖拽已装备仅卸下 |
|
||||
| EQ-19 | 勇士图标 | `不做` | 桌面端显示 | 无 |
|
||||
| EQ-20 | 勇士属性 | `不做` | `getNowStatus` 列表 | 左侧 `attributes` 列表 + 增减值 |
|
||||
| EQ-21 | 选中装备图标/名称 | `不做` | 描述区顶部图标+名称 | 只有名称 |
|
||||
| EQ-22 | 装备孔类型 | `不做` | 显示 `equip.type` | 显示 `equip.slots` 列表 |
|
||||
| EQ-23 | 增减属性列表 | `不做` | 独立「增减属性」Scroll | 属性区行内增减值 |
|
||||
| EQ-24 | 装备描述 | `有(纯文本)` | 支持 `!!html` | `TextContent` 纯文本 |
|
||||
| EQ-25 | 打字动画 | `不做` | 有 | 无 |
|
||||
| EQ-26 | 拖拽浮层 | `有` | 有 | 有 |
|
||||
| EQ-27 | 键盘操作 | `无` | `exit` / `equipbox` / `quickEquip` | 无 |
|
||||
| EQ-28 | 移动端布局 | `不做` | 媒体查询、列反转 | 无 |
|
||||
| EQ-29 | 空状态 | `有` | 「没有选择装备」 | 「未选择装备」 |
|
||||
| EQ-30 | 拥有装备排序 | `不做` | 按属性/筛选排序 | 按图块数字升序 |
|
||||
| EQ-31 | 状态区属性来源 | `有` | 勇士 `nowStatus` | 外部 `attributes` |
|
||||
|
||||
## C. toolbox
|
||||
|
||||
legacy 对应:`ui/toolbox.vue` + `tools/toolbox.tsx`
|
||||
|
||||
| ID | 功能 | 状态 | legacy | 当前 toolbox |
|
||||
|---|---|---|---|---|
|
||||
| TB-01 | 顶部返回 | `有` | 左上「← 返回游戏」 | 右下「退出」 |
|
||||
| TB-02 | 跳到装备栏 | `无` | 右上「装备栏 →」 | 无 |
|
||||
| TB-03 | 列表布局 | `不做` | 两列列表 + 右侧详情 | 三列:永久/消耗/详情 |
|
||||
| TB-04 | 移动端列表 | `不做` | tab 切换消耗/永久 | 无 |
|
||||
| TB-05 | 道具分类 | `有` | `tools` / `constants` | `consumable` true/false |
|
||||
| TB-06 | 隐藏道具 | `有` | 无明确 hide 过滤 | 过滤 `hideInToolbox` |
|
||||
| TB-07 | 排序 | `有` | 原顺序 | 数量降序,同数量按 num 升序 |
|
||||
| TB-08 | 行内容 | `有` | 图标+名称+数量 | 图标+名称+数量 |
|
||||
| TB-09 | 选中高亮 | `有` | 有 | 有 |
|
||||
| TB-10 | 单击选中 | `有` | 有 | 有 |
|
||||
| TB-11 | 再次点击使用 | `有` | 有 | 有 |
|
||||
| TB-12 | 使用失败提示 | `无` | 「当前无法使用该道具!」 | 无 |
|
||||
| TB-13 | 使用流程 | `不做` | `holdOn` → 关闭 → `tryUseItem` → end | 直接 `useItem` |
|
||||
| TB-14 | 键盘:左右切列 | `无` | 有 | 无 |
|
||||
| TB-15 | 键盘:上下移动 | `无` | 有 | 无 |
|
||||
| TB-16 | 键盘:确认使用 | `无` | 有 | 无 |
|
||||
| TB-17 | 键盘:关闭/切换道具栏 | `无` | 有 | 无 |
|
||||
| TB-18 | 详情图标 | `无` | 有 | 无 |
|
||||
| TB-19 | 详情名称 | `有` | 有 | 有 |
|
||||
| TB-20 | 详情类型 | `无` | 永久/消耗 | 无 |
|
||||
| TB-21 | 使用提示 | `无` | 「点击该物品以使用」 | 无 |
|
||||
| TB-22 | 描述 HTML | `有(纯文本)` | 支持 `!!html` | 纯文本,维持 |
|
||||
| TB-23 | 打字动画 | `不做` | 有 | 无 |
|
||||
| TB-24 | 移动端布局 | `不做` | 有 | 无 |
|
||||
| TB-25 | 空状态 | `有` | 「没有选择道具/没有道具」 | 「未选择道具」 |
|
||||
|
||||
## D. book 主列表
|
||||
|
||||
legacy 对应:`ui/book.vue`(`7428f41^`)+ `components/enemyOne.vue`
|
||||
|
||||
| ID | 功能 | 状态 | legacy | 当前 book |
|
||||
|---|---|---|---|---|
|
||||
| BL-01 | 页面范围 | `不做` | 宽 80%、高 100% | 整屏 `FULL_LOC` |
|
||||
| BL-02 | 返回按钮 | `有` | 左上「← 返回游戏」 | 右下「退出」 |
|
||||
| BL-03 | 空列表 | `无` | 「本层无怪物」 | 无空状态提示 |
|
||||
| BL-04 | 滚动列表 | `有` | `Scroll` 拖动滚动 | `Scroll noscroll` |
|
||||
| BL-05 | 行结构 | `不做` | 复用 `EnemyOne` + 分隔线 | 平铺 13 个子元素 |
|
||||
| BL-06 | 行高 | `有` | `ui.bookScale` 换算 | 固定 72 |
|
||||
| BL-07 | 选中/悬停 | `无` | 当前行高亮、悬停更新 | 无 |
|
||||
| BL-08 | 点击行 | `不做` | 选中+打开详情;拖动忽略 | 点击直接打开 |
|
||||
| BL-09 | 打开详情 | `有` | 隐藏 book,显示 overlay | 打开 `BookDetailUI` |
|
||||
| BL-10 | 关闭详情 | `有` | 恢复 book | UI 栈返回 |
|
||||
| BL-11 | 键盘上下 | `无` | `@book_up` / `@book_down` | 无 |
|
||||
| BL-12 | 键盘翻五条 | `无` | `@book_pageUp` / `@book_pageDown` | 无 |
|
||||
| BL-13 | 键盘确认/退出 | `无` | `confirm` / `exit` | 无 |
|
||||
| BL-14 | 选中项自动滚动 | `无` | `checkScroll` | 无 |
|
||||
| BL-15 | 数据来源 | `有` | `core.getCurrentEnemys` | 传入 `context`,按 code 去重 |
|
||||
| BL-16 | 行内容 | `不做` | 图标/名称/特殊摘要/9 属性 | 图标/名称/特殊属性/5+4 属性 |
|
||||
| BL-17 | 伤害颜色 | `无` | `damageColor` | 固定白色 |
|
||||
| BL-18 | 防御减伤 | `有` | `{ratio}防` | 「1 防」 |
|
||||
| BL-19 | 临界缺失 | `有` | 无 0 回退 | 显示 0 |
|
||||
| BL-20 | 详情入口参数 | `不做` | `detailInfo.pos` + `fromBook` | `enemy`/`view` |
|
||||
| BL-21 | 退出恢复 | `有` | `holdOn` + 恢复事件/浏览地图 | 新 UI 栈自然恢复,无需单独实现 |
|
||||
| BL-22 | 移动端 | `不做` | 有 | 无 |
|
||||
| BL-23 | 缩放设置 | `不做` | `ui.bookScale` | 无 |
|
||||
| BL-24 | 选中索引 | `无` | `selected` 索引 | 无 |
|
||||
|
||||
## 备注
|
||||
|
||||
- `hotkey` / `settings` 未列入本表;如需一并对照,另开一节。
|
||||
- 本文档只是功能台账;`状态` 以“当前主界面/组件是否具备该功能”为准。
|
||||
|
||||
## 09-42 执行回填(用户覆盖)
|
||||
|
||||
本节只记录 09-42 的执行结果,不改写上方原只读对照表。用户本次指令明确要求
|
||||
book / bookDetail / toolbox 按 legacy 视觉与功能尽量补齐,这是对
|
||||
09-CONTEXT.md D-20 的**显式覆盖**(user override);equipbox 仍保持现有新布局,
|
||||
不回退 legacy。
|
||||
|
||||
- book 已实现:`BL-03`、`BL-05`、`BL-07`、`BL-11`、`BL-12`、`BL-13`、`BL-14`、
|
||||
`BL-16`、`BL-17`、`BL-24`。
|
||||
- bookDetail 已实现:`BD-07`、`BD-08`、`BD-10`、`BD-11`、`BD-12`、`BD-13`、
|
||||
`BD-14`、`BD-15`、`BD-16`、`BD-21`、`BD-22`、`BD-27`。
|
||||
- toolbox 已实现:`TB-02`、`TB-03`、`TB-12`、`TB-13`、`TB-14`、`TB-15`、
|
||||
`TB-16`、`TB-17`、`TB-18`、`TB-20`、`TB-21`。
|
||||
- equipbox 已实现:`EQ-02`、`EQ-18`、`EQ-27`;`quickEquip` 因新数据层没有套装
|
||||
保存/读取接口而未实现。
|
||||
- 仍排除:移动端、临界图表/滑条、目标面板、HTML 描述注入、打字动画、
|
||||
equipbox 筛选/排序、book 缩放,以及所有接线/装配/注册/菜单入口。
|
||||
- 验证:eslint(六个文件)、09-42 源码结构闸门、现有 hotkeySettingsGate、基于
|
||||
重建基线的范围差集;未运行 vue-tsc,未做浏览器 UAT,未提交任何代码。
|
||||
Reference in new issue
Block a user