feat(check): 同一行按单元格;业绩条款不再误跳;重复条目只合并显示(C5 修订)

- 送给模型的「同一行」在有框线的表格里按单元格分行
- 「以合同签订时间为准」不再被当成「签订合同时才交」,要附合同的业绩评分项照常查
- 重复的要求各自判断,问题接口给出 duplicate_ids,查标页同组折叠显示、一起处理
- 决策 C5 修订记入 decisions.md;开发集召回 74%、致命级 25/32

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
YANG JIANKUAN
2026-11-04 18:00:24 +08:00
parent df3ebbb6a1
commit 4a5421a412
12 changed files with 111 additions and 13 deletions

View File

@@ -56,7 +56,18 @@ async function cancel() {
type Sev = "" | Finding["severity"];
const sev = ref<Sev>("");
const all = computed(() => findingsQ.data.value?.items ?? []);
const live = computed(() => all.value.filter((f) => f.presence !== "resolved"));
/** 清单里重复出现的要求(决策 C5 修订):各自判断,同组只显示第一条,处理时一起处理 */
const hidden = computed(() => {
const seen = new Set<string>();
const out = new Set<string>();
for (const f of all.value) {
if ((f.duplicate_ids ?? []).some((id) => seen.has(id))) out.add(f.id);
seen.add(f.id);
}
return out;
});
const group = (f: Finding) => [f, ...all.value.filter((x) => (f.duplicate_ids ?? []).includes(x.id))];
const live = computed(() => all.value.filter((f) => f.presence !== "resolved" && !hidden.value.has(f.id)));
const todo = computed(() => live.value.filter((f) => f.status === "pending" && (!sev.value || f.severity === sev.value)));
const handled = computed(() => live.value.filter((f) => f.status !== "pending" && (!sev.value || f.severity === sev.value)));
const resolved = computed(() => all.value.filter((f) => f.presence === "resolved"));
@@ -88,9 +99,11 @@ async function resolve(f: Finding, status: "confirmed_ok" | "modified" | "ignore
}
busyId.value = f.id;
try {
await unwrap(
api.POST("/findings/{finding_id}/resolve", { params: { path: { finding_id: f.id } }, body: { status, ...(text ? { note: text.trim() } : {}) } }),
);
for (const x of group(f).filter((g) => g.status === "pending")) {
await unwrap(
api.POST("/findings/{finding_id}/resolve", { params: { path: { finding_id: x.id } }, body: { status, ...(text ? { note: text.trim() } : {}) } }),
);
}
noteFor.value = null;
toast(`已标记为「${FINDING_ACTION[status]}」`, { undo: () => void reopen(f) });
refresh();
@@ -104,7 +117,9 @@ async function resolve(f: Finding, status: "confirmed_ok" | "modified" | "ignore
async function reopen(f: Finding) {
try {
await unwrap(api.POST("/findings/{finding_id}/reopen", { params: { path: { finding_id: f.id } } }));
for (const x of group(f)) {
await unwrap(api.POST("/findings/{finding_id}/reopen", { params: { path: { finding_id: x.id } } }));
}
refresh();
} catch (e) {
toast(errorTitle(e), { ok: false });
@@ -226,6 +241,7 @@ const errorText = computed(() => {
<div class="f-head">
<span class="sev" :class="SEVERITY_CLASS[f.severity]">{{ SEVERITY[f.severity] }}</span>
<span class="f-title">{{ f.title }}</span>
<span v-if="(f.duplicate_ids?.length ?? 0) > 0" class="flag" title="同一要求在清单里出现了几处,各自检查、结果相同,处理时一起处理">清单里出现 <span class="fig">{{ (f.duplicate_ids?.length ?? 0) + 1 }}</span> 处</span>
<span v-if="f.presence === 'carried'" class="flag">沿用<template v-if="f.carried_status === 'modified'">:上次已修改,这次仍查出</template></span>
</div>
<p class="f-text">{{ f.explanation }}</p>