paseo-req-kit 1.14.0 → 1.15.0-beta.1
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/CHANGELOG.md +17 -0
- package/docs/guide//347/273/264/346/212/244/350/200/205/346/214/207/345/215/227.md +2 -0
- package/engine/src/review-assets/app.css +153 -234
- package/engine/src/review-assets/app.html +44 -32
- package/engine/src/review-assets/app.js +11 -7
- package/engine/src/review-assets/check.css +11 -56
- package/engine/src/review-assets/check.html +48 -22
- package/engine/src/review-assets/check.js +169 -86
- package/engine/src/review-assets/code.css +15 -92
- package/engine/src/review-assets/code.html +41 -26
- package/engine/src/review-assets/code.js +196 -101
- package/engine/src/review-assets/rk-icons.js +98 -0
- package/engine/src/review-assets/rk-tokens.css +252 -0
- package/engine/src/review-assets/rk.css +252 -0
- package/engine/src/review.ts +6 -4
- package/package.json +1 -1
- package/paseo-plugin/package.json +1 -1
- package/site-dist/404.html +2 -2
- package/site-dist/assets/{app.VQ-QxRPH.js → app.D5s_yGHZ.js} +1 -1
- package/site-dist/assets/changelog.md.RJaBnmnw.js +1 -0
- package/site-dist/assets/chunks/@localSearchIndexroot.Dric-MbI.js +1 -0
- package/site-dist/assets/chunks/{VPLocalSearchBox.CT4Vlj76.js → VPLocalSearchBox.B4CyDsTr.js} +1 -1
- package/site-dist/assets/chunks/{theme.B_UmHHou.js → theme.CCocenmB.js} +2 -2
- package/site-dist/assets/{guide_getting-started.md.CZM53rO_.js → guide_getting-started.md.wFOkpMSx.js} +1 -1
- package/site-dist/assets/guide_maintainer.md.mFZprBj1.js +1 -0
- package/site-dist/assets/{guide_maintainer.md.C1v7EpSY.lean.js → guide_maintainer.md.mFZprBj1.lean.js} +1 -1
- package/site-dist/changelog.html +5 -5
- package/site-dist/guide/getting-started.html +5 -5
- package/site-dist/guide/how-it-works.html +4 -4
- package/site-dist/guide/index.html +4 -4
- package/site-dist/guide/install.html +4 -4
- package/site-dist/guide/maintainer.html +5 -5
- package/site-dist/guide/phases.html +4 -4
- package/site-dist/guide/project.html +4 -4
- package/site-dist/guide/user.html +4 -4
- package/site-dist/hashmap.json +1 -1
- package/site-dist/index.html +4 -4
- package/site-dist/reference/adr/0001-step-agents-under-origin-chat.html +4 -4
- package/site-dist/reference/adr/0002-no-directory-switch-in-chat.html +4 -4
- package/site-dist/reference/adr/0003-script-engine-not-hub.html +4 -4
- package/site-dist/reference/adr/0004-result-files.html +4 -4
- package/site-dist/reference/adr/0005-no-default-branch-local-commits-only.html +4 -4
- package/site-dist/reference/adr/0006-skill-install-targets.html +4 -4
- package/site-dist/reference/adr/0007-own-interview-skill.html +4 -4
- package/site-dist/reference/adr/0008-entries-do-not-call-entries.html +4 -4
- package/site-dist/reference/adr/0009-versioned-install-layout.html +4 -4
- package/site-dist/reference/adr/0010-generated-feature-overview.html +4 -4
- package/site-dist/reference/adr/0011-resume-after-completion.html +4 -4
- package/site-dist/reference/adr/0012-review-panel.html +4 -4
- package/site-dist/reference/adr/0013-no-release-tooling.html +4 -4
- package/site-dist/reference/adr/0014-notifier-slot.html +4 -4
- package/site-dist/reference/adr/0015-incremental-continue.html +4 -4
- package/site-dist/reference/adr/0016-upgrade-from-tags.html +4 -4
- package/site-dist/reference/adr/0017-use-paseo-capabilities.html +4 -4
- package/site-dist/reference/adr/0018-own-worktree-option.html +4 -4
- package/site-dist/reference/adr/0019-parallel-orchestration.html +4 -4
- package/site-dist/reference/adr/0020-scheduling-through-the-chat.html +4 -4
- package/site-dist/reference/adr/0021-npm-version-manager.html +4 -4
- package/site-dist/reference/adr/0022-develop-and-verify-phases.html +4 -4
- package/site-dist/reference/adr/0023-types-gate-slices.html +4 -4
- package/site-dist/reference/adr/0024-confirm-early-review-axes.html +4 -4
- package/site-dist/reference/adr/0025-joint-requirements.html +4 -4
- package/site-dist/reference/adr/0026-machine-records-and-api.html +4 -4
- package/site-dist/reference/adr/0027-paseo-plugin.html +4 -4
- package/site-dist/reference/adr/0028-external-integrations.html +4 -4
- package/site-dist/reference/adr/0029-requirements-from-cards.html +4 -4
- package/site-dist/reference/adr/0030-card-intake-process.html +4 -4
- package/site-dist/reference/adr/0031-fewer-interventions.html +4 -4
- package/site-dist/reference/adr/0032-npm-only-install.html +4 -4
- package/site-dist/reference/adr/0033-prototype-review-and-ui-check.html +4 -4
- package/site-dist/reference/adr/index.html +4 -4
- package/site-dist/reference/commands.html +4 -4
- package/site-dist/reference/config.html +4 -4
- package/site-dist/reference/contracts/cli-json.html +4 -4
- package/site-dist/reference/contracts/feature-files.html +4 -4
- package/site-dist/reference/contracts/index.html +4 -4
- package/site-dist/reference/contracts/notifier-event.html +4 -4
- package/site-dist/reference/contracts/result-file.html +4 -4
- package/site-dist/reference/contracts/spec.html +4 -4
- package/site-dist/reference/contracts/state.html +4 -4
- package/site-dist/reference/contracts/usage-record.html +4 -4
- package/site-dist/reference/contracts/verify.html +4 -4
- package/site-dist/reference/scope.html +4 -4
- package/site-dist/assets/changelog.md.CIKYJXpF.js +0 -1
- package/site-dist/assets/chunks/@localSearchIndexroot.h7oQE_np.js +0 -1
- package/site-dist/assets/guide_maintainer.md.C1v7EpSY.js +0 -1
- /package/site-dist/assets/{changelog.md.CIKYJXpF.lean.js → changelog.md.RJaBnmnw.lean.js} +0 -0
- /package/site-dist/assets/{guide_getting-started.md.CZM53rO_.lean.js → guide_getting-started.md.wFOkpMSx.lean.js} +0 -0
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
// The acceptance checklist page (1.14). Plain script, no framework: the server
|
|
2
|
-
// its JSON API (api/info, api/draft, api/try, api/submit).
|
|
1
|
+
// The acceptance checklist page (1.14; design system 1.15). Plain script, no framework: the server
|
|
2
|
+
// serves it next to its JSON API (api/info, api/draft, api/try, api/submit).
|
|
3
3
|
(function () {
|
|
4
4
|
"use strict";
|
|
5
5
|
var state = { info: null, answers: {}, reviewer: "", saveTimer: null, busy: false };
|
|
6
6
|
var $ = function (id) {
|
|
7
7
|
return document.getElementById(id);
|
|
8
8
|
};
|
|
9
|
+
var icon = function (name, cls) {
|
|
10
|
+
return window.RkIcon ? window.RkIcon(name, cls) : null;
|
|
11
|
+
};
|
|
9
12
|
|
|
10
13
|
function el(tag, attrs, children) {
|
|
11
14
|
var node = document.createElement(tag);
|
|
12
15
|
Object.keys(attrs || {}).forEach(function (k) {
|
|
13
|
-
if (attrs[k] === null || attrs[k] === undefined) return;
|
|
16
|
+
if (attrs[k] === null || attrs[k] === undefined || attrs[k] === false) return;
|
|
14
17
|
if (k === "text") node.textContent = attrs[k];
|
|
15
18
|
else if (k === "className") node.className = attrs[k];
|
|
16
19
|
else if (k.indexOf("on") === 0) node.addEventListener(k.slice(2), attrs[k]);
|
|
@@ -22,6 +25,10 @@
|
|
|
22
25
|
return node;
|
|
23
26
|
}
|
|
24
27
|
|
|
28
|
+
function badge(kind, iconName, text) {
|
|
29
|
+
return el("span", { className: "rk-badge rk-badge--" + kind }, [iconName ? icon(iconName) : null, text]);
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
function toast(text) {
|
|
26
33
|
var t = $("toast");
|
|
27
34
|
t.textContent = text;
|
|
@@ -41,7 +48,14 @@
|
|
|
41
48
|
});
|
|
42
49
|
}
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
// What the pipeline found for each criterion: badge kind, icon, words.
|
|
52
|
+
var PIPELINE = { pass: ["success", "circle-check", "流水线已验证"], fail: ["failed", "circle-x", "流水线没通过"], unverified: ["muted", "circle-dashed", "流水线没验证"], unknown: ["muted", "circle-dashed", "流水线没有记录"] };
|
|
53
|
+
var DETAIL = /^(前置条件|操作|预期结果|预期)[::]\s*(.*)$/;
|
|
54
|
+
|
|
55
|
+
function hhmm() {
|
|
56
|
+
var d = new Date();
|
|
57
|
+
return (d.getHours() < 10 ? "0" : "") + d.getHours() + ":" + (d.getMinutes() < 10 ? "0" : "") + d.getMinutes();
|
|
58
|
+
}
|
|
45
59
|
|
|
46
60
|
function answersList() {
|
|
47
61
|
return Object.keys(state.answers).map(function (id) {
|
|
@@ -53,7 +67,13 @@
|
|
|
53
67
|
if (state.info.readOnly) return;
|
|
54
68
|
clearTimeout(state.saveTimer);
|
|
55
69
|
state.saveTimer = setTimeout(function () {
|
|
56
|
-
api("api/draft", "PUT", { answers: answersList(), reviewer: state.reviewer })
|
|
70
|
+
api("api/draft", "PUT", { answers: answersList(), reviewer: state.reviewer })
|
|
71
|
+
.then(function () {
|
|
72
|
+
$("saved").textContent = "已自动保存 " + hhmm();
|
|
73
|
+
})
|
|
74
|
+
.catch(function () {
|
|
75
|
+
$("saved").textContent = "没保存上,网络恢复后再改一下会重试";
|
|
76
|
+
});
|
|
57
77
|
}, 600);
|
|
58
78
|
}
|
|
59
79
|
|
|
@@ -70,44 +90,55 @@
|
|
|
70
90
|
if (i.required && a && a.mark === "fail") failedRequired++;
|
|
71
91
|
});
|
|
72
92
|
var verdict = failedRequired > 0 ? "不通过" : missing === 0 ? "通过" : null;
|
|
73
|
-
$("summary").textContent =
|
|
74
|
-
|
|
75
|
-
|
|
93
|
+
$("summary").textContent = "已核对 " + (c.pass + c.fail) + " / " + items.length + (c.fail ? " · " + c.fail + " 项不对" : "") + (c.skip ? " · 先不验 " + c.skip : "");
|
|
94
|
+
$("summary-sub").textContent = state.info.readOnly
|
|
95
|
+
? "这是只读链接,只能查看"
|
|
96
|
+
: verdict === "通过"
|
|
97
|
+
? "可以提交:验收通过"
|
|
98
|
+
: verdict === "不通过"
|
|
99
|
+
? "提交后结论是“不通过”,不对的地方会转给需求的对话处理"
|
|
100
|
+
: "还有 " + missing + " 条必须验的没有核对;核对完,或者有不对的标“不对”后才能提交";
|
|
76
101
|
$("submit").disabled = state.info.readOnly || state.busy || verdict === null;
|
|
77
|
-
$("submit").textContent = verdict === "不通过" ? "
|
|
102
|
+
$("submit").lastChild.textContent = verdict === "不通过" ? "提交验收(不通过)" : "提交验收";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function details(item) {
|
|
106
|
+
var rows = [];
|
|
107
|
+
var rest = [];
|
|
108
|
+
item.details.forEach(function (d) {
|
|
109
|
+
var m = DETAIL.exec(d.replace(/^\s*[-*]\s+/, ""));
|
|
110
|
+
if (m) rows.push(el("dt", { text: m[1] === "预期" ? "预期结果" : m[1] }), el("dd", { text: m[2] }));
|
|
111
|
+
else rest.push(el("li", { text: d.replace(/^\s*[-*]\s+/, "") }));
|
|
112
|
+
});
|
|
113
|
+
return [rows.length ? el("dl", {}, rows) : null, rest.length ? el("ul", { className: "ck-details" }, rest) : null];
|
|
78
114
|
}
|
|
79
115
|
|
|
80
|
-
function renderItem(item
|
|
116
|
+
function renderItem(item) {
|
|
81
117
|
var a = state.answers[item.id];
|
|
82
|
-
var li = el("li", { className: "item" + (a ? " " + a.mark : "") });
|
|
83
118
|
var p = PIPELINE[item.pipeline] || PIPELINE.unknown;
|
|
84
|
-
li.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
119
|
+
var li = el("li", { className: "rk-check" + (a && a.mark === "fail" ? " is-fail" : a && a.mark === "pass" ? " is-pass" : ""), id: "item-" + item.id });
|
|
120
|
+
li.appendChild(el("span", { className: "rk-check__id", text: item.id }));
|
|
121
|
+
var main = el("div", { className: "rk-check__main" });
|
|
122
|
+
main.appendChild(
|
|
123
|
+
el("div", { className: "rk-check__title" }, [
|
|
124
|
+
el("span", { text: item.title }),
|
|
125
|
+
item.kind === "criterion" ? badge(p[0], p[1], p[2]) : badge("needs", "circle-dot", "请你表态"),
|
|
126
|
+
item.required ? null : badge("outline", null, "可以以后再说"),
|
|
90
127
|
]),
|
|
91
128
|
);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return el("li", { text: d });
|
|
99
|
-
}),
|
|
100
|
-
),
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
if (item.evidence) li.appendChild(el("div", { className: "evidence", text: "流水线的记录:" + item.evidence }));
|
|
104
|
-
var labels = item.kind === "decision" ? [["pass", "同意"], ["fail", "不同意"], ["skip", "先不表态"]] : [["pass", "通过"], ["fail", "不对"], ["skip", "先不验"]];
|
|
105
|
-
var marks = el(
|
|
129
|
+
details(item).forEach(function (n) {
|
|
130
|
+
if (n) main.appendChild(n);
|
|
131
|
+
});
|
|
132
|
+
if (item.evidence) main.appendChild(el("div", { className: "rk-check__evidence", text: (item.kind === "criterion" ? "流水线的记录:" : "") + item.evidence }));
|
|
133
|
+
var labels = item.kind === "decision" ? [["pass", "同意", "is-pass"], ["fail", "不同意", "is-fix"], ["skip", "先不表态", ""]] : [["pass", "通过", "is-pass"], ["fail", "不对", "is-fix"], ["skip", "先不验", ""]];
|
|
134
|
+
var seg = el(
|
|
106
135
|
"div",
|
|
107
|
-
{ className: "
|
|
136
|
+
{ className: "rk-seg", role: "group", "aria-label": item.kind === "decision" ? "表态" : "核对结果", style: "align-self:flex-start" },
|
|
108
137
|
labels.map(function (l) {
|
|
109
138
|
return el("button", {
|
|
110
|
-
|
|
139
|
+
type: "button",
|
|
140
|
+
className: l[2],
|
|
141
|
+
"aria-pressed": String(!!(a && a.mark === l[0])),
|
|
111
142
|
text: l[1],
|
|
112
143
|
disabled: state.info.readOnly ? "disabled" : null,
|
|
113
144
|
onclick: function () {
|
|
@@ -115,49 +146,94 @@
|
|
|
115
146
|
state.answers[item.id] = { mark: l[0], note: cur ? cur.note : "" };
|
|
116
147
|
save();
|
|
117
148
|
render();
|
|
149
|
+
if (l[0] === "fail") setTimeout(function () {
|
|
150
|
+
var t = document.querySelector("#item-" + CSS.escape(item.id) + " textarea");
|
|
151
|
+
if (t && !t.value) t.focus();
|
|
152
|
+
});
|
|
118
153
|
},
|
|
119
154
|
});
|
|
120
155
|
}),
|
|
121
156
|
);
|
|
122
|
-
|
|
123
|
-
b.disabled = true;
|
|
124
|
-
});
|
|
125
|
-
li.appendChild(marks);
|
|
157
|
+
main.appendChild(seg);
|
|
126
158
|
if (a && a.mark === "fail") {
|
|
127
|
-
var ta = el("textarea", { placeholder: item.kind === "decision" ? "为什么不同意?希望怎样?" : "看到了什么?应该是什么样?(例如:点导出后提示“失败”,应该下载文件)" });
|
|
159
|
+
var ta = el("textarea", { className: "rk-input", rows: "2", "aria-label": "看到了什么", placeholder: item.kind === "decision" ? "为什么不同意?希望怎样?" : "看到了什么?应该是什么样?(例如:点导出后提示“失败”,应该下载文件)" });
|
|
128
160
|
ta.value = a.note || "";
|
|
161
|
+
ta.disabled = !!state.info.readOnly;
|
|
129
162
|
ta.addEventListener("input", function () {
|
|
130
163
|
state.answers[item.id].note = ta.value;
|
|
131
164
|
save();
|
|
132
165
|
});
|
|
133
|
-
|
|
166
|
+
main.appendChild(ta);
|
|
134
167
|
}
|
|
168
|
+
li.appendChild(main);
|
|
135
169
|
return li;
|
|
136
170
|
}
|
|
137
171
|
|
|
172
|
+
function navIcon(item) {
|
|
173
|
+
var a = state.answers[item.id];
|
|
174
|
+
if (a && a.mark === "pass") return ["circle-check", "var(--rk-status-success)"];
|
|
175
|
+
if (a && a.mark === "fail") return ["circle-x", "var(--rk-status-failed)"];
|
|
176
|
+
if (item.kind === "decision") return ["circle-dot", "var(--rk-brand-text)"];
|
|
177
|
+
if (item.pipeline === "fail") return ["circle-x", "var(--rk-status-failed)"];
|
|
178
|
+
if (item.pipeline === "pass") return ["circle-check", "var(--rk-text-muted)"];
|
|
179
|
+
return ["circle-dashed", "var(--rk-text-muted)"];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function renderNav() {
|
|
183
|
+
var nav = $("nav");
|
|
184
|
+
nav.textContent = "";
|
|
185
|
+
var first = state.info.items.filter(function (i) {
|
|
186
|
+
return i.kind === "decision" || i.pipeline !== "pass";
|
|
187
|
+
});
|
|
188
|
+
var rest = state.info.items.filter(function (i) {
|
|
189
|
+
return first.indexOf(i) < 0;
|
|
190
|
+
});
|
|
191
|
+
[["先看这些", first], ["流水线已验证", rest]].forEach(function (g) {
|
|
192
|
+
if (!g[1].length) return;
|
|
193
|
+
nav.appendChild(el("div", { className: "rk-side__group", text: g[0] }));
|
|
194
|
+
g[1].forEach(function (item) {
|
|
195
|
+
var ic = navIcon(item);
|
|
196
|
+
var svg = icon(ic[0]);
|
|
197
|
+
if (svg) svg.style.color = ic[1];
|
|
198
|
+
nav.appendChild(
|
|
199
|
+
el("a", { href: "#item-" + item.id, onclick: function (e) {
|
|
200
|
+
e.preventDefault();
|
|
201
|
+
var t = $("item-" + item.id);
|
|
202
|
+
if (t) t.scrollIntoView({ block: "start", behavior: "smooth" });
|
|
203
|
+
} }, [svg, el("span", { text: item.id + " " + item.title })]),
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
138
209
|
function render() {
|
|
139
210
|
var list = $("items");
|
|
140
|
-
list.
|
|
141
|
-
state.info.items.
|
|
142
|
-
list.appendChild(
|
|
211
|
+
list.textContent = "";
|
|
212
|
+
if (!state.info.items.length) {
|
|
213
|
+
list.appendChild(el("li", { className: "rk-card rk-card--sm" }, [el("div", { className: "rk-empty" }, [icon("clipboard-check", "ic ic-xl"), el("h3", { text: "没有找到验收标准" }), el("p", { text: "需求说明里没有 AC 编号的验收标准。请在需求的对话里补上,或者按完成报告验收。" })])]));
|
|
214
|
+
}
|
|
215
|
+
state.info.items.forEach(function (item) {
|
|
216
|
+
list.appendChild(renderItem(item));
|
|
143
217
|
});
|
|
218
|
+
renderNav();
|
|
144
219
|
summary();
|
|
145
220
|
}
|
|
146
221
|
|
|
147
222
|
function showTry(result) {
|
|
148
223
|
var box = $("try-box");
|
|
149
|
-
box.
|
|
150
|
-
|
|
151
|
-
box.appendChild(el("h2", { text: result.ok ? "已经跑起来了" : "没能一键跑起来" }));
|
|
152
|
-
box.appendChild(el("div", { text: (result.message || "").split("\n")[0] }));
|
|
224
|
+
box.textContent = "";
|
|
225
|
+
$("try-state").textContent = "";
|
|
153
226
|
var running = (result.services || []).filter(function (s) {
|
|
154
227
|
return s.running && s.url;
|
|
155
228
|
});
|
|
229
|
+
$("try-state").appendChild(result.ok && running.length ? badge("running", null, "正在运行") : badge(result.ok ? "success" : "warning", result.ok ? "circle-check" : "triangle-alert", result.ok ? "已启动" : "没能一键跑起来"));
|
|
230
|
+
if (result.ok && running.length) $("try-state").firstChild.insertBefore(el("span", { className: "rk-spin" }), $("try-state").firstChild.firstChild);
|
|
231
|
+
box.appendChild(el("p", { text: (result.message || "").split("\n")[0] }));
|
|
156
232
|
if (running.length) {
|
|
157
233
|
box.appendChild(
|
|
158
234
|
el(
|
|
159
235
|
"ul",
|
|
160
|
-
{ className: "services" },
|
|
236
|
+
{ className: "ck-services" },
|
|
161
237
|
running.map(function (s) {
|
|
162
238
|
return el("li", {}, [s.name + ":", el("a", { href: s.url, target: "_blank", rel: "noopener", text: s.url })]);
|
|
163
239
|
}),
|
|
@@ -165,19 +241,20 @@
|
|
|
165
241
|
);
|
|
166
242
|
$("try-stop").hidden = false;
|
|
167
243
|
}
|
|
168
|
-
if (result.howTo && result.howTo.length) box.appendChild(el("pre", { className: "howto", text: result.howTo.join("\n") }));
|
|
244
|
+
if (result.howTo && result.howTo.length) box.appendChild(el("pre", { className: "ck-howto", text: result.howTo.join("\n") }));
|
|
169
245
|
}
|
|
170
246
|
|
|
171
247
|
function tryIt(stop) {
|
|
172
248
|
var button = stop ? $("try-stop") : $("try");
|
|
249
|
+
var label = stop ? button : button.lastChild;
|
|
173
250
|
button.disabled = true;
|
|
174
|
-
|
|
251
|
+
label.textContent = stop ? "正在停止…" : "正在启动,可能要一两分钟…";
|
|
175
252
|
api("api/try", "POST", { stop: !!stop })
|
|
176
253
|
.then(function (r) {
|
|
177
254
|
if (stop) {
|
|
178
255
|
toast(r.message);
|
|
179
256
|
$("try-stop").hidden = true;
|
|
180
|
-
$("try-
|
|
257
|
+
$("try-state").textContent = "";
|
|
181
258
|
} else showTry(r);
|
|
182
259
|
})
|
|
183
260
|
.catch(function (e) {
|
|
@@ -185,7 +262,7 @@
|
|
|
185
262
|
})
|
|
186
263
|
.then(function () {
|
|
187
264
|
button.disabled = false;
|
|
188
|
-
|
|
265
|
+
label.textContent = stop ? "停止试用" : "把它跑起来试用";
|
|
189
266
|
});
|
|
190
267
|
}
|
|
191
268
|
|
|
@@ -195,8 +272,9 @@
|
|
|
195
272
|
api("api/submit", "POST", { answers: answersList(), reviewer: state.reviewer })
|
|
196
273
|
.then(function (r) {
|
|
197
274
|
toast(r.message);
|
|
198
|
-
var
|
|
199
|
-
|
|
275
|
+
var ok = r.verdict === "通过";
|
|
276
|
+
var done = el("div", { className: "rk-banner " + (ok ? "rk-banner--ok" : "rk-banner--warn") }, [icon(ok ? "circle-check" : "undo-2"), el("span", { text: "已提交:验收" + r.verdict + "。" + r.message })]);
|
|
277
|
+
$("main").insertBefore(done, $("main").firstChild);
|
|
200
278
|
window.scrollTo(0, 0);
|
|
201
279
|
})
|
|
202
280
|
.catch(function (e) {
|
|
@@ -207,40 +285,45 @@
|
|
|
207
285
|
}
|
|
208
286
|
|
|
209
287
|
function start() {
|
|
210
|
-
api("api/info")
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
288
|
+
api("api/info")
|
|
289
|
+
.then(function (info) {
|
|
290
|
+
state.info = info;
|
|
291
|
+
document.title = "验收清单 · " + info.title;
|
|
292
|
+
$("title").textContent = info.title;
|
|
293
|
+
$("project").textContent = info.featureDir;
|
|
294
|
+
$("project").hidden = false;
|
|
295
|
+
$("readonly").hidden = !info.readOnly;
|
|
296
|
+
$("sub").textContent = [info.reportVerdict ? "流水线结论:" + info.reportVerdict : null, info.accepted ? "上次验收:" + info.accepted : null].filter(Boolean).join(" ");
|
|
297
|
+
(info.draft && info.draft.answers ? info.draft.answers : []).forEach(function (a) {
|
|
298
|
+
state.answers[a.id] = { mark: a.mark, note: a.note || "" };
|
|
299
|
+
});
|
|
300
|
+
state.reviewer = (info.draft && info.draft.reviewer) || "";
|
|
301
|
+
$("reviewer").value = state.reviewer;
|
|
302
|
+
$("reviewer").disabled = !!info.readOnly;
|
|
303
|
+
if (info.notes && info.notes.length) {
|
|
304
|
+
var n = $("notes");
|
|
305
|
+
n.hidden = false;
|
|
306
|
+
n.appendChild(icon("info"));
|
|
307
|
+
n.appendChild(
|
|
308
|
+
el("div", {}, [
|
|
309
|
+
el("b", { text: "先看一下(来自提测说明)" }),
|
|
310
|
+
el(
|
|
311
|
+
"ul",
|
|
312
|
+
{},
|
|
313
|
+
info.notes.map(function (t) {
|
|
314
|
+
return el("li", { text: t });
|
|
315
|
+
}),
|
|
316
|
+
),
|
|
317
|
+
]),
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
if (info.readOnly || info.running) $("try-card").hidden = true;
|
|
321
|
+
render();
|
|
322
|
+
})
|
|
323
|
+
.catch(function (e) {
|
|
324
|
+
$("title").textContent = "打不开验收清单";
|
|
325
|
+
$("sub").textContent = e.message;
|
|
220
326
|
});
|
|
221
|
-
state.reviewer = (info.draft && info.draft.reviewer) || "";
|
|
222
|
-
$("reviewer").value = state.reviewer;
|
|
223
|
-
if (info.notes && info.notes.length) {
|
|
224
|
-
var n = $("notes");
|
|
225
|
-
n.hidden = false;
|
|
226
|
-
n.appendChild(el("h2", { text: "先看一下(来自提测说明)" }));
|
|
227
|
-
n.appendChild(
|
|
228
|
-
el(
|
|
229
|
-
"ul",
|
|
230
|
-
{},
|
|
231
|
-
info.notes.map(function (t) {
|
|
232
|
-
return el("li", { text: t });
|
|
233
|
-
}),
|
|
234
|
-
),
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
if (info.readOnly || info.running) $("try").hidden = true;
|
|
238
|
-
if (!info.items.length) $("items").appendChild(el("li", { className: "item", text: "需求说明里没有找到验收标准(AC-编号)。" }));
|
|
239
|
-
render();
|
|
240
|
-
}).catch(function (e) {
|
|
241
|
-
$("title").textContent = "打不开验收清单";
|
|
242
|
-
$("sub").textContent = e.message;
|
|
243
|
-
});
|
|
244
327
|
$("reviewer").addEventListener("input", function (e) {
|
|
245
328
|
state.reviewer = e.target.value;
|
|
246
329
|
save();
|
|
@@ -1,93 +1,16 @@
|
|
|
1
|
-
:
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
[hidden] { display: none !important; }
|
|
17
|
-
body { margin: 0; background: var(--bg); color: var(--text); font: 15px/1.6 system-ui, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; padding-bottom: 110px; }
|
|
18
|
-
.top { padding: 20px 24px 8px; }
|
|
19
|
-
.eyebrow { font-size: 13px; color: var(--muted); }
|
|
20
|
-
h1 { margin: 2px 0 4px; font-size: 22px; }
|
|
21
|
-
h2 { font-size: 15px; margin: 0 0 6px; }
|
|
22
|
-
.muted { color: var(--muted); }
|
|
23
|
-
.small { font-size: 13px; }
|
|
24
|
-
.pad { padding: 8px 12px; }
|
|
25
|
-
.banner { margin: 8px 24px; padding: 10px 14px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--border); }
|
|
26
|
-
.banner.ok { background: var(--ok-soft); border-color: var(--ok); }
|
|
27
|
-
.banner.warn { background: var(--warn-soft); border-color: var(--warn); }
|
|
28
|
-
.layout { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: 16px; padding: 0 24px; }
|
|
29
|
-
.files { position: sticky; top: 8px; align-self: start; max-height: calc(100vh - 140px); overflow: auto; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 10px; font-size: 13px; }
|
|
30
|
-
.nav-title { font-weight: 600; margin-bottom: 6px; }
|
|
31
|
-
.files a { display: flex; justify-content: space-between; gap: 6px; padding: 4px 6px; border-radius: 6px; color: var(--text); text-decoration: none; word-break: break-all; }
|
|
32
|
-
.files a:hover { background: var(--surface-2); }
|
|
33
|
-
.files a.doc { color: var(--muted); }
|
|
34
|
-
main { min-width: 0; }
|
|
35
|
-
.intro { margin: 8px 0 12px; }
|
|
36
|
-
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 14px 16px; margin: 0 0 12px; }
|
|
37
|
-
.badge { font-size: 12px; padding: 1px 8px; border-radius: 999px; background: var(--surface-2); color: var(--muted); white-space: nowrap; }
|
|
38
|
-
.badge.danger { background: var(--danger-soft); color: var(--danger); }
|
|
39
|
-
.badge.warn { background: var(--warn-soft); color: var(--warn); }
|
|
40
|
-
.badge.ok { background: var(--ok-soft); color: var(--ok); }
|
|
41
|
-
.file { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; margin-bottom: 12px; overflow: hidden; }
|
|
42
|
-
.file-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 8px 12px; background: var(--surface-2); cursor: pointer; border-bottom: 1px solid var(--border); }
|
|
43
|
-
.file.folded .file-body { display: none; }
|
|
44
|
-
.file.folded .file-head { border-bottom: 0; }
|
|
45
|
-
.path { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; font-weight: 600; word-break: break-all; }
|
|
46
|
-
.plus { color: var(--ok); font-size: 13px; }
|
|
47
|
-
.minus { color: var(--danger); font-size: 13px; }
|
|
48
|
-
.file-body { overflow-x: auto; }
|
|
49
|
-
table.diff { border-collapse: collapse; width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; line-height: 1.5; }
|
|
50
|
-
.diff td { padding: 0 8px; vertical-align: top; }
|
|
51
|
-
.diff .num { width: 1%; min-width: 44px; text-align: right; color: var(--muted); user-select: none; }
|
|
52
|
-
.diff .code { white-space: pre; }
|
|
53
|
-
.diff .hunk td { color: var(--muted); background: var(--surface-2); padding: 2px 8px; }
|
|
54
|
-
.diff .line { cursor: pointer; }
|
|
55
|
-
.diff .line:hover td { outline: 1px solid var(--border); }
|
|
56
|
-
.diff .add td { background: var(--add); }
|
|
57
|
-
.diff .del td { background: var(--del); }
|
|
58
|
-
.diff .flash td, .flash { background: var(--flash) !important; }
|
|
59
|
-
.finding { border: 1px solid var(--border); border-left: 4px solid var(--border); border-radius: 8px; padding: 8px 10px; margin: 6px 0; background: var(--surface); font-family: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; font-size: 14px; white-space: normal; }
|
|
60
|
-
.finding.danger { border-left-color: var(--danger); }
|
|
61
|
-
.finding.warn { border-left-color: var(--warn); }
|
|
62
|
-
.finding.decided-ok { opacity: 0.6; }
|
|
63
|
-
.finding-row td, .comment-row td { padding: 4px 12px 8px; }
|
|
64
|
-
.finding-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
65
|
-
.finding-text { margin: 4px 0; }
|
|
66
|
-
.where { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; color: var(--accent); }
|
|
67
|
-
.marks { display: flex; gap: 8px; margin-top: 6px; }
|
|
68
|
-
.comment { border: 1px solid var(--accent); border-radius: 8px; padding: 8px 10px; background: var(--surface); font-family: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
|
|
69
|
-
textarea, input { font: inherit; color: inherit; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 6px 8px; width: 100%; }
|
|
70
|
-
.note-input { margin-top: 6px; }
|
|
71
|
-
.btn { font: inherit; border: 1px solid var(--border); background: var(--surface); color: var(--text); border-radius: 8px; padding: 6px 14px; cursor: pointer; }
|
|
72
|
-
.btn.small { padding: 2px 10px; font-size: 13px; }
|
|
73
|
-
.btn.on-fix { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
|
|
74
|
-
.btn.on-ok { background: var(--ok-soft); border-color: var(--ok); color: var(--ok); }
|
|
75
|
-
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
|
|
76
|
-
.btn.danger { border-color: var(--danger); color: var(--danger); }
|
|
77
|
-
.btn:disabled { opacity: 0.5; cursor: default; }
|
|
78
|
-
.bar { position: fixed; left: 0; right: 0; bottom: 0; display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center; padding: 10px 24px; background: var(--surface); border-top: 1px solid var(--border); }
|
|
79
|
-
.bar .summary { flex: 1 1 240px; font-size: 13px; }
|
|
80
|
-
.field { display: flex; align-items: center; gap: 6px; font-size: 13px; white-space: nowrap; }
|
|
81
|
-
.field input { width: 200px; }
|
|
82
|
-
.actions { display: flex; gap: 8px; }
|
|
83
|
-
.toast { position: fixed; left: 50%; bottom: 90px; transform: translateX(-50%); max-width: min(640px, 92vw); background: var(--text); color: var(--bg); padding: 10px 14px; border-radius: 10px; box-shadow: 0 6px 24px rgba(0,0,0,.2); white-space: pre-line; }
|
|
84
|
-
@media (max-width: 860px) {
|
|
85
|
-
.layout { grid-template-columns: minmax(0, 1fr); padding: 0 12px; }
|
|
86
|
-
.files { position: static; max-height: 220px; }
|
|
87
|
-
.top { padding: 16px 12px 6px; }
|
|
88
|
-
.banner { margin: 8px 12px; }
|
|
89
|
-
.bar { padding: 10px 12px; }
|
|
90
|
-
.field { flex: 1 1 100%; }
|
|
91
|
-
.field input { width: 100%; }
|
|
92
|
-
body { padding-bottom: 220px; }
|
|
1
|
+
/* The code review page (1.15): only what the design system (rk.css) does not cover. */
|
|
2
|
+
#reviewers { width: 200px; }
|
|
3
|
+
#reviewer { width: 130px; }
|
|
4
|
+
.cr-files-n { font: 12px/1 var(--rk-font-mono); color: var(--rk-text-muted); }
|
|
5
|
+
.cr-files-n.has-ai { color: var(--rk-ai); }
|
|
6
|
+
.rk-side a.is-doc span:first-of-type { color: var(--rk-text-muted); }
|
|
7
|
+
.rk-finding .rk-input { height: 32px; }
|
|
8
|
+
.rk-finding textarea.rk-input { height: auto; width: 100%; }
|
|
9
|
+
.rk-finding a.rk-finding__where { text-decoration: none; }
|
|
10
|
+
.rk-finding a.rk-finding__where:hover { text-decoration: underline; }
|
|
11
|
+
.rk-finding.is-ok { opacity: 0.7; }
|
|
12
|
+
.cr-summary li::marker { color: var(--rk-status-success); }
|
|
13
|
+
.cr-summary { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 6px; color: var(--rk-text-secondary); }
|
|
14
|
+
@media (max-width: 640px) {
|
|
15
|
+
#reviewers, #reviewer { width: 100%; }
|
|
93
16
|
}
|
|
@@ -2,42 +2,57 @@
|
|
|
2
2
|
<html lang="zh-CN">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
6
6
|
<title>代码审查</title>
|
|
7
|
+
<link rel="stylesheet" href="rk-tokens.css">
|
|
8
|
+
<link rel="stylesheet" href="rk.css">
|
|
7
9
|
<link rel="stylesheet" href="code.css">
|
|
10
|
+
<script src="rk-icons.js"></script>
|
|
8
11
|
</head>
|
|
9
12
|
<body>
|
|
10
|
-
<header class="top">
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
<header class="rk-top">
|
|
14
|
+
<span class="rk-brand"><svg data-logo style="--ground:var(--rk-surface-0)"></svg><span>req<i>-</i>kit</span></span>
|
|
15
|
+
<span class="rk-top__sep rk-top__hide-sm"></span>
|
|
16
|
+
<span class="rk-top__title" id="title">代码审查</span>
|
|
17
|
+
<span class="rk-code" id="project" hidden></span>
|
|
18
|
+
<span class="rk-top__spacer"></span>
|
|
19
|
+
<span class="rk-cap rk-top__hide-sm" id="saved"></span>
|
|
20
|
+
<span class="rk-badge rk-badge--outline" id="readonly" hidden><svg class="ic" data-i="lock"></svg>只读</span>
|
|
21
|
+
<button class="rk-btn rk-btn--ghost rk-btn--sm" type="button" data-rk-theme title="切换深浅色" aria-label="切换深浅色"><svg class="ic" data-i="sun"></svg></button>
|
|
16
22
|
</header>
|
|
17
|
-
|
|
18
|
-
<div class="layout">
|
|
19
|
-
<nav class="
|
|
20
|
-
<main>
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<
|
|
27
|
-
|
|
23
|
+
|
|
24
|
+
<div class="rk-layout">
|
|
25
|
+
<nav class="rk-side" id="files" aria-label="改动的文件"></nav>
|
|
26
|
+
<main class="rk-main">
|
|
27
|
+
<div class="rk-banner" id="status" hidden></div>
|
|
28
|
+
<div class="rk-head">
|
|
29
|
+
<span class="rk-eyebrow">Code review</span>
|
|
30
|
+
<h1>看一下这次的改动,决定退回修改还是评审通过</h1>
|
|
31
|
+
<p id="sub"></p>
|
|
32
|
+
<p class="rk-cap">点代码行写意见(只对这一行);AI 的意见可以标"要改"或"不用改"。退回后流水线会按意见修改,改完再评审、再验证。进度会自动保存。</p>
|
|
33
|
+
</div>
|
|
34
|
+
<section class="rk-card rk-card--sm" id="summary" hidden></section>
|
|
35
|
+
<section class="rk-card rk-card--sm" id="ai" hidden></section>
|
|
36
|
+
<div class="rk-col" id="diffs"></div>
|
|
37
|
+
<section class="rk-card rk-card--sm">
|
|
38
|
+
<label class="rk-field" for="note">总体意见(可不填)</label>
|
|
39
|
+
<textarea class="rk-input" id="note" rows="3" placeholder="例如:整体可以,但导出接口要加权限校验"></textarea>
|
|
28
40
|
</section>
|
|
29
41
|
</main>
|
|
30
42
|
</div>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
<
|
|
37
|
-
<
|
|
43
|
+
|
|
44
|
+
<footer class="rk-decide">
|
|
45
|
+
<div class="rk-decide__in">
|
|
46
|
+
<div class="rk-decide__sum"><b id="count"></b><span class="rk-cap" id="countsub"></span></div>
|
|
47
|
+
<label class="rk-field">审核人<input class="rk-input" id="reviewers" placeholder="@用户名,用逗号分开"></label>
|
|
48
|
+
<label class="rk-field">评审人<input class="rk-input" id="reviewer" placeholder="你的名字" maxlength="60"></label>
|
|
49
|
+
<div class="rk-decide__acts">
|
|
50
|
+
<button class="rk-btn rk-btn--danger-outline" id="changes" type="button" disabled><svg class="ic" data-i="undo-2"></svg><span>退回修改</span></button>
|
|
51
|
+
<button class="rk-btn rk-btn--primary" id="approve" type="button" disabled><svg class="ic" data-i="check"></svg><span>评审通过</span></button>
|
|
52
|
+
</div>
|
|
38
53
|
</div>
|
|
39
54
|
</footer>
|
|
40
|
-
<div
|
|
55
|
+
<div class="rk-toast" id="toast" role="status" hidden></div>
|
|
41
56
|
<script src="code.js"></script>
|
|
42
57
|
</body>
|
|
43
58
|
</html>
|