kld-sdd 2.6.16 → 2.6.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/kld-sdd-init.js +10 -0
- package/kld-sdd-guide.html +22 -0
- package/lib/device-auth-cli.js +130 -0
- package/lib/device-auth.js +345 -0
- package/lib/init-account-binding.js +108 -0
- package/lib/init.js +238 -55
- package/lib/skills-bundle.js +20 -1
- package/lib/tool-profiles.js +8 -0
- package/package.json +7 -3
- package/skywalk-sdd/apply-worktree-finish.cjs +2 -23
- package/skywalk-sdd/context-client.cjs +38 -87
- package/skywalk-sdd/index.cjs +860 -132
- package/skywalk-sdd/kb-sync-identity.cjs +780 -0
- package/skywalk-sdd/kb-upload.cjs +505 -0
- package/skywalk-sdd/lib/shared.cjs +811 -0
- package/skywalk-sdd/lib/usage-contract.cjs +276 -0
- package/skywalk-sdd/lib/usage-reporter.cjs +354 -0
- package/skywalk-sdd/lib/user-config.cjs +157 -0
- package/skywalk-sdd/metrics-v3.cjs +138 -8
- package/skywalk-sdd/ontology/archive-package.cjs +19 -34
- package/skywalk-sdd/ontology/change-lock.cjs +3 -7
- package/skywalk-sdd/ontology/external-key.cjs +18 -4
- package/skywalk-sdd/ontology/id.cjs +26 -5
- package/skywalk-sdd/ontology/identity-index.cjs +3 -7
- package/skywalk-sdd/ontology/resolve-spec-root.cjs +20 -6
- package/skywalk-sdd/ontology/runtime.cjs +16 -12
- package/skywalk-sdd/ontology/traceability-validator.cjs +7 -4
- package/skywalk-sdd/reporting/change-report-markdown.cjs +137 -19
- package/skywalk-sdd/reporting/change-report-model.cjs +993 -14
- package/skywalk-sdd/reporting/change-report-renderer.cjs +106 -41
- package/skywalk-sdd/reporting/change-report-view-model.cjs +272 -43
- package/skywalk-sdd/reporting/core-metric-definitions.cjs +192 -0
- package/skywalk-sdd/spec-root.cjs +31 -0
- package/templates/git-hooks/pre-commit-consistency-check.cjs +271 -116
- package/templates/git-hooks/pre-push-consistency-check.cjs +252 -123
- package/templates/hooks/codebuddy/hooks/hook-gate-core.cjs +327 -0
- package/templates/hooks/codebuddy/hooks/sdd-apply-test-gate.cjs +54 -9
- package/templates/hooks/codebuddy/hooks/sdd-mid-checkpoint.cjs +63 -6
- package/templates/hooks/codebuddy/hooks/sdd-tdd-rhythm-gate.cjs +113 -65
- package/templates/openspec/proposal.md +7 -3
- package/templates/openspec/spec.md +3 -3
- package/templates/skills/kld-sdd/opsx-apply/SKILL.md +8 -6
- package/templates/skills/kld-sdd/opsx-apply/checklist.md +2 -0
- package/templates/skills/kld-sdd/opsx-apply/reference.md +29 -7
- package/templates/skills/kld-sdd/opsx-archive/SKILL.md +6 -5
- package/templates/skills/kld-sdd/opsx-check/SKILL.md +49 -17
- package/templates/skills/kld-sdd/opsx-check/checklist.md +4 -2
- package/templates/skills/kld-sdd/opsx-consistency-check/SKILL.md +187 -257
- package/templates/skills/kld-sdd/opsx-consistency-check/reference.md +129 -0
- package/templates/skills/kld-sdd/opsx-design/SKILL.md +2 -2
- package/templates/skills/kld-sdd/opsx-explore/SKILL.md +2 -2
- package/templates/skills/kld-sdd/opsx-kb-config/SKILL.md +185 -0
- package/templates/skills/kld-sdd/opsx-kb-config/reference.md +127 -0
- package/templates/skills/kld-sdd/opsx-kb-ingest/SKILL.md +218 -53
- package/templates/skills/kld-sdd/opsx-kb-ingest/reference.md +51 -9
- package/templates/skills/kld-sdd/opsx-ontology-query/SKILL.md +12 -50
- package/templates/skills/kld-sdd/opsx-ontology-query/phase-3-postchange.md +2 -2
- package/templates/skills/kld-sdd/opsx-ontology-query/reference.md +1 -1
- package/templates/skills/kld-sdd/opsx-propose/SKILL.md +35 -23
- package/templates/skills/kld-sdd/opsx-propose/checklist.md +2 -0
- package/templates/skills/kld-sdd/opsx-propose/reference.md +22 -17
- package/templates/skills/kld-sdd/opsx-rules/SKILL.md +2 -2
- package/templates/skills/kld-sdd/opsx-spec/SKILL.md +19 -15
- package/templates/skills/kld-sdd/opsx-spec/checklist.md +2 -0
- package/templates/skills/kld-sdd/opsx-task/SKILL.md +2 -4
- package/templates/skills/kld-sdd/opsx-test/SKILL.md +2 -2
- package/templates/skills/kld-sdd/tdd-core/reference.md +1 -1
- package/templates/skills/kld-sdd/tdd-rules/rules/test-skeleton-telemetry.md +1 -1
- package/templates/skills/kld-sdd/opsx-kb-ingest/state.example.json +0 -7
- package/templates/skills/kld-sdd/opsx-ontology-query/state.example.json +0 -7
|
@@ -14,24 +14,62 @@ function escapeHtml(value) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function statusTone(status) {
|
|
17
|
-
if (['trusted', 'complete', 'passed', 'verified', 'implemented'].includes(status)) return 'good';
|
|
18
|
-
if (['attention', 'incomplete', 'provisional', 'needs_input', 'open'].includes(status)) return 'warn';
|
|
19
|
-
if (['failed', 'blocked', 'error'].includes(status)) return 'bad';
|
|
17
|
+
if (['trusted', 'complete', 'passed', 'verified', 'implemented', 'success', '可信', '已独立验证', '可计算'].includes(status)) return 'good';
|
|
18
|
+
if (['attention', 'incomplete', 'provisional', 'inconsistent', 'needs_input', 'open', '需补证', '尚未独立验证', '计算不一致'].includes(status)) return 'warn';
|
|
19
|
+
if (['failed', 'failure', 'blocked', 'error'].includes(status)) return 'bad';
|
|
20
20
|
return 'neutral';
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function
|
|
23
|
+
function metricDrawerBody(detail, fallback) {
|
|
24
|
+
if (!detail) return fallback;
|
|
25
|
+
const inputs = (detail.raw_inputs || []).map((item) => {
|
|
26
|
+
const value = item.display_value ?? item.value ?? '暂无数据';
|
|
27
|
+
const exclusion = item.included === false
|
|
28
|
+
? `;不计入公式:${item.exclusion_reason || '仅作辅助说明'}`
|
|
29
|
+
: '';
|
|
30
|
+
return `- ${item.label}:${value}\n 来源:${item.source || '未记录'}${exclusion}`;
|
|
31
|
+
});
|
|
32
|
+
const evidence = (detail.evidence || []).map(item => (
|
|
33
|
+
[
|
|
34
|
+
`- ${item.path || item.event_id || item.type || '证据'}${item.count == null ? '' : `(${item.count} 条)`}`,
|
|
35
|
+
...(item.scenarios || []).map(scenario => ` - 场景:${scenario.title || '未命名'}(第 ${scenario.line ?? '未知'} 行)`),
|
|
36
|
+
].join('\n')
|
|
37
|
+
));
|
|
38
|
+
return [
|
|
39
|
+
`指标定义\n${detail.definition}`,
|
|
40
|
+
`指标用途\n${detail.purpose}`,
|
|
41
|
+
`计算公式\n${detail.formula}`,
|
|
42
|
+
`数据来源\n${detail.source_description}`,
|
|
43
|
+
`对应方案\n${detail.scheme_reference}`,
|
|
44
|
+
`本次原始数据\n${inputs.join('\n') || '- 暂无可展示的原始数据'}`,
|
|
45
|
+
`计算步骤\n${(detail.calculation_steps || []).map(step => `- ${step}`).join('\n') || '- 暂无可复算步骤'}`,
|
|
46
|
+
`本次结果\n${detail.result?.display_value || '暂无数据'}\n可信度:${detail.trust?.explanation || '未记录'}`,
|
|
47
|
+
`证据与限制\n${[...evidence, ...(detail.caveats || []).map(item => `- ${item}`)].join('\n') || '- 暂无可定位证据'}`,
|
|
48
|
+
].join('\n\n');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function metricCard({
|
|
52
|
+
name,
|
|
53
|
+
code,
|
|
54
|
+
value,
|
|
55
|
+
state,
|
|
56
|
+
state_code: stateCode,
|
|
57
|
+
explanation,
|
|
58
|
+
source,
|
|
59
|
+
detail,
|
|
60
|
+
}) {
|
|
61
|
+
const drawerBody = metricDrawerBody(detail, source || explanation);
|
|
24
62
|
return `<article class="metric-card">
|
|
25
63
|
<div class="metric-top"><span>${escapeHtml(name)}</span><span class="metric-code">${escapeHtml(code)}</span></div>
|
|
26
64
|
<strong>${escapeHtml(value)}</strong>
|
|
27
|
-
<div class="metric-state ${statusTone(state)}">${escapeHtml(state || '暂定')}</div>
|
|
65
|
+
<div class="metric-state ${statusTone(stateCode || state)}">${escapeHtml(state || '暂定')}</div>
|
|
28
66
|
<p>${escapeHtml(explanation)}</p>
|
|
29
|
-
<button class="info-btn" type="button" data-info="${escapeHtml(source || explanation)}" data-drawer-title="${escapeHtml(`${name} · ${code}`)}" data-drawer-body="${escapeHtml(
|
|
67
|
+
<button class="info-btn" type="button" data-info="${escapeHtml(source || explanation)}" data-drawer-title="${escapeHtml(`${detail?.name || name} · ${code}`)}" data-drawer-body="${escapeHtml(drawerBody)}">查看计算口径与证据</button>
|
|
30
68
|
</article>`;
|
|
31
69
|
}
|
|
32
70
|
|
|
33
71
|
function renderChangeReportHtml(report) {
|
|
34
|
-
if (!report.change_summary || !report.artifacts?.files) {
|
|
72
|
+
if (!report.change_summary || !report.artifacts?.files || !report.metric_details) {
|
|
35
73
|
report = buildChangeReportModel(report);
|
|
36
74
|
}
|
|
37
75
|
const view = buildChangeReportViewModel(report);
|
|
@@ -50,12 +88,23 @@ function renderChangeReportHtml(report) {
|
|
|
50
88
|
const conclusionTone = statusTone(view.conclusion?.status);
|
|
51
89
|
const evidenceCategories = evidence.categories;
|
|
52
90
|
const stageNodes = metricsSection.stages || [];
|
|
53
|
-
const activeLinks = artifactSection.attribution?.active_links || [];
|
|
54
|
-
const candidateIds = artifactSection.attribution?.unassigned_candidate_event_ids || [];
|
|
55
91
|
const knownWarnings = view.known_risks?.warnings || [];
|
|
56
|
-
const
|
|
57
|
-
Array.isArray(warning.items)
|
|
58
|
-
|
|
92
|
+
const riskRows = knownWarnings.flatMap((warning) => {
|
|
93
|
+
const items = Array.isArray(warning.items) && warning.items.length > 0
|
|
94
|
+
? warning.items
|
|
95
|
+
: [{ description: `${warning.command || '检查'} 阶段记录了 ${warning.warnings || 0} 个提醒` }];
|
|
96
|
+
return items.map(item => ({
|
|
97
|
+
...item,
|
|
98
|
+
status_label: ({
|
|
99
|
+
fixed: '已解决',
|
|
100
|
+
accepted: '已接受,保留观察',
|
|
101
|
+
waived: '已豁免,不阻断',
|
|
102
|
+
ignored: '不重要,已标记',
|
|
103
|
+
needs_input: '等待补充信息',
|
|
104
|
+
open: '待处理',
|
|
105
|
+
}[item.disposition] || (warning.resolved === true ? '已解决' : '待确认')),
|
|
106
|
+
}));
|
|
107
|
+
});
|
|
59
108
|
const telemetryWarnings = view.telemetry_warnings?.by_type || {};
|
|
60
109
|
const artifactRows = artifacts.map((item) => `
|
|
61
110
|
<tr>
|
|
@@ -74,18 +123,20 @@ function renderChangeReportHtml(report) {
|
|
|
74
123
|
: (item?.size_bytes == null ? '—' : escapeHtml(item.size_bytes))}</td>
|
|
75
124
|
</tr>`).join('');
|
|
76
125
|
const evidenceRows = evidenceCategories.map((group) => {
|
|
77
|
-
const issueDetails = (group.issues || []).map((issue) =>
|
|
126
|
+
const issueDetails = (group.issues || []).map((issue) => {
|
|
127
|
+
const displayIssue = issueSection.issues.find(item => item.issue_id === issue.issue_id) || issue;
|
|
128
|
+
return [
|
|
78
129
|
`问题:${issue.issue_id}`,
|
|
79
130
|
`对象:${issue.subject || '本次变更'}`,
|
|
80
|
-
`状态:${issue.status}`,
|
|
81
|
-
`判定:${issue.verdict}`,
|
|
131
|
+
`状态:${displayIssue.status_label || issue.status}`,
|
|
82
132
|
`根因:${issue.root_cause}`,
|
|
83
133
|
`预期:${issue.expected}`,
|
|
84
134
|
`实际:${issue.actual}`,
|
|
85
135
|
`影响:${issue.impact}`,
|
|
86
136
|
`修复建议:${issue.remediation}`,
|
|
87
137
|
`证据事件:${(issue.evidence_event_ids || []).join('、') || '暂无可定位事件'}`,
|
|
88
|
-
|
|
138
|
+
].join('\n');
|
|
139
|
+
}).join('\n\n');
|
|
89
140
|
return `
|
|
90
141
|
<article class="issue-row">
|
|
91
142
|
<div><span class="issue-count">${escapeHtml(group.occurrence_count)}</span></div>
|
|
@@ -95,7 +146,7 @@ function renderChangeReportHtml(report) {
|
|
|
95
146
|
</div>
|
|
96
147
|
<button class="text-button drawer-trigger" type="button"
|
|
97
148
|
data-drawer-title="${escapeHtml(group.label)}"
|
|
98
|
-
data-drawer-body="${escapeHtml(`${issueDetails || `规则编码:${group.code}\n
|
|
149
|
+
data-drawer-body="${escapeHtml(`${issueDetails || `规则编码:${group.code}\n状态:待处理\n根因:当前证据未满足规则\n预期:规则证据完整\n实际:证据不足\n修复建议:补充真实证据`}\n\n规则编码:${group.code}\n关联任务:${(group.affected_tasks || []).join('、') || '无'}\n事件:${(group.event_ids || []).join('、') || '无'}`)}">查看详情</button>
|
|
99
150
|
</article>`;
|
|
100
151
|
}).join('');
|
|
101
152
|
const stageRows = stageNodes.map((node) => {
|
|
@@ -106,7 +157,7 @@ function renderChangeReportHtml(report) {
|
|
|
106
157
|
<td>${escapeHtml(stage)}${round > 1 ? '²' : ''}${round > 1 ? '<span class="tl-repeat">R</span>' : ''}</td>
|
|
107
158
|
<td>${escapeHtml(round)}</td>
|
|
108
159
|
<td><span class="tl-time">${escapeHtml(node.display_start)}→${escapeHtml(node.display_end)}</span><br>${escapeHtml(node.display_duration)}</td>
|
|
109
|
-
<td><span class="state ${statusTone(node.
|
|
160
|
+
<td><span class="state ${statusTone(node.result)}">${escapeHtml(node.display_result)}</span></td>
|
|
110
161
|
</tr>`;
|
|
111
162
|
}).join('');
|
|
112
163
|
|
|
@@ -164,9 +215,11 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
164
215
|
.issue-row{display:grid;grid-template-columns:52px 1fr auto;align-items:center;gap:14px;padding:15px 4px;border-bottom:1px solid #edf0f4}.issue-row:last-child{border-bottom:0}.issue-count{display:grid;place-items:center;width:42px;height:42px;border-radius:12px;background:var(--warn-soft);color:var(--warn);font-weight:800}.issue-row h3{margin:0 0 2px}.issue-row p{margin:0;color:var(--muted);font-size:12px}
|
|
165
216
|
.callout{border-left:4px solid var(--blue);background:var(--blue-soft);padding:12px 14px;border-radius:0 10px 10px 0;color:#344054}
|
|
166
217
|
.drawer-backdrop{position:fixed;inset:0;z-index:40;background:rgba(15,23,42,.42);display:none}.drawer-backdrop.open{display:block}
|
|
167
|
-
.drawer{position:absolute;right:0;top:0;height:100%;width:
|
|
218
|
+
.drawer{position:absolute;right:0;top:0;height:100%;width:min(720px,92vw);background:#fff;padding:26px;box-shadow:-20px 0 70px rgba(15,23,42,.22);overflow:auto}
|
|
168
219
|
.drawer-head{display:flex;justify-content:space-between;gap:16px;align-items:start}.drawer h2{margin:0}.drawer pre{white-space:pre-wrap;word-break:break-word;background:#f8fafc;border:1px solid var(--line);border-radius:12px;padding:15px;color:#344054}
|
|
169
220
|
.icon-button{border:1px solid var(--line);background:#fff;border-radius:9px;width:34px;height:34px;cursor:pointer}.icon-button:focus-visible{outline:3px solid #b2ccff}
|
|
221
|
+
@media screen and (max-width:1040px){body{min-width:0}.shell{width:100%;margin:0}.report-head{border-width:0 0 1px;border-radius:0}.identity{align-items:flex-start;flex-direction:column;padding:16px;gap:8px}.freshness{text-align:left}.tabs{overflow-x:auto;padding:0 8px}.tabs button{flex:0 0 auto;padding-left:12px;padding-right:12px}.panel{border-width:0;padding:12px;min-height:0;border-radius:0}.hero,.two-col{grid-template-columns:minmax(0,1fr)}.summary-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.metric-grid{grid-template-columns:minmax(0,1fr)}.section,.card,.wide-card{min-width:0}.wide-card{overflow-x:auto}.wide-card table{min-width:640px}.issue-row{grid-template-columns:44px minmax(0,1fr)}.issue-row .text-button{grid-column:2}.section-head{align-items:flex-start}.drawer{width:100%;max-width:100vw;padding:18px}}
|
|
222
|
+
@media screen and (max-width:520px){.summary-grid{grid-template-columns:minmax(0,1fr)}}
|
|
170
223
|
@media print{body{min-width:0;background:#fff}.shell{width:100%;margin:0}.report-head{position:static;box-shadow:none}.tabs{display:none}.panel{display:block!important;border:0;min-height:0;break-before:page}.drawer-backdrop{display:none!important}}
|
|
171
224
|
</style>
|
|
172
225
|
</head>
|
|
@@ -196,7 +249,7 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
196
249
|
<h2>一句话结论</h2>
|
|
197
250
|
<p>${escapeHtml(view.conclusion?.text || '当前证据不足,暂不能形成可信结论。')}</p>
|
|
198
251
|
</article>
|
|
199
|
-
<article class="card scope"><span
|
|
252
|
+
<article class="card scope"><span>标准阶段有效耗时(含返工)</span><strong>${escapeHtml(overviewSection.scope_duration)}</strong><span>辅助阶段和阶段间空隙不进入 E1 分子</span></article>
|
|
200
253
|
</div>
|
|
201
254
|
<div class="summary-grid">${summaryCards}</div>
|
|
202
255
|
<div class="section">
|
|
@@ -227,13 +280,24 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
227
280
|
|
|
228
281
|
<section class="panel" role="tabpanel" id="panel-evidence" aria-labelledby="tab-evidence" hidden>
|
|
229
282
|
<div class="section-head"><div><h2>问题与证据</h2><p>按问题类别聚合;展开后再查看任务和事件,不把规则命中误称为独立缺陷。</p></div></div>
|
|
283
|
+
<article class="wide-card">
|
|
284
|
+
<h3>这些内容是怎么收集的</h3>
|
|
285
|
+
<p>${escapeHtml(issueSection.collection_method)}</p>
|
|
286
|
+
<p><strong>功能验证:</strong>${escapeHtml(issueSection.functional_result?.summary)} ${escapeHtml(issueSection.functional_result?.explanation)}</p>
|
|
287
|
+
<p><strong>证据链判断:</strong>${escapeHtml(issueSection.traceability_result?.summary)} ${escapeHtml(issueSection.traceability_result?.explanation)}</p>
|
|
288
|
+
</article>
|
|
230
289
|
<div class="issue-summary"><strong>${escapeHtml(evidence.category_count ?? 0)} 类问题</strong><p>${escapeHtml(evidence.occurrence_count ?? 0)} 次规则命中</p></div>
|
|
231
290
|
<article class="wide-card">${evidenceRows || '<p class="micro">当前没有证据问题。</p>'}</article>
|
|
291
|
+
${(issueSection.warning_dispositions || []).length > 0 ? `
|
|
292
|
+
<article class="wide-card section">
|
|
293
|
+
<h3>告警处置</h3>
|
|
294
|
+
${issueSection.warning_dispositions.map(item => `<p>${escapeHtml(item.warning || '未标识告警')}:${escapeHtml(item.disposition_label || '待处理')}${item.reason ? `;${escapeHtml(item.reason)}` : ''}</p>`).join('')}
|
|
295
|
+
</article>` : ''}
|
|
232
296
|
<article class="wide-card section">
|
|
233
297
|
<h3>已知风险</h3>
|
|
234
298
|
<p>严重问题:${escapeHtml(view.known_risks?._available ? (view.known_risks.severe_issues?.length ?? 0) : '无数据')}</p>
|
|
235
|
-
${
|
|
236
|
-
?
|
|
299
|
+
${riskRows.length > 0
|
|
300
|
+
? riskRows.map((item) => `<p><span class="state ${item.status_label === '待处理' || item.status_label === '待确认' ? 'warn' : 'neutral'}">${escapeHtml(item.status_label)}</span> ${escapeHtml(item.description || '待确认项')}${item.target ? `(${escapeHtml(item.target)})` : ''}</p>`).join('')
|
|
237
301
|
: '<p class="micro">无需修复</p>'}
|
|
238
302
|
</article>
|
|
239
303
|
${Array.isArray(view.process_notes?.notes) && view.process_notes.notes.length > 0 ? `
|
|
@@ -246,26 +310,25 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
246
310
|
</section>
|
|
247
311
|
|
|
248
312
|
<section class="panel" role="tabpanel" id="panel-artifacts" aria-labelledby="tab-artifacts" hidden>
|
|
249
|
-
<div class="section-head"><div><h2>产物与追溯</h2><p
|
|
313
|
+
<div class="section-head"><div><h2>产物与追溯</h2><p>分别说明任务过程中涉及的文件和最终实际交付的文件。</p></div></div>
|
|
250
314
|
<div class="two-col">
|
|
251
315
|
<article class="wide-card">
|
|
252
|
-
<h3
|
|
253
|
-
<
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
</tbody></table>
|
|
259
|
-
<p class="micro">${escapeHtml(changed.reconciliation_note || '暂无口径说明。')}</p>
|
|
316
|
+
<h3>${escapeHtml(changed.task_scope?.label || '任务过程中涉及的文件')}</h3>
|
|
317
|
+
<p>${escapeHtml(changed.task_scope?.explanation || '')}</p>
|
|
318
|
+
<p><strong>${escapeHtml(changed.task_scope?.count ?? '暂无数据')} 个</strong></p>
|
|
319
|
+
<p class="path">${escapeHtml((changed.task_scope?.files || []).join('、') || '暂无文件清单')}</p>
|
|
320
|
+
<p class="micro">来源:${escapeHtml(changed.task_scope?.source || '未记录')};任务:${escapeHtml((changed.task_scope?.task_ids || []).join('、') || '未记录')};最新记录:${escapeHtml(changed.task_scope?.latest_at || '未记录')}</p>
|
|
321
|
+
${(changed.task_scope?.no_file_change_reasons || []).map(item => `<p class="micro">未改文件说明:${escapeHtml(item.task_id || '未标识任务')}:${escapeHtml(item.reason || item)}</p>`).join('')}
|
|
260
322
|
</article>
|
|
261
323
|
<article class="wide-card">
|
|
262
|
-
<h3
|
|
263
|
-
<
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
</
|
|
268
|
-
<
|
|
324
|
+
<h3>${escapeHtml(changed.final_scope?.label || '最终实际交付的文件')}</h3>
|
|
325
|
+
<p>${escapeHtml(changed.final_scope?.explanation || '')}</p>
|
|
326
|
+
<p><strong>${escapeHtml(changed.final_scope?.count ?? '暂无数据')} 个</strong></p>
|
|
327
|
+
<p class="path">${escapeHtml((changed.final_scope?.files || []).join('、') || '暂无文件清单')}</p>
|
|
328
|
+
<p class="micro">来源:${escapeHtml(changed.final_scope?.source || '未记录')};事件:${escapeHtml(changed.final_scope?.event_id || '未记录')};快照时间:${escapeHtml(changed.final_scope?.captured_at || '未记录')}</p>
|
|
329
|
+
<p class="micro">未产生交付文件的原因:${escapeHtml(changed.final_scope?.no_output_reason || '不适用')}</p>
|
|
330
|
+
<p class="micro">行数统计说明:${escapeHtml(changed.final_scope?.line_statistics_explanation || '未记录')}</p>
|
|
331
|
+
<p class="micro">${escapeHtml(changed.reconciliation_note || '暂无口径说明。')}</p>
|
|
269
332
|
</article>
|
|
270
333
|
</div>
|
|
271
334
|
<article class="wide-card section">
|
|
@@ -312,6 +375,7 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
312
375
|
var drawer=backdrop.querySelector('.drawer');
|
|
313
376
|
var drawerTitle=document.getElementById('drawerTitle');
|
|
314
377
|
var drawerBody=document.getElementById('drawerBody');
|
|
378
|
+
var drawerClose=document.getElementById('drawerClose');
|
|
315
379
|
var lastTrigger=null;
|
|
316
380
|
function closeDrawer(){
|
|
317
381
|
if(!backdrop.classList.contains('open'))return;
|
|
@@ -322,10 +386,10 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
322
386
|
button.addEventListener('click',function(){
|
|
323
387
|
lastTrigger=button;drawerTitle.textContent=button.dataset.drawerTitle||'详情';
|
|
324
388
|
drawerBody.textContent=button.dataset.drawerBody||'暂无详情';
|
|
325
|
-
backdrop.classList.add('open');backdrop.setAttribute('aria-hidden','false');
|
|
389
|
+
backdrop.classList.add('open');backdrop.setAttribute('aria-hidden','false');drawerClose.focus();
|
|
326
390
|
});
|
|
327
391
|
});
|
|
328
|
-
|
|
392
|
+
drawerClose.addEventListener('click',closeDrawer);
|
|
329
393
|
backdrop.addEventListener('click',function(event){if(event.target===backdrop)closeDrawer();});
|
|
330
394
|
document.addEventListener('keydown',function(event){
|
|
331
395
|
if(event.key === 'Escape')closeDrawer();
|
|
@@ -333,7 +397,8 @@ table{width:100%;border-collapse:collapse}th,td{padding:11px 10px;border-bottom:
|
|
|
333
397
|
if(e.key==='Tab'&&backdrop.classList.contains('open')){
|
|
334
398
|
var items=drawer.querySelectorAll('button,[href],[tabindex]:not([tabindex="-1"])');
|
|
335
399
|
var first=items[0],last=items[items.length-1];
|
|
336
|
-
if(
|
|
400
|
+
if(!drawer.contains(document.activeElement)){event.preventDefault();(event.shiftKey?last:first).focus();}
|
|
401
|
+
else if(event.shiftKey&&document.activeElement===first){event.preventDefault();last.focus();}
|
|
337
402
|
else if(!event.shiftKey&&document.activeElement===last){event.preventDefault();first.focus();}
|
|
338
403
|
}
|
|
339
404
|
});
|