iter(g304): 配方补可排序检查单(句柄式拼装)

This commit is contained in:
HIsheR
2026-10-27 00:33:10 +08:00
parent b24da598d1
commit 0851de91a1
4 changed files with 58 additions and 0 deletions
+9
View File
@@ -3122,6 +3122,15 @@
"motivation": "sortableList 的拖拽态原本整行 opacity: .7,实测把拖拽行正文对比从 12.03 压到 4.86(亮色)——低于项目正文 7:1 的口径,而「正在被拿起」完全可以用虚线边 + 抬升投影表达,不必牺牲可读性。", "motivation": "sortableList 的拖拽态原本整行 opacity: .7,实测把拖拽行正文对比从 12.03 压到 4.86(亮色)——低于项目正文 7:1 的口径,而「正在被拿起」完全可以用虚线边 + 抬升投影表达,不必牺牲可读性。",
"acceptance": "① 拖拽态去掉整行 opacity,改用 --ua-shadow-2 抬升 + 虚线 accent 边 + tint-accent 底;② 静态态对比回到 12+(亮)/ 11+(暗);③ 把手悬停转 accent 色提示可抓,拖拽中 cursor 为 grabbing;④ audit:visual / audit:contrast / audit:motion 全绿,manifest 步骤同步。", "acceptance": "① 拖拽态去掉整行 opacity,改用 --ua-shadow-2 抬升 + 虚线 accent 边 + tint-accent 底;② 静态态对比回到 12+(亮)/ 11+(暗);③ 把手悬停转 accent 色提示可抓,拖拽中 cursor 为 grabbing;④ audit:visual / audit:contrast / audit:motion 全绿,manifest 步骤同步。",
"notes": ".is-dragging 去 opacity 改 --ua-shadow-2 抬升 + 虚线 accent 边 + tint-accent 底(对比 4.86 → 12.03 亮 / 11.32 暗);把手悬停转 accent、拖拽中 grabbing;item 过渡补 box-shadow,manifest 步骤同步" "notes": ".is-dragging 去 opacity 改 --ua-shadow-2 抬升 + 虚线 accent 边 + tint-accent 底(对比 4.86 → 12.03 亮 / 11.32 暗);把手悬停转 accent、拖拽中 grabbing;item 过渡补 box-shadow,manifest 步骤同步"
},
{
"id": "g304",
"domain": "tooling",
"status": "open",
"title": "配方补一条可排序检查单:句柄式组件的拼装样例",
"motivation": "sortableList 是句柄式组件(返回 { el, move, order }),与 form / virtualList 同类,但 18 条配方里没有一条演示句柄式拼装——Agent 照配方抄会把返回值直接当节点 append,页面一片空白;「顺序只存一份」「move 与拖拽同一条提交路径」这些约定也没人写下来。",
"acceptance": "① recipes.md 新增配方 19(编号连续、带代码块与要点),代码块里认出 sortableList / card / button / toast;② 要点覆盖句柄要 append(.el)、顺序唯一真相、move 与拖拽同路径、边缘不自动滚动、条目文案唯一;③ ua recipes 19 能取到该条,llms-full.txt 与 llms.txt 重生成;④ npm run verify / check 全绿。",
"notes": ""
} }
] ]
} }
+24
View File
@@ -3264,6 +3264,30 @@ const panel = h('div', { class: 'review' },
- `codeBlock` 的行号不参与复制,`copyButton` 直接读原文,所以「复制出来的代码」永远干净。 - `codeBlock` 的行号不参与复制,`copyButton` 直接读原文,所以「复制出来的代码」永远干净。
- 差异行多了给 `diffView` 外层限定高度(或放进抽屉),否则长差异会把审阅按钮挤出视口。 - 差异行多了给 `diffView` 外层限定高度(或放进抽屉),否则长差异会把审阅按钮挤出视口。
## 配方 19:可排序的检查单(拖拽 + 键盘搬位 + 顺序回写)
```ts
const steps = ['需求评审', '接口联调', '灰度发布', '线上回归'];
const order = signal(steps); // 顺序唯一真相:组件与下方摘要读同一个信号
const list = sortableList({
items: steps, // 文案即排序键,要求唯一
value: order,
onChange: (next) => toast.success(`已调整:${next[0]} 置顶`),
});
card({ title: '发布检查单(可拖动,Alt+↑↓ 键盘搬位)' },
list.el, // 返回的是句柄不是节点:忘了 append(el) 页面上一片空白
h('p', { class: 'desc' }, () => order().map((t, i) => `${i + 1}. ${t}`).join(' ')),
button({ label: '把「灰度发布」置顶', variant: 'ghost', onClick: () => list.move(2, 0) }),
);
```
要点:
- `sortableList` 返回 `{ el, move, order }` 句柄(与 `form` / `virtualList` 同类):节点在 `el` 上,业务侧要改顺序用 `move(from, to)`,读当前顺序用 `order()`。
- 顺序只存一份:`value` 信号同时喂组件与摘要 / 上报逻辑,否则会出现「界面拖了、提交的仍是老顺序」。
- `move()` 与拖拽走同一条提交路径(都会触发 `onChange`),所以「置顶 / 上移」按钮不必另写状态同步;越界下标静默忽略,不用自己判边界。
- 拖到边缘不会自动滚动:条目多到一屏放不下时改用筛选或上下移按钮,不要指望拖出视口;跨列表搬运用两个实例各持一个信号。
- 条目文案要唯一——重名会让「谁被搬走了」失去参照,需要重复内容时把序号拼进文案(`'确认条款 #1'`)。
## 通用约定 ## 通用约定
- 状态一律放信号(`signal` / `computed`),组件只读写信号,不持有业务数据副本。 - 状态一律放信号(`signal` / `computed`),组件只读写信号,不持有业务数据副本。
+1
View File
@@ -190,3 +190,4 @@
- 配方 16:后台页骨架(3 条要点) - 配方 16:后台页骨架(3 条要点)
- 配方 17:表格刷新遮罩(3 条要点) - 配方 17:表格刷新遮罩(3 条要点)
- 配方 18:变更审阅面板(3 条要点) - 配方 18:变更审阅面板(3 条要点)
- 配方 19:可排序的检查单(拖拽 + 键盘搬位 + 顺序回写)(5 条要点)
+24
View File
@@ -407,6 +407,30 @@ const panel = h('div', { class: 'review' },
- `codeBlock` 的行号不参与复制,`copyButton` 直接读原文,所以「复制出来的代码」永远干净。 - `codeBlock` 的行号不参与复制,`copyButton` 直接读原文,所以「复制出来的代码」永远干净。
- 差异行多了给 `diffView` 外层限定高度(或放进抽屉),否则长差异会把审阅按钮挤出视口。 - 差异行多了给 `diffView` 外层限定高度(或放进抽屉),否则长差异会把审阅按钮挤出视口。
## 配方 19:可排序的检查单(拖拽 + 键盘搬位 + 顺序回写)
```ts
const steps = ['需求评审', '接口联调', '灰度发布', '线上回归'];
const order = signal(steps); // 顺序唯一真相:组件与下方摘要读同一个信号
const list = sortableList({
items: steps, // 文案即排序键,要求唯一
value: order,
onChange: (next) => toast.success(`已调整:${next[0]} 置顶`),
});
card({ title: '发布检查单(可拖动,Alt+↑↓ 键盘搬位)' },
list.el, // 返回的是句柄不是节点:忘了 append(el) 页面上一片空白
h('p', { class: 'desc' }, () => order().map((t, i) => `${i + 1}. ${t}`).join(' ')),
button({ label: '把「灰度发布」置顶', variant: 'ghost', onClick: () => list.move(2, 0) }),
);
```
要点:
- `sortableList` 返回 `{ el, move, order }` 句柄(与 `form` / `virtualList` 同类):节点在 `el` 上,业务侧要改顺序用 `move(from, to)`,读当前顺序用 `order()`。
- 顺序只存一份:`value` 信号同时喂组件与摘要 / 上报逻辑,否则会出现「界面拖了、提交的仍是老顺序」。
- `move()` 与拖拽走同一条提交路径(都会触发 `onChange`),所以「置顶 / 上移」按钮不必另写状态同步;越界下标静默忽略,不用自己判边界。
- 拖到边缘不会自动滚动:条目多到一屏放不下时改用筛选或上下移按钮,不要指望拖出视口;跨列表搬运用两个实例各持一个信号。
- 条目文案要唯一——重名会让「谁被搬走了」失去参照,需要重复内容时把序号拼进文案(`'确认条款 #1'`)。
## 通用约定 ## 通用约定
- 状态一律放信号(`signal` / `computed`),组件只读写信号,不持有业务数据副本。 - 状态一律放信号(`signal` / `computed`),组件只读写信号,不持有业务数据副本。