From 54cbb556d0900fade3ee99bc6a11381debbdb338 Mon Sep 17 00:00:00 2001 From: YANG JIANKUAN Date: Thu, 5 Nov 2026 13:39:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E6=9F=A5=E6=A0=87=E7=AC=AC=206=20?= =?UTF-8?q?=E6=AD=A5=E5=89=8D=E7=AB=AF=E8=A1=A5=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 读标页:每条清单项写最近一次检查的结论,点投标侧出处看投标原文; 「已响应」也能反馈判断有误(漏报);证书要求写资质库里的情况 (新接口 GET /projects/{id}/qualification-matches,资质库空时不写) - 概览:检查记录卡(历次检查、各级问题数、报告下载)、文件卡 - 工作台:「致命」列、统计可点筛选、待处理的致命问题卡(?f= 定位到问题) - 日期输入加日历弹层(DateInput,Reka UI Calendar) - 查标页:? 键显示快捷键说明;反馈对话框抽成 FeedbackDialog 共用 - 扫描件提示去掉多余空格 Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 7 +- apps/web/package.json | 1 + apps/web/src/components/ChecklistItemRow.vue | 82 ++++++++- apps/web/src/components/DateInput.vue | 112 ++++++++++++ apps/web/src/components/FeedbackDialog.vue | 98 +++++++++++ apps/web/src/components/FieldDialog.vue | 3 +- apps/web/src/components/ProjectRow.vue | 4 +- .../src/components/QualificationDialog.vue | 22 +-- apps/web/src/lib/keys.ts | 2 + apps/web/src/lib/labels.ts | 13 ++ apps/web/src/lib/queries.ts | 31 ++++ apps/web/src/pages/ChecklistPage.vue | 48 +++++- apps/web/src/pages/OverviewPage.vue | 161 +++++++++++++++++- apps/web/src/pages/ReportPage.vue | 13 +- apps/web/src/pages/ResultsPage.vue | 90 +++------- apps/web/src/pages/WorkbenchPage.vue | 138 ++++++++++++--- apps/web/src/styles/app.css | 33 ++++ docs/architecture/api-design.md | 6 +- docs/architecture/m3-plan.md | 2 +- docs/architecture/openapi.yaml | 54 ++++++ docs/architecture/project-info-plan.md | 2 +- docs/architecture/qualification-plan.md | 4 +- packages/api-client/src/schema.d.ts | 83 +++++++++ pnpm-lock.yaml | 3 + server/src/bid_doctor/check/api.py | 6 + server/src/bid_doctor/check/findings.py | 2 +- server/src/bid_doctor/check/qualification.py | 37 ++++ server/src/bid_doctor/check/service.py | 18 +- server/tests/test_check_qualification.py | 22 +++ server/tests/test_check_units.py | 2 +- 30 files changed, 956 insertions(+), 143 deletions(-) create mode 100644 apps/web/src/components/DateInput.vue create mode 100644 apps/web/src/components/FeedbackDialog.vue diff --git a/AGENTS.md b/AGENTS.md index cb949be..ae7e856 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -227,4 +227,9 @@ samples/ - 2026-11-05:查标结果页、概览改成和读标页一样的左右布局(左边内容,右边原文贴住视口),去掉原文侧边抽屉。查标右侧招标文件、投标文件左右切换(`components/DocStage.vue`):当前的占满,另一份露出一窄条(文件名、纸边、带方向箭头的标签,截断处淡入底色),点它或点另一边的出处就滑过去,两边各自记着页和高亮,J / K 时原文跟着翻。概览把关键日期和采购包合成一张卡放到最上面。修了原文查看器直接打开到某页时重复渲染出镜像乱字的问题。内容区四角改为直角(设计规范 §5.3 同步)。 - 2026-11-05:国动真实投标文件跑了一次(`check-eval.md` §14,代码 `5b77012`、规则集 r4、资质库空):重新读标 299 条,查标 26 秒报 44 个问题,逐条对原文后有用 9 个,**致命、重要各 1 个都是误报**(响应格「完全满足,无偏离。」被当成错位;「不……另行收费」被当成附加条件)。另外扫描页只按整页有没有字判断、不需要回答的条目还在出提示。人工看到评分索引表页码和实际页码对不上,产品还不查这一项。结果只记录,6 处问题修不修待用户定。 - 2026-11-05:按用户决定(E3)修了国动暴露的 6 处,规则集 2026.11-r5(`check-eval.md` §15)。国动重新检查 44 → 11 个,全是提示级,有用的 9 个都还在(看过结果才修,只作参考)。开发集不退化:召回 75% → 74%(模型重跑的波动),致命级 25 / 32 不变,规矩的投标文件报出的问题 239 → 213,规则问题 57 / 57。投标分册的文字层升到 v5(每页记图片占比,用来判断扫描件)。 -- 下一步:可做的有:第 6 步前端补齐(键盘操作、批量处理、判断有误反馈、日期选择器的日历弹层、概览页的检查记录和文件卡)、M6 剩下的 P0(额度流水、套餐开通、数据删除、运营后台)。 +- 2026-11-05:查标第 6 步前端补齐(`m3-plan.md` 进度)。 + - 读标页:每条清单项写最近一次检查的结论,点投标侧出处看投标原文;「已响应」也能反馈判断有误。证书要求写资质库里有没有、在截止日是否有效(新接口 `qualification-matches`,资质库空时不写)。 + - 概览:加了检查记录卡和文件卡。 + - 工作台:加了「致命」列、统计可点筛选、待处理的致命问题卡(「去处理」定位到那条问题)。 + - 日期输入加了日历弹层;查标页加了 ? 键。 +- 下一步:M6 剩下的 P0(额度流水、套餐开通、数据删除、运营后台)。 diff --git a/apps/web/package.json b/apps/web/package.json index 2cf6b70..8c7df56 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -13,6 +13,7 @@ "@bid-doctor/api-client": "workspace:*", "@fontsource/barlow-semi-condensed": "5.3.0", "@fontsource/noto-serif-sc": "5.3.0", + "@internationalized/date": "3.12.4", "@lucide/vue": "1.50.0", "@tanstack/vue-query": "5.104.1", "pdfjs-dist": "6.4.299", diff --git a/apps/web/src/components/ChecklistItemRow.vue b/apps/web/src/components/ChecklistItemRow.vue index 4dbfc9a..e41458e 100644 --- a/apps/web/src/components/ChecklistItemRow.vue +++ b/apps/web/src/components/ChecklistItemRow.vue @@ -7,11 +7,23 @@ import { computed } from "vue"; import CitationBlock from "./CitationBlock.vue"; import { groupCitations, type CitationGroup } from "@/lib/citations"; -import { category, failureText, IMPORTANCE_MARK, IMPORTANCE_NAME, pendingReason, specFacts } from "@/lib/labels"; -import type { ChecklistItem } from "@/lib/queries"; +import { RouterLink } from "vue-router"; +import { category, CHECK_VERDICT, failureText, fmtDay, IMPORTANCE_MARK, IMPORTANCE_NAME, pendingReason, specFacts } from "@/lib/labels"; +import type { CheckItemResult, ChecklistItem, Citation, QualificationMatch } from "@/lib/queries"; -const props = defineProps<{ item: ChecklistItem; pending?: boolean; selected?: boolean; activeCitation?: string | null }>(); -const emit = defineEmits<{ select: []; openCitation: [g: CitationGroup] }>(); +const props = defineProps<{ + item: ChecklistItem; + pending?: boolean; + selected?: boolean; + activeCitation?: string | null; + /** 最近一次检查对这一条的结论(C9);没参与检查的不传 */ + check?: CheckItemResult | null; + /** 资质库里的情况(资格条件、评分项里认出证书类别的条目);资质库是用户自己录的,墨色,不用蓝铅笔色 */ + library?: QualificationMatch[] | null; + /** library 按哪天判断有效期:「投标截止日」或「今天」 */ + libraryAsOf?: string; +}>(); +const emit = defineEmits<{ select: []; openCitation: [g: CitationGroup]; openBid: [c: Citation]; feedback: []; findings: [] }>(); /** 一条结论最多默认展开 2 处出处,其余写「另有 N 处」(§7.3);跨页的一句话算一处 */ const groups = computed(() => groupCitations(props.item.citations)); @@ -22,6 +34,9 @@ const mark = computed(() => IMPORTANCE_MARK[props.item.importance] ?? ""); const summary = computed(() => (props.item.summary && props.item.summary !== props.item.title ? props.item.summary : "")); /** 结构化取值:分值、参数要求、日期等,都来自原文(取值校验通过的才进正式清单) */ const facts = computed(() => specFacts(props.item.spec)); +/** 检查结论:已响应、默认接受是好的;其余在查标页有问题可处理 */ +const checkOk = computed(() => props.check?.verdict === "responded" || props.check?.verdict === "implicit_accepted"); +const bidCites = computed(() => groupCitations(props.check?.bid_citations ?? []).slice(0, 2));