From 8951c54c8815b118a28db8cec6ffaf9ad3aeb47b Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Sat, 10 Oct 2026 22:23:01 +0800 Subject: [PATCH] fix(09-minimum-browser-runnable): revise 09-09 plan per checker feedback --- .../09-minimum-browser-runnable/09-09-PLAN.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.planning/phases/09-minimum-browser-runnable/09-09-PLAN.md b/.planning/phases/09-minimum-browser-runnable/09-09-PLAN.md index d1916db..f86b582 100644 --- a/.planning/phases/09-minimum-browser-runnable/09-09-PLAN.md +++ b/.planning/phases/09-minimum-browser-runnable/09-09-PLAN.md @@ -30,7 +30,7 @@ must_haves: provides: "D-38..D-41 的商店 UI:顶部 购买/售出 切换;左侧 商店列表(id 排序 / 库存右对齐 / 可滚动显滚动条);右侧 物品名称 / 描述(纵滚无滚动条)/ 购入量选择(`<< < {量} > >>`,按物品分别记忆,上限 库存/拥有,按钮上方 买价/卖价/存货/拥有/总价)/ 购买按钮;右下退出;入参 `IShopState` + `IHeroState` + `IShopSystem`。" key_links: - "`packages-user/client-modules/src/ui/shop.tsx` ↔ `IShopState`(`packages-user/data-base/src/shop/types.ts:14-59`)—— 商店数据来源(`iterateItems` `:34` / `getCount` `:29` / `enabled` `:16` / **`getPrice` `:48`**);**已从 `@user/data-base` 公共面导出**(`data-base/src/index.ts:5` `export * from './shop'` + `shop/index.ts:1`,GAP-S0 已闭合)" - - "`shop.tsx` ↔ `IShopState.iterateItems(): Iterable<[num: number, count: number]>`(`shop/types.ts:34`)—— 商店列表来源(购入与**售出共用**同一列表,D-39);`IShopState.getCount(token)`(`:29`)—— 商店库存(不存在返回 `-1`)" + - "`shop.tsx` ↔ `IShopState.iterateItems(): Iterable<[number, number]>`(`shop/types.ts:34`)—— 商店列表来源(购入与**售出共用**同一列表,D-39);`IShopState.getCount(token)`(`:29`)—— 商店库存(不存在返回 `-1`)" - "`shop.tsx` ↔ `IShopState.getPrice(item): [buy: number, sell: number]`(`shop/types.ts:48`)+ `IShopStateSave.prices: ReadonlyMap`(`:11`)—— **买价 / 卖价**(GAP-S1 已闭合;不使用 legacy `ItemShopChoice.money`/`.sell`)" - "`shop.tsx` ↔ `IShopSystem.buyItem(item, count)`(`packages-user/data-system/src/shop/types.ts:37`)/ `sellItem(item, count)`(`:44`)—— **购买 / 售出交易**(GAP-S2 已闭合);`IShopSystem.openShop(shop)`(`:25`)/ `closeShop()`(`:30`)—— 交易所针对的当前商店(OI-S6 已确认:UI 于 setup 调用 `shopSystem.openShop(props.shop)`);`IShopHeroAttr.money`(`:5`)—— **支付资源 = money**(GAP-S3 已闭合)" - "`shop.tsx` ↔ `props.state.itemStore.getData(token)` → `IItemRawData`(`name` / `text` / `category`,`data-common/src/store/types.ts:176-200`)—— 物品名称 / 描述" @@ -70,7 +70,7 @@ must_haves: --- -Phase 9 · 任务 3 · 第 9 个计划 —— **shop 商店界面**。 +Phase 9 · 任务 4 · 第 9 个计划 —— **shop 商店界面**。 **要解决的问题(D-38..D-41):** 把 legacy `packages/legacy-ui` 的商店界面(`src/ui/shop.vue`)移植到新 UI 接口上,并按用户已定形态实现:顶部「购买选项 | 售出选项」切换;左「商店列表」;右列自上而下 = 物品名称 / 物品描述 / 购入量选择(按钮上方显示 买价/卖价/存货/拥有/总价)/ 购买按钮;占满游戏画面;购入/售出切换、售出复用商店列表、id 排序、库存最右右对齐、列表滚动带滚动条;数量四按钮 `<< < {量} > >`(±10 / ±1),数量按物品分别记忆;退出右下。 @@ -218,7 +218,7 @@ Phase 9 · 任务 3 · 第 9 个计划 —— **shop 商店界面**。 纯新增文件;删除 / `git revert` 可回退。 - node -e "const fs=require('fs');const p='packages-user/client-modules/src/ui/shop.tsx';const bad=[];if(!fs.existsSync(p)){bad.push('shop.tsx missing')}else{const t=fs.readFileSync(p,'utf8').replace(/\r\n/g,'\n');const need=[['interface ShopProps','props interface'],['IShopState','shop state type'],['IShopSystem','shop system type'],['IHeroState','hero state type'],['shopSystem','shopSystem prop'],['shop','shop prop'],['heroState','heroState prop'],['IShopItemEntry','row type'],['.iterateItems(','enumerate shop items'],['.getCount(','stock lookup'],['.getPrice(','price lookup'],['itemCount(','owned count'],['itemStore','item store'],['getData(','item raw'],['.text','description'],['.name','name'],['0){console.error('SHOP LAYOUT GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK shop: layout + shop list (sorted, stock right-aligned, scrollable) + prices via getPrice present')" + node -e "const fs=require('fs');const p='packages-user/client-modules/src/ui/shop.tsx';const bad=[];if(!fs.existsSync(p)){bad.push('shop.tsx missing')}else{const t=fs.readFileSync(p,'utf8').replace(/\r\n/g,'\n');const need=[['interface ShopProps','props interface'],['IShopState','shop state type'],['IShopSystem','shop system type'],['IHeroState','hero state type'],['shopSystem','shopSystem prop'],['shop','shop prop'],['heroState','heroState prop'],['IShopItemEntry','row type'],['.iterateItems(','enumerate shop items'],['.getCount(','stock lookup'],['.getPrice(','price lookup'],['itemCount(','owned count'],['itemStore','item store'],['getData(','item raw'],['.text','description'],['.name','name'],['0){console.error('SHOP LAYOUT GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK shop: layout + shop list (sorted, stock right-aligned, scrollable) + prices via getPrice present')" non-zero exit, 或 stderr 含 "SHOP LAYOUT GATE FAILED"(文件缺失 / 关键符号缺失 / props 数组不含 `shop`/`heroState`/`shopSystem` / 引入 `import type` 或 `?.`) node -e "const fs=require('fs');const raw=fs.readFileSync('packages-user/client-modules/src/ui/shop.tsx','utf8');const bad=[];if(/(? non-zero exit, 或 stderr 含 "CRLF FAILED"(换行非 CRLF) @@ -261,7 +261,7 @@ Phase 9 · 任务 3 · 第 9 个计划 —— **shop 商店界面**。 纯新增文件内交互逻辑;`git revert` 可回退。 - node -e "const fs=require('fs');const p='packages-user/client-modules/src/ui/shop.tsx';const bad=[];if(!fs.existsSync(p)){bad.push('shop.tsx missing')}else{const t=fs.readFileSync(p,'utf8').replace(/\r\n/g,'\n');const need=[['购买','buy label'],['售出','sell label'],['买价','buy price label'],['卖价','sell price label'],['存货','stock label'],['拥有','owned label'],['总价','total label'],['.getPrice(','price lookup'],['.buyItem(','buy transaction'],['.sellItem(','sell transaction'],['count','quantity state'],['onClick','click handlers'],['controller.close(','exit']];for(const [s,label] of need){if(t.indexOf(s)<0)bad.push('missing '+label+': '+s)}if(/import type/.test(t))bad.push('has import type');if(/\?\.[a-zA-Z]/.test(t))bad.push('has optional chaining');if(/as\s+\w+\s+as\s+/.test(t))bad.push('chained as');}if(bad.length>0){console.error('SHOP ACTION GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK shop: mode toggle + quantity + price info + buy/sell via IShopSystem + exit present')" + node -e "const fs=require('fs');const p='packages-user/client-modules/src/ui/shop.tsx';const bad=[];if(!fs.existsSync(p)){bad.push('shop.tsx missing')}else{const t=fs.readFileSync(p,'utf8').replace(/\r\n/g,'\n');const need=[['购买','buy label'],['售出','sell label'],['买价','buy price label'],['卖价','sell price label'],['存货','stock label'],['拥有','owned label'],['总价','total label'],['.getPrice(','price lookup'],['.buyItem(','buy transaction'],['.sellItem(','sell transaction'],['.openShop(','open current shop'],['count','quantity state'],['onClick','click handlers'],['controller.close(','exit']];for(const [s,label] of need){if(t.indexOf(s)<0)bad.push('missing '+label+': '+s)}if(/import type/.test(t))bad.push('has import type');if(/\?\.[a-zA-Z]/.test(t))bad.push('has optional chaining');if(/import\s*\{[^}]*\bclient\b/.test(t))bad.push('client singleton import');if(/from\s*['\"][^'\"]*\/core['\"]/.test(t))bad.push('core singleton import');if(/as\s+\w+\s+as\s+/.test(t))bad.push('chained as');}if(bad.length>0){console.error('SHOP ACTION GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK shop: mode toggle + quantity + price info + buy/sell via IShopSystem + exit present')" non-zero exit, 或 stderr 含 "SHOP ACTION GATE FAILED"(数量 / 价格标签 / 交易或交互符号缺失 / 引入 `import type` / `?.` / 连续 `as`) node -e "const fs=require('fs');const raw=fs.readFileSync('packages-user/client-modules/src/ui/shop.tsx','utf8');const bad=[];if(/(? non-zero exit, 或 stderr 含 "CRLF FAILED"(换行非 CRLF) @@ -272,8 +272,9 @@ Phase 9 · 任务 3 · 第 9 个计划 —— **shop 商店界面**。 - 四按钮 `<< < {量} > >>`:步进 ±10 / ±1,`clamp` 至 `[0, 上限]`;上限 = 购入 库存 / 售出 拥有;数量按物品分别记忆 - 信息区在按钮上方显示 买价/卖价/存货/拥有/总价(买价/卖价 经 `getPrice`);购买按钮交易当前选中物品数量;数量 0 不执行 - 交易:购入 `props.shopSystem.buyItem(num, count)`、售出 `props.shopSystem.sellItem(num, count)`;**UI 未自行组装金钱 / 物品转移,未 invent 任何新接口** + - setup 时经 `props.shopSystem.openShop(props.shop)` 绑定当前商店(OI-S6 已确认);交易对象 = 当前界面商店(threat T-09-09-09) - 右下退出调用 `controller.close(props.instance)` - - 无 `import type`、无 `?.`、无连续 `as`、CRLF、无未使用 import;范围守卫无越界改动 + - 无 `client` 单例、无 `import type`、无 `?.`、无连续 `as`、CRLF、无未使用 import;范围守卫无越界改动 数量选择 + 信息区 + 购买/售出(经 `IShopSystem`)+ 退出完成;最终静态门禁 + CRLF + 范围守卫全绿 @@ -330,13 +331,13 @@ Phase 9 · 任务 3 · 第 9 个计划 —— **shop 商店界面**。 |---|---|---| | `packages-user/client-modules/src/ui/shop.tsx`(新增) | 组件 / UI | 商店界面:`Shop` / `ShopUI` / `ShopProps` / `IShopItemEntry`(命名已批准) | -**新增导出命名(已获用户批准):** 文件 `shop.tsx`;组件 `Shop`;`GameUI` 名 `'shop'`;props 接口 `ShopProps`;props 数组 `['controller','instance','state','shop','heroState','shopSystem']`(新 prop `shopSystem`);行类型 `IShopItemEntry`;导出 `ShopUI`。**不向 `src/ui/index.ts` 添加导出**(保持与同 wave 计划互不冲突)。 +**新增导出命名(已获用户批准):** 文件 `shop.tsx`;组件 `Shop`;`GameUI` 名 `'shop'`;props 接口 `ShopProps`;props 数组 `['controller','instance','state','shop','heroState','shopSystem']`(新 prop `shopSystem`);行类型 `IShopItemEntry`;导出 `ShopUI`。**不向 `src/ui/index.ts` 添加导出**(保持与同 wave 计划互不冲突;符合 D-69 桶边界:barrel 仅允许同目录 `./` 导出,本计划不新增任何 barrel 导出)。 ## Source Coverage Audit | 来源 | 项 | 处置 | |------|----|------| -| GOAL | Phase 9(ROADMAP)「完成渲染端框架,产出可在浏览器运行并验证的最小版本」 | COVERED(增量·任务 3 子切片:shop 移植) | +| GOAL | Phase 9(ROADMAP)「完成渲染端框架,产出可在浏览器运行并验证的最小版本」 | COVERED(增量·任务 4 子切片:shop 移植) | | REQ | Phase 9 ROADMAP「Requirements: TBD」,无需求 ID | N/A(`requirements: []` 如实标注) | | RESEARCH | 无 RESEARCH.md | N/A | | CONTEXT | D-38(布局:顶切换 / 左列表 / 右 名称·描述·购入量选择·购买按钮;价格信息在按钮上方) | COVERED(Task 1/2) |