R107/A1-A6 修复网页视觉巡检问题
这个提交包含在:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generatedAt": "2026-05-07T11:36:29.101Z",
|
||||
"generatedAt": "2026-05-07T12:03:41.249Z",
|
||||
"checks": [
|
||||
{
|
||||
"label": "PC Portal",
|
||||
@@ -37,16 +37,16 @@
|
||||
{
|
||||
"label": "PC 报销详情",
|
||||
"type": "button-compare",
|
||||
"status": "diff",
|
||||
"status": "same-layout",
|
||||
"labelA": "明细编辑",
|
||||
"labelB": "明细删除",
|
||||
"data": {
|
||||
"a": {
|
||||
"text": "编辑",
|
||||
"rect": {
|
||||
"width": 50,
|
||||
"width": 64,
|
||||
"height": 40,
|
||||
"x": 853,
|
||||
"x": 805,
|
||||
"y": 1060
|
||||
},
|
||||
"style": {
|
||||
@@ -62,10 +62,10 @@
|
||||
"b": {
|
||||
"text": "删除",
|
||||
"rect": {
|
||||
"width": 50,
|
||||
"width": 64,
|
||||
"height": 40,
|
||||
"x": 853,
|
||||
"y": 1108
|
||||
"x": 877,
|
||||
"y": 1060
|
||||
},
|
||||
"style": {
|
||||
"backgroundColor": "rgba(255, 255, 255, 0.9)",
|
||||
@@ -133,7 +133,7 @@
|
||||
"height": 960,
|
||||
"scrollWidth": 1440,
|
||||
"tableCount": 1,
|
||||
"uuidCount": 1
|
||||
"uuidCount": 0
|
||||
},
|
||||
{
|
||||
"label": "PC 请假",
|
||||
@@ -235,55 +235,7 @@
|
||||
"uuidCount": 0
|
||||
}
|
||||
],
|
||||
"findings": [
|
||||
{
|
||||
"severity": "P2",
|
||||
"page": "PC 报销详情",
|
||||
"category": "文案",
|
||||
"title": "页面仍有偏解释型/啰嗦文案",
|
||||
"detail": "可在当前页面内直接绑定已有发票,或上传新发票后等待 OCR 完成。",
|
||||
"evidence": "/home/shunli/Projects/hyb-oa/output/playwright/r106/pc-reimbursement-detail.png"
|
||||
},
|
||||
{
|
||||
"severity": "P2",
|
||||
"page": "PC 报销详情",
|
||||
"category": "按钮一致性",
|
||||
"title": "明细编辑 与 明细删除 视觉规格不一致",
|
||||
"detail": "纵向位置 1060/1108"
|
||||
},
|
||||
{
|
||||
"severity": "P1",
|
||||
"page": "PC 票据中心",
|
||||
"category": "业务展示",
|
||||
"title": "页面向用户暴露内部 UUID",
|
||||
"detail": "1 个 UUID",
|
||||
"evidence": "/home/shunli/Projects/hyb-oa/output/playwright/r106/pc-invoice-pool.png"
|
||||
},
|
||||
{
|
||||
"severity": "P2",
|
||||
"page": "PC 票据中心",
|
||||
"category": "业务展示",
|
||||
"title": "票据详情仍把内部票据 ID 放在主信息区",
|
||||
"detail": "票据详情存在“票据 ID”字段,应改为票据号码/销售方/金额等业务信息。",
|
||||
"evidence": "/home/shunli/Projects/hyb-oa/output/playwright/r106/pc-invoice-pool.png"
|
||||
},
|
||||
{
|
||||
"severity": "P2",
|
||||
"page": "PC 票据中心",
|
||||
"category": "按钮样式",
|
||||
"title": "危险/终态操作出现主色背景叠红色文字",
|
||||
"detail": "[{\"text\":\"手工归档\",\"testid\":\"invoice-archive\",\"color\":\"rgb(220, 38, 38)\",\"backgroundColor\":\"rgb(0, 57, 138)\",\"visible\":true,\"redTextOnPrimary\":true}]",
|
||||
"evidence": "/home/shunli/Projects/hyb-oa/output/playwright/r106/pc-invoice-pool.png"
|
||||
},
|
||||
{
|
||||
"severity": "P2",
|
||||
"page": "PC 差旅",
|
||||
"category": "视觉",
|
||||
"title": "存在疑似文字/控件裁切",
|
||||
"detail": "[{\"tag\":\"input\",\"testid\":\"\",\"text\":\"\",\"client\":\"189x42\",\"scroll\":\"224x42\"},{\"tag\":\"input\",\"testid\":\"\",\"text\":\"\",\"client\":\"189x42\",\"scroll\":\"224x42\"}]",
|
||||
"evidence": "/home/shunli/Projects/hyb-oa/output/playwright/r106/pc-travel.png"
|
||||
}
|
||||
],
|
||||
"findings": [],
|
||||
"seedData": {
|
||||
"reimbursementId": "[redacted-local-id]",
|
||||
"reimbursementItemId": "[redacted-local-id]",
|
||||
|
||||
@@ -1,34 +1,18 @@
|
||||
# R106 自动视觉巡检报告
|
||||
|
||||
生成时间:2026-05-07T11:36:29.101Z
|
||||
生成时间:2026-05-07T12:03:41.249Z
|
||||
|
||||
## 结论
|
||||
|
||||
- 巡检记录数:15
|
||||
- 发现问题数:6
|
||||
- 发现问题数:0
|
||||
- 截图目录:/home/shunli/Projects/hyb-oa/output/playwright/r106
|
||||
|
||||
## 发现的问题
|
||||
|
||||
1. [P2] PC 报销详情 / 文案:页面仍有偏解释型/啰嗦文案
|
||||
- 详情:可在当前页面内直接绑定已有发票,或上传新发票后等待 OCR 完成。
|
||||
- 截图:/home/shunli/Projects/hyb-oa/output/playwright/r106/pc-reimbursement-detail.png
|
||||
2. [P2] PC 报销详情 / 按钮一致性:明细编辑 与 明细删除 视觉规格不一致
|
||||
- 详情:纵向位置 1060/1108
|
||||
3. [P1] PC 票据中心 / 业务展示:页面向用户暴露内部 UUID
|
||||
- 详情:1 个 UUID
|
||||
- 截图:/home/shunli/Projects/hyb-oa/output/playwright/r106/pc-invoice-pool.png
|
||||
4. [P2] PC 票据中心 / 业务展示:票据详情仍把内部票据 ID 放在主信息区
|
||||
- 详情:票据详情存在“票据 ID”字段,应改为票据号码/销售方/金额等业务信息。
|
||||
- 截图:/home/shunli/Projects/hyb-oa/output/playwright/r106/pc-invoice-pool.png
|
||||
5. [P2] PC 票据中心 / 按钮样式:危险/终态操作出现主色背景叠红色文字
|
||||
- 详情:[{"text":"手工归档","testid":"invoice-archive","color":"rgb(220, 38, 38)","backgroundColor":"rgb(0, 57, 138)","visible":true,"redTextOnPrimary":true}]
|
||||
- 截图:/home/shunli/Projects/hyb-oa/output/playwright/r106/pc-invoice-pool.png
|
||||
6. [P2] PC 差旅 / 视觉:存在疑似文字/控件裁切
|
||||
- 详情:[{"tag":"input","testid":"","text":"","client":"189x42","scroll":"224x42"},{"tag":"input","testid":"","text":"","client":"189x42","scroll":"224x42"}]
|
||||
- 截图:/home/shunli/Projects/hyb-oa/output/playwright/r106/pc-travel.png
|
||||
- 未发现规则命中的视觉/文案/展示问题。
|
||||
|
||||
## 按钮对比
|
||||
|
||||
- PC 报销详情:明细编辑 vs 明细删除 => diff
|
||||
- PC 报销详情:明细编辑 vs 明细删除 => same-layout
|
||||
- PC 报销详情:返回列表 vs 删除草稿 => same-layout
|
||||
@@ -1034,7 +1034,7 @@ export function ReimbursementDetailPage() {
|
||||
<TableHead>发生日期</TableHead>
|
||||
<TableHead>金额</TableHead>
|
||||
<TableHead>说明</TableHead>
|
||||
<TableHead className="w-32">操作</TableHead>
|
||||
<TableHead className="w-44">操作</TableHead>
|
||||
</tr>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -1087,9 +1087,10 @@ export function ReimbursementDetailPage() {
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{canEditItems ? (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<div className="flex min-w-36 items-center gap-2">
|
||||
<Button
|
||||
data-testid="reimbursement-item-edit"
|
||||
className="min-w-16"
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
@@ -1099,6 +1100,7 @@ export function ReimbursementDetailPage() {
|
||||
</Button>
|
||||
<Button
|
||||
data-testid="reimbursement-item-delete"
|
||||
className="min-w-16"
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
@@ -1142,7 +1144,6 @@ export function ReimbursementDetailPage() {
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-1">
|
||||
<h3 className="text-base font-semibold text-zinc-900">关联票据</h3>
|
||||
<p className="text-xs text-zinc-500">可在当前页面内直接绑定已有发票,或上传新发票后等待 OCR 完成。</p>
|
||||
</div>
|
||||
{canOpenInvoiceBindDialog ? (
|
||||
<Button
|
||||
|
||||
@@ -1072,7 +1072,7 @@ function TravelRequestForm(props: TravelRequestFormProps) {
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<Label>角色</Label>
|
||||
<select
|
||||
|
||||
@@ -45,6 +45,18 @@ export type InvoiceDetailPanelProps = {
|
||||
setConfirmAction: (action: { type: string; invoiceId: string } | null) => void;
|
||||
};
|
||||
|
||||
function invoiceStatusLabel(status: string) {
|
||||
const labels: Record<string, string> = {
|
||||
pending_ocr: "识别中",
|
||||
available: "可用",
|
||||
bound: "已绑定",
|
||||
archived: "已归档",
|
||||
ocr_failed: "识别失败",
|
||||
duplicate_suspected: "疑似重复",
|
||||
};
|
||||
return labels[status] ?? status;
|
||||
}
|
||||
|
||||
export function InvoiceDetailPanel({
|
||||
detail,
|
||||
canEditDetail,
|
||||
@@ -84,6 +96,17 @@ export function InvoiceDetailPanel({
|
||||
confirmAction,
|
||||
setConfirmAction,
|
||||
}: InvoiceDetailPanelProps) {
|
||||
const invoiceNo = detail
|
||||
? [detail.invoiceCode, detail.invoiceNumber].filter(Boolean).join("-") || detail.originalName || "待识别"
|
||||
: "";
|
||||
const bindingStatus = detail?.linkedReimbursementId
|
||||
? "已绑定报销单"
|
||||
: detail?.linkedTravelOrderId
|
||||
? "已绑定差旅订单"
|
||||
: detail?.linkedItemId
|
||||
? "已绑定明细"
|
||||
: "未绑定";
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="card-std space-y-4 p-4 md:p-5" data-testid="invoice-detail-panel">
|
||||
@@ -91,12 +114,12 @@ export function InvoiceDetailPanel({
|
||||
{detail ? (
|
||||
<>
|
||||
<div className="grid grid-cols-2 gap-y-3 pt-2 text-sm text-zinc-700">
|
||||
<div><span className="text-zinc-500">票据 ID:</span>{detail.id}</div>
|
||||
<div><span className="text-zinc-500">状态:</span>{detail.status}</div>
|
||||
<div className="col-span-2">
|
||||
<span className="text-zinc-500">发票号:</span>
|
||||
{detail.invoiceCode || "-"}-{detail.invoiceNumber || "-"}
|
||||
<span className="text-zinc-500">发票号码:</span>
|
||||
{invoiceNo}
|
||||
</div>
|
||||
<div><span className="text-zinc-500">状态:</span>{invoiceStatusLabel(detail.status)}</div>
|
||||
<div><span className="text-zinc-500">绑定状态:</span>{bindingStatus}</div>
|
||||
<div><span className="text-zinc-500">销售方:</span>{detail.sellerName || "-"}</div>
|
||||
<div><span className="text-zinc-500">购买方:</span>{detail.buyerName || "-"}</div>
|
||||
<div className="col-span-2">
|
||||
@@ -104,7 +127,7 @@ export function InvoiceDetailPanel({
|
||||
{detail.totalAmount ? new Intl.NumberFormat("zh-CN", { style: "currency", currency: "CNY" }).format(Number(detail.totalAmount)) : "-"}
|
||||
</div>
|
||||
<div><span className="text-zinc-500">开票日期:</span>{detail.invoiceDate || "-"}</div>
|
||||
<div><span className="text-zinc-500">关联报销:</span>{detail.linkedReimbursementId || "-"}</div>
|
||||
<div><span className="text-zinc-500">文件名:</span>{detail.originalName || "-"}</div>
|
||||
<div className="col-span-2 flex flex-col gap-1">
|
||||
<div>
|
||||
<span className="text-zinc-500">验真状态:</span>
|
||||
@@ -144,7 +167,7 @@ export function InvoiceDetailPanel({
|
||||
{canVerifyInvoice ? (
|
||||
<Button
|
||||
data-testid="invoice-trigger-verification"
|
||||
className="btn-secondary"
|
||||
variant="secondary"
|
||||
type="button"
|
||||
disabled={triggerVerificationMutation.isPending || detail.status === "pending_ocr"}
|
||||
onClick={() => triggerVerificationMutation.mutate(detail.id)}
|
||||
@@ -158,7 +181,7 @@ export function InvoiceDetailPanel({
|
||||
["failed", "error"].includes(detail.verification.status) ? (
|
||||
<Button
|
||||
data-testid="invoice-retry-verification"
|
||||
className="btn-secondary"
|
||||
variant="secondary"
|
||||
type="button"
|
||||
disabled={retryVerificationMutation.isPending}
|
||||
onClick={() => retryVerificationMutation.mutate(detail.id)}
|
||||
@@ -170,7 +193,7 @@ export function InvoiceDetailPanel({
|
||||
{canEditDetail ? (
|
||||
<Button
|
||||
data-testid="invoice-edit-open"
|
||||
className="btn-secondary"
|
||||
variant="secondary"
|
||||
type="button"
|
||||
disabled={patchMutation.isPending}
|
||||
onClick={() => setEditOpen(!editOpen)}
|
||||
@@ -182,7 +205,7 @@ export function InvoiceDetailPanel({
|
||||
{canEditDetail && detail.status === "ocr_failed" ? (
|
||||
<Button
|
||||
data-testid="invoice-retry-ocr"
|
||||
className="btn-secondary"
|
||||
variant="secondary"
|
||||
type="button"
|
||||
disabled={retryOcrMutation.isPending}
|
||||
onClick={() => setConfirmAction({ type: "retryOcr", invoiceId: detail.id })}
|
||||
@@ -194,7 +217,7 @@ export function InvoiceDetailPanel({
|
||||
{canEditDetail && canUploadInvoice && detail.status === "bound" ? (
|
||||
<Button
|
||||
data-testid="invoice-unbind"
|
||||
className="btn-secondary"
|
||||
variant="secondary"
|
||||
type="button"
|
||||
disabled={unbindMutation.isPending}
|
||||
onClick={() => setConfirmAction({ type: "unbind", invoiceId: detail.id })}
|
||||
@@ -206,7 +229,7 @@ export function InvoiceDetailPanel({
|
||||
{canEditDetail && detail.status !== "bound" && detail.status !== "archived" ? (
|
||||
<Button
|
||||
data-testid="invoice-archive"
|
||||
className="btn-secondary text-red-600 hover:text-red-700 hover:bg-red-50"
|
||||
variant="destructive"
|
||||
type="button"
|
||||
disabled={archiveMutation.isPending}
|
||||
onClick={() => setConfirmAction({ type: "archive", invoiceId: detail.id })}
|
||||
@@ -220,7 +243,7 @@ export function InvoiceDetailPanel({
|
||||
["available", "ocr_failed"].includes(detail.status) ? (
|
||||
<Button
|
||||
data-testid="invoice-override-duplicate"
|
||||
className="btn-secondary"
|
||||
variant="secondary"
|
||||
type="button"
|
||||
disabled={overrideDuplicateMutation.isPending}
|
||||
onClick={() => {
|
||||
@@ -246,10 +269,10 @@ export function InvoiceDetailPanel({
|
||||
<p className="mb-3 text-sm font-semibold text-zinc-900 border-b border-zinc-200/60 pb-2">验真记录</p>
|
||||
{(verificationQuery.data?.items ?? []).length ? (
|
||||
<div className="space-y-3 text-xs">
|
||||
{(verificationQuery.data?.items ?? []).map((record) => (
|
||||
{(verificationQuery.data?.items ?? []).map((record, index) => (
|
||||
<div key={record.id} className="rounded-md border border-zinc-200 bg-white px-3 py-2 shadow-sm">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2 mb-1">
|
||||
<span className="font-mono text-zinc-500">{record.id}</span>
|
||||
<span className="font-medium text-zinc-700">记录 {index + 1}</span>
|
||||
<span className={`font-semibold ${record.status === "failed" || record.status === "error" ? "text-red-600" : "text-emerald-600"}`}>
|
||||
{record.status}
|
||||
</span>
|
||||
@@ -403,7 +426,6 @@ export function InvoiceDetailPanel({
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
data-testid="invoice-edit-save"
|
||||
className="btn-primary"
|
||||
disabled={patchMutation.isPending}
|
||||
onClick={() => patchMutation.mutate()}
|
||||
type="button"
|
||||
@@ -412,7 +434,7 @@ export function InvoiceDetailPanel({
|
||||
</Button>
|
||||
<Button
|
||||
data-testid="invoice-edit-cancel"
|
||||
className="btn-secondary"
|
||||
variant="secondary"
|
||||
disabled={patchMutation.isPending}
|
||||
onClick={() => setEditOpen(false)}
|
||||
type="button"
|
||||
|
||||
@@ -21,6 +21,11 @@ export type InvoiceListCardProps = {
|
||||
signedUrlMutation: UseMutationResult<{ signedUrl: string; expiresAt: string }, Error, string>;
|
||||
};
|
||||
|
||||
function invoiceDisplayName(item: InvoicePoolListResponse["items"][number]) {
|
||||
const invoiceNo = [item.invoiceCode, item.invoiceNumber].filter(Boolean).join("-");
|
||||
return item.originalName || invoiceNo || "待识别票据";
|
||||
}
|
||||
|
||||
export function InvoiceListCard({
|
||||
listQuery,
|
||||
status,
|
||||
@@ -79,8 +84,8 @@ export function InvoiceListCard({
|
||||
<article className="rounded-xl border border-zinc-200 bg-white p-4 shadow-sm" data-testid="invoice-mobile-row" key={item.id}>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="truncate text-sm font-semibold text-zinc-900">{item.originalName || item.id}</div>
|
||||
<div className="mt-1 font-mono text-xs text-zinc-500">{item.id.slice(0, 8)}</div>
|
||||
<div className="truncate text-sm font-semibold text-zinc-900">{invoiceDisplayName(item)}</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">{item.invoiceDate || "待识别"}</div>
|
||||
</div>
|
||||
<StatusBadge className="shrink-0" status={item.status} />
|
||||
</div>
|
||||
@@ -141,7 +146,7 @@ export function InvoiceListCard({
|
||||
<table className="w-full text-left text-sm text-zinc-600" data-testid="invoice-pool-table">
|
||||
<thead className="bg-zinc-50/50">
|
||||
<tr>
|
||||
<th className="table-th w-[280px]">票据流水号</th>
|
||||
<th className="table-th w-[280px]">票据名称</th>
|
||||
<th className="table-th w-[120px]">状态</th>
|
||||
<th className="table-th w-[200px]">发票号码</th>
|
||||
<th className="table-th">识别状态</th>
|
||||
@@ -153,7 +158,8 @@ export function InvoiceListCard({
|
||||
listQuery.data?.items.map((item) => (
|
||||
<tr className="table-row" data-testid="invoice-row" key={item.id}>
|
||||
<td className="table-td">
|
||||
<div className="font-mono text-xs text-zinc-500">{item.id}</div>
|
||||
<div className="max-w-[260px] truncate font-medium text-zinc-900">{invoiceDisplayName(item)}</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">{item.invoiceDate || "待识别"}</div>
|
||||
</td>
|
||||
<td className="table-td"><StatusBadge status={item.status} /></td>
|
||||
<td className="table-td font-medium text-zinc-900">
|
||||
|
||||
@@ -566,6 +566,10 @@ test.describe.serial("R106 自动视觉巡检", () => {
|
||||
await adminMobileContext.close();
|
||||
|
||||
writeReport(seedData);
|
||||
if (process.env.R106_VISUAL_FAIL_ON_FINDINGS === "1") {
|
||||
const summary = findings.map((finding) => `${finding.severity} ${finding.page} / ${finding.category}: ${finding.title}`).join("\n");
|
||||
expect(findings, summary || "R106 visual findings should be empty").toEqual([]);
|
||||
}
|
||||
await expect.poll(() => findings.length, { message: "R106 report generated" }).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
ID,Title,Description,Acceptance,Test_Method,Tools,Dev_Status,Review1_Status,Regression_Status,Files,Dependencies,Notes
|
||||
A1,R107 UI 修复执行合同,"基于 R106 自动视觉巡检报告修复报销、票据、差旅的展示与视觉问题;不修改权限、审批状态机、Prisma schema。","CSV 校验通过;范围清晰;后续验证命令可复现。","python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-07_19-51-41-r107-ui-visual-fixes.csv;业务流程:把 R106 发现转为 R107 可验收修复项。","python|manual",DONE,DONE,DONE,"issues/2026-05-07_19-51-41-r107-ui-visual-fixes.csv","[]","回滚路径:删除本批前端改动和 R107 CSV,不涉及数据迁移。"
|
||||
A2,报销详情文案与明细按钮一致性,"精简关联票据说明文案;修复明细编辑/删除按钮上下堆叠和视觉规格不一致。","R106 报告不再出现 PC 报销详情啰嗦文案和明细按钮 diff。","cd client && npm run typecheck && npm run build;QA_SKIP_CLIENT_BUILD=1 QA_PORT=3199 QA_DB_NAME=hyb_oa_r107_ui bash scripts/qa/run-web-gates.sh r106-visual-local;业务流程:员工查看报销草稿、编辑/删除明细、添加发票入口。","typescript|playwright",DONE,DONE,DONE,"client/src/pages/ReimbursementDetailPage.tsx","[""A1""]","只改展示和布局。"
|
||||
A3,票据详情业务化展示,"票据详情主信息不展示内部 UUID;用发票号、销售方、购买方、金额、开票日期、绑定状态替代内部 ID。","R106 报告不再出现 PC 票据中心 UUID 暴露和票据 ID 主展示问题。","cd client && npm run typecheck && npm run build;运行 R106 视觉巡检并检查 artifacts/r106-visual-audit/report.md;业务流程:员工进入票据中心查看票据详情。","typescript|playwright",DONE,DONE,DONE,"client/src/pages/invoice-pool/InvoiceDetailPanel.tsx|client/src/pages/invoice-pool/InvoiceListCard.tsx","[""A1""]","若 API 暂无业务单号,则只显示绑定状态,不显示 UUID。"
|
||||
A4,票据归档按钮语义样式,"修复手工归档蓝底红字问题,使用统一危险操作样式。","R106 报告不再出现危险按钮红字叠主色背景。","cd client && npm run typecheck && npm run build;运行 R106 视觉巡检;业务流程:票据中心对未绑定票据执行归档前确认。","typescript|playwright",DONE,DONE,DONE,"client/src/pages/invoice-pool/InvoiceDetailPanel.tsx","[""A3""]","只改按钮样式与文案,不改归档逻辑。"
|
||||
A5,差旅行程表单裁切修复,"调整差旅行程段表单列宽,避免时间输入控件裁切。","R106 报告不再出现 PC 差旅控件裁切。","cd client && npm run typecheck && npm run build;运行 R106 视觉巡检;业务流程:员工新建差旅草稿、填写行程段出发/到达时间。","typescript|playwright",DONE,DONE,DONE,"client/src/pages/TravelRequestsPage.tsx","[""A1""]","只改布局,不改差旅保存/提交逻辑。"
|
||||
A6,R107 严格视觉回归与提交,"增加或使用严格视觉验证模式,确认 R107 修复项归零,回填状态并中文提交。","R107 相关命令通过;CSV 状态回填 DONE;中文 commit 引用 R107/A1-A6。","python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-07_19-51-41-r107-ui-visual-fixes.csv;cd client && npm run typecheck && npm run build;R106_VISUAL_FAIL_ON_FINDINGS=1 QA_SKIP_CLIENT_BUILD=1 QA_PORT=3199 QA_DB_NAME=hyb_oa_r107_ui bash scripts/qa/run-web-gates.sh r106-visual-local;git diff --check;业务流程:PC 报销、票据、差旅页面视觉回归。","python|typescript|playwright|git",DONE,DONE,DONE,"client/tests/e2e/r106-visual-audit.spec.ts|artifacts/r106-visual-audit","[""A2"",""A3"",""A4"",""A5""]","报告发现问题数为 0;已检查不包含密码、token、cookie、secret 和 UUID 原文;不提交 output/playwright 截图。"
|
||||
|
在新工单中引用
屏蔽一个用户