mirror of
https://github.com/motajs/template.git
synced 2026-10-16 10:30:18 +08:00
fix(09-minimum-browser-runnable): revise 09-09 plan per checker feedback
This commit is contained in:
parent
7f0c90e3af
commit
8951c54c88
@ -30,7 +30,7 @@ must_haves:
|
||||
provides: "D-38..D-41 的商店 UI:顶部 购买/售出 切换;左侧 商店列表(id 排序 / 库存右对齐 / 可滚动显滚动条);右侧 物品名称 / 描述(纵滚无滚动条)/ 购入量选择(`<< < {量} > >>`,按物品分别记忆,上限 库存/拥有,按钮上方 买价/卖价/存货/拥有/总价)/ 购买按钮;右下退出;入参 `IShopState` + `IHeroState<IHeroAttr>` + `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<number, [buy: number, sell: number]>`(`: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:
|
||||
---
|
||||
|
||||
<objective>
|
||||
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 商店界面**。
|
||||
</action>
|
||||
<reversibility rating="reversible">纯新增文件;删除 / `git revert` 可回退。</reversibility>
|
||||
<verify>
|
||||
<automated>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'],['<Scroll','scroll'],['noscroll','description no-scroll'],['<icon','icon'],['new GameUI(','GameUI export'],['props: [','props array']];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(!/ShopUI\s*=\s*new GameUI\(/.test(t))bad.push('ShopUI not exported');if(!/props:\s*\[\s*'controller'\s*,\s*'instance'\s*,\s*'state'\s*,\s*'shop'\s*,\s*'heroState'\s*,\s*'shopSystem'\s*\]/.test(t))bad.push('props array incomplete');}if(bad.length>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')"</automated>
|
||||
<automated>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'],['<Scroll','scroll'],['noscroll','description no-scroll'],['<icon','icon'],['new GameUI(','GameUI export'],['props: [','props array']];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(!/ShopUI\s*=\s*new GameUI\(/.test(t))bad.push('ShopUI not exported');if(!/props:\s*\[\s*'controller'\s*,\s*'instance'\s*,\s*'state'\s*,\s*'shop'\s*,\s*'heroState'\s*,\s*'shopSystem'\s*\]/.test(t))bad.push('props array incomplete');}if(bad.length>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')"</automated>
|
||||
<fails_when>non-zero exit, 或 stderr 含 "SHOP LAYOUT GATE FAILED"(文件缺失 / 关键符号缺失 / props 数组不含 `shop`/`heroState`/`shopSystem` / 引入 `import type` 或 `?.`)</fails_when>
|
||||
<automated>node -e "const fs=require('fs');const raw=fs.readFileSync('packages-user/client-modules/src/ui/shop.tsx','utf8');const bad=[];if(/(?<!\r)\n/.test(raw))bad.push('LF without CR');if(raw.includes('\r')&&!/\r\n/.test(raw))bad.push('stray CR');if(bad.length){console.error('CRLF FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK CRLF: shop.tsx')"</automated>
|
||||
<fails_when>non-zero exit, 或 stderr 含 "CRLF FAILED"(换行非 CRLF)</fails_when>
|
||||
@ -261,7 +261,7 @@ Phase 9 · 任务 3 · 第 9 个计划 —— **shop 商店界面**。
|
||||
</action>
|
||||
<reversibility rating="reversible">纯新增文件内交互逻辑;`git revert` 可回退。</reversibility>
|
||||
<verify>
|
||||
<automated>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')"</automated>
|
||||
<automated>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')"</automated>
|
||||
<fails_when>non-zero exit, 或 stderr 含 "SHOP ACTION GATE FAILED"(数量 / 价格标签 / 交易或交互符号缺失 / 引入 `import type` / `?.` / 连续 `as`)</fails_when>
|
||||
<automated>node -e "const fs=require('fs');const raw=fs.readFileSync('packages-user/client-modules/src/ui/shop.tsx','utf8');const bad=[];if(/(?<!\r)\n/.test(raw))bad.push('LF without CR');if(raw.includes('\r')&&!/\r\n/.test(raw))bad.push('stray CR');if(bad.length){console.error('CRLF FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK CRLF: shop.tsx')"</automated>
|
||||
<fails_when>non-zero exit, 或 stderr 含 "CRLF FAILED"(换行非 CRLF)</fails_when>
|
||||
@ -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;范围守卫无越界改动
|
||||
</acceptance_criteria>
|
||||
<done>数量选择 + 信息区 + 购买/售出(经 `IShopSystem`)+ 退出完成;最终静态门禁 + CRLF + 范围守卫全绿</done>
|
||||
</task>
|
||||
@ -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) |
|
||||
|
||||
Loading…
Reference in New Issue
Block a user