feat(w05): 按卡独立编辑 + 缺项点名并进顶条 + 区序换回 + 类型层叠图

2026-08-31 用户四连:
① 「各自编辑各自的部分,不要做联动」—— 平台新增 scopedTargets(ui-kit):开合作用域
  跟着触发钮走,三档 —— 钮在本地址宿主的行里→只这一行(A 卡的笔只开 A 卡);钮在别的
  宿主的行里→行内属于本地址的宿主(这张卡的卡背);外部闸行→全宿主(老行为,② 的闸
  不受影响)。收值与保存不随作用域收窄(collect 仍按地址全量,「只写真改了的行」在
  服务端把关)。编辑态/锁面/笔盘显隐 CSS 全部改成按卡(.dossier.on);同地址同时只开
  一张卡(笔在别卡编辑中自动隐去,快照键按地址,双开会互相踩)。2 测 + 变异验证。
② 逐块「还差 N 行必填」红带撤下,点名并进顶部「还差什么」—— 同一批基座读数
  (_missing/_missing_keys)在顶条出两行子行,数值 0 自己消失;gate_reason 改口。
③ 比对基准与芯片档案区序换回(基准在前)。
④ 比对基准换「层叠双封装」SVG(后面那枚淡出成剪影 —— 一类而非一颗),不再与芯片卡
  共用同一枚封装图。

测:ui-kit 35(scoped 2 新,变异红过)+ 包内 105 + 2528 全仓;2.11.0 装库五探针核过
(合并条/无旧红带/类型图/按卡 CSS/基准在前);浏览器实测 XL 卡独自翻背进编辑、LM 卡纹丝不动。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
xcs
2026-08-31 17:01:06 +08:00
co-authored by Claude Fable 5
parent 05ebecb541
commit 26db6c685b
7 changed files with 130 additions and 32 deletions
+37 -4
View File
@@ -365,15 +365,45 @@ export function addRow(doc: Document, address: string): boolean {
}
/**
* 那块内容与它的闸一起开合 —— **同一地址的全部宿主一起**(多宿主时一把闸管一整张表)。
* `.on` 落在哪:行容器是表格的落在 `<table>`(老件,样式按 `table.on` 写);
* 不是表格的落在 `data-edit` 元素自身(2026-08-02 A2 对决卡立的规矩)。
* 那块内容与它的闸一起开合。`.on` 落在哪:行容器是表格的落在 `<table>`(老件,样式按
* `table.on` 写);不是表格的落在 `data-edit` 元素自身(2026-08-02 A2 对决卡立的规矩)。
* 这一版是**全宿主**开合(外部闸行用它);按了卡上钮的走 `scopedTargets`(见下)。
*/
export function setOpenIn(doc: Document, address: string, on: boolean): void {
doc.querySelector<HTMLElement>(`[data-edit-for="${CSS.escape(address)}"]`)?.classList.toggle('on', on);
for (const host of hostsOf(doc, address)) (host.closest('table') ?? host).classList.toggle('on', on);
}
/**
* **开合的作用域跟着触发钮走**(2026-08-31 用户:「芯片档案编辑就不能各自编辑各自的部分吗,
* 不要做联动」)。三档,从近到远:
* ① 钮长在这张表**自己的宿主**里 —— 落到钮所在的**那一行**(宿主的直接子元素;
* W05 芯片卡的笔落在这张卡的行上,只这张卡进编辑);行认不出就整个宿主;
* ② 钮长在**别的**宿主的某一行里 —— 落到**这一行内部**属于本地址的宿主们
* (W05 卡背的参数宿主住在主数据宿主的行里:点这张卡的笔,只这张卡的卡背开);
* ③ 都不是(外部闸行,如差异审核件的编辑闸)—— 全部宿主一起,老行为。
* 收值与保存**不随作用域收窄**(collect 仍按地址全量;「只写真改了的行」在服务端把关,
* 没动的行是 no-op)—— 作用域只管「屏上哪一块进编辑态」这一件事。
*/
export function scopedTargets(doc: Document, address: string, trigger: Element): HTMLElement[] {
const hosts = hostsOf(doc, address);
for (const host of hosts) {
if (host.contains(trigger)) {
const row = rowOf(host, trigger);
return [(row as HTMLElement | null) ?? host];
}
}
/* 钮在别的宿主的行里:找出包住钮的那一行(任意 data-edit 宿主的直接子元素),取行内的本地址宿主 */
for (let n: Element | null = trigger; n !== null; n = n.parentElement) {
const parent = n.parentElement;
if (parent !== null && parent.hasAttribute('data-edit')) {
const inner = hosts.filter((h) => n !== null && n.contains(h));
if (inner.length > 0) return inner;
}
}
return hosts.map((h) => (h.closest('table') ?? h) as HTMLElement);
}
/**
* `data-file="域.对象/主键值"` → 取文件要的那两样。
*
@@ -514,7 +544,10 @@ export function ViewContainer({ html, title = '活动视图', editable = false,
* 容器是 `<section>`,从前这里写死 `closest('table')`,非表格容器永远拿不到
* `.on`,编辑态整个打不开)。
*/
const setOpen = (address: string, on: boolean): void => setOpenIn(doc, address, on);
const setOpen = (address: string, on: boolean): void => {
doc.querySelector<HTMLElement>(`[data-edit-for="${CSS.escape(address)}"]`)?.classList.toggle('on', on);
for (const target of scopedTargets(doc, address, hit)) (target.closest('table') ?? target).classList.toggle('on', on);
};
const open = hit.dataset['editOpen'];
const cancel = hit.dataset['editCancel'];