fix(ui): 读标、查标走查修复;查标结果页按原型补齐
走查(注册 → 读标 → 查标 → 导出报告)发现的问题: - 下拉:原生 select 换成 SelectBox(Reka UI,规范 §7.21):右侧 chevron 有边距、 sm 高 32 和搜索框一致、浮层和触发框同宽、选中项加粗带 ✓;响应方式栏的签名不再被挤成竖排 - 读标:「依据 · 第 N 页」改成行内出处链接;证明材料标签不再被挤压 - 概览:关键日期同一种、同一时刻只列一次 - 报告页:「还有 N 项致命」读项目实时值(检查的 counts 是快照) - 读标抽取:★ 只表示「重要」的文件,清单项也挂到 ★ 规则上(「已识别 N 条」) - 检查范围文案和后端覆盖范围一致;报告出处不重复写文件名(模板 2026.11-v3) - 带加载态的按钮图标和文字间距;单选、半选、禁用复选框样式 查标结果页按原型 results.html 补齐: - 本次处理的问题留在原位(署名行可撤销),修了处理后仍显示处理按钮的问题 - 键盘 J / K / O / 1 / 2 / 3 / X / U / Esc;「提示」批量确认无误;「判断有误?」反馈 - 覆盖范围默认收起成一行;顶部加导出报告入口和致命提示;已处理计数 - 检查进度卡和解析进度卡同一种样式 - 项目头「就绪 / 未就绪」,致命数挂在查标标签上 - 上传投标文件页:已有分册时新增表单收起到「添加分册」 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import HistoryDialog from "@/components/HistoryDialog.vue";
|
||||
import ItemDialog from "@/components/ItemDialog.vue";
|
||||
import PdfViewer, { type ViewerTarget } from "@/components/PdfViewer.vue";
|
||||
import ProjectHead from "@/components/ProjectHead.vue";
|
||||
import SelectBox from "@/components/SelectBox.vue";
|
||||
import { api, errorTitle } from "@/lib/api";
|
||||
import { useChecklistOps, useDeletedItems, type ItemFields } from "@/lib/checklistOps";
|
||||
import { groupCitations, type CitationGroup } from "@/lib/citations";
|
||||
@@ -152,6 +153,10 @@ const openGroup = (g: CitationGroup) => openCitation(g.head, g.tail);
|
||||
/* ---------- 响应方式(C12):招标文件要求逐条响应,还是只列偏离、没列出的视为接受;查标按它判没找到响应算不算问题 ---------- */
|
||||
const qc = useQueryClient();
|
||||
const MODE_CATEGORY: Record<string, string> = { technical: "技术要求", commercial: "商务要求" };
|
||||
const MODE_OPTIONS = [
|
||||
{ value: "explicit", label: "逐条响应" },
|
||||
{ value: "implicit", label: "只列偏离,没列出的视为接受" },
|
||||
];
|
||||
const MODE_STATUS: Record<string, string> = { unknown: "招标文件没写明,按逐条响应处理", conflict: "招标文件两处写法不一致,按逐条响应处理" };
|
||||
const modeBusy = ref("");
|
||||
async function setMode(category: "technical" | "commercial", mode: "explicit" | "implicit") {
|
||||
@@ -357,17 +362,15 @@ onBeforeUnmount(() => window.removeEventListener("keydown", onKey));
|
||||
<span class="cap muted">响应方式</span>
|
||||
<div v-for="m in cl.response_modes" :key="m.category" class="mode">
|
||||
<label :for="`mode-${m.category}`">{{ MODE_CATEGORY[m.category] }}</label>
|
||||
<select
|
||||
<SelectBox
|
||||
:id="`mode-${m.category}`"
|
||||
class="input sm"
|
||||
:value="m.mode"
|
||||
size="sm"
|
||||
:model-value="m.mode"
|
||||
:options="MODE_OPTIONS"
|
||||
:disabled="readOnly || modeBusy === m.category"
|
||||
@change="setMode(m.category, ($event.target as HTMLSelectElement).value as 'explicit' | 'implicit')"
|
||||
>
|
||||
<option value="explicit">逐条响应</option>
|
||||
<option value="implicit">只列偏离,没列出的视为接受</option>
|
||||
</select>
|
||||
<button v-if="m.citations.length" type="button" class="plain cap" @click="openCitation(m.citations[0]!)">依据 · 第 {{ m.citations[0]!.page_no }} 页</button>
|
||||
@update:model-value="(v) => v !== m.mode && setMode(m.category, v as 'explicit' | 'implicit')"
|
||||
/>
|
||||
<button v-if="m.citations.length" type="button" class="cite-src inline" @click="openCitation(m.citations[0]!)">依据 · 第 <span class="fig">{{ m.citations[0]!.page_no }}</span> 页</button>
|
||||
<span v-if="m.origin === 'human'" class="sign cap"><span aria-hidden="true">✓</span><b>{{ m.set_by?.name }}</b><span>改过</span></span>
|
||||
<span v-else-if="MODE_STATUS[m.status]" class="hint-warn cap">{{ MODE_STATUS[m.status] }}</span>
|
||||
</div>
|
||||
@@ -413,10 +416,15 @@ onBeforeUnmount(() => window.removeEventListener("keydown", onKey));
|
||||
<input v-model="kw" class="input" placeholder="搜索清单" aria-label="搜索清单" />
|
||||
</label>
|
||||
<label class="choice cap"><input v-model="starOnly" type="checkbox" class="ck" />只看 ★</label>
|
||||
<select v-if="sources.length > 1" v-model="source" class="input sm src" aria-label="来源">
|
||||
<option value="">全部来源</option>
|
||||
<option v-for="x in sources" :key="x.id" :value="x.id">{{ x.label }}({{ x.n }})</option>
|
||||
</select>
|
||||
<SelectBox
|
||||
v-if="sources.length > 1"
|
||||
:model-value="source"
|
||||
size="sm"
|
||||
class="src"
|
||||
aria-label="来源"
|
||||
:options="[{ value: '', label: '全部来源' }, ...sources.map((x) => ({ value: x.id, label: `${x.label}(${x.n})` }))]"
|
||||
@update:model-value="(v) => (source = v ?? '')"
|
||||
/>
|
||||
<label v-if="supCount" class="choice cap"><input v-model="supOnly" type="checkbox" class="ck" />只看日期更新({{ supCount }})</label>
|
||||
</div>
|
||||
|
||||
@@ -539,8 +547,10 @@ onBeforeUnmount(() => window.removeEventListener("keydown", onKey));
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.lst-tb .src {
|
||||
.lst-tb :deep(.src) {
|
||||
flex: none;
|
||||
width: auto;
|
||||
max-width: 200px;
|
||||
}
|
||||
.lot-scope {
|
||||
display: flex;
|
||||
@@ -567,7 +577,12 @@ onBeforeUnmount(() => window.removeEventListener("keydown", onKey));
|
||||
color: var(--text-secondary);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.modes :deep(.select) {
|
||||
width: 236px;
|
||||
}
|
||||
.modes .sign {
|
||||
flex: none;
|
||||
flex-wrap: nowrap;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user