docs(design): 按 v0.2 重刷全部 UI demo,确认 Q1–Q13 与 U 键规则
- ui-demo:控件、表单、卡片、列表、工作台、读标六页按 v0.2 重刷;侧栏与顶栏框架、项目行、署名行、可键入日期输入、对照布局等收回 demo.css / demo.js 共用 - system:tokens 补悬停 / 按下 / 遮罩色、数字字阶与原文排版变量;components.css 按 v0.2 改写并清除裸色值 - design-system.md:写入 Q1–Q13 答案,新增开关、分页、下拉、日期选择等组件规则,类别标签、待确认区、选中态、对照布局按 v0.2 修订 - Q13:额度流水记录 0 点的「免扣」行,FR-M6-03 与领域模型同步;U 键改为撤销当前条 - decisions.md 记录 Q1–Q12、Q13 与 U 键;AGENTS.md 同步进度 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,12 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
陈列页 · 表单(v0.2)
|
||||
新建项目、资质录入、问题处理时的说明(忽略原因 / 致命问题确认说明 U4)。
|
||||
日期按 Q10:输入框写「2026-11-20 09:30」,可直接键入,也可用日期选择器;界面显示「11月20日 09:30」。
|
||||
日期输入、字段错误、署名行、行内说明都是共用组件(demo.js 的 Demo.dateInput / Demo.fieldError,demo.css 的 .dt / .sign / .u4)。
|
||||
主按钮按 Q12:表单一个、对话框一个;问题处理面板里的提交按钮是次按钮(§7.1「问题处理三按钮」)。
|
||||
?state=errors 直接显示新建项目的错误汇总(截图用)。
|
||||
-->
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
@@ -10,11 +18,24 @@
|
||||
<link rel="stylesheet" href="../system/components.css">
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
<style>
|
||||
/* 只属于本页的排版 */
|
||||
/* 本页布局 */
|
||||
.lot-radios{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--sp-3)}
|
||||
.f-card{max-width:none}
|
||||
.card-b.form-b{padding-top:var(--sp-4);padding-bottom:var(--sp-6)}
|
||||
.card-f .cost{margin-right:auto}
|
||||
.card-f .cost .hint-u{margin-left:var(--sp-1)}
|
||||
.hint-warn{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--warn-text);display:flex;gap:6px;align-items:flex-start}
|
||||
.hint-warn .ico{width:14px;height:14px;font-size:10px;margin:3px 0 0}
|
||||
.field.err .drop{border-color:var(--warn)}
|
||||
/* 错误汇总:卡片头下方的一条,不在卡片里再套框 */
|
||||
.err-sum{margin:0;border-width:var(--line) 0;border-radius:0;outline-offset:-2px}
|
||||
.err-sum .ico{margin-top:1px}
|
||||
.err-sum li{color:var(--text-primary)}
|
||||
.st6{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:var(--sp-5)}
|
||||
.st3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--sp-5);align-items:start}
|
||||
.d-box + .d-box,.d-box + .d-grid{margin-top:var(--gap-card)}
|
||||
.d-box > .d-label{margin-bottom:var(--sp-4)}
|
||||
|
||||
.dialog .form{margin-top:var(--sp-1)}
|
||||
</style>
|
||||
</head>
|
||||
<body class="demo">
|
||||
@@ -22,50 +43,56 @@
|
||||
<main class="d-page">
|
||||
<div class="d-head">
|
||||
<h1>② 表单</h1>
|
||||
<p>三张真实业务表单:新建项目、资质录入、处理问题时的「忽略并注明原因」。标签在上,必填写「(必填)」,错误用赭色边框 + 下方赭色小字 + 「!」图标,离开输入框时校验、提交时汇总。</p>
|
||||
<nav class="d-toc" aria-label="本页目录"><a href="#s-new">01 新建项目</a><a href="#s-states">02 状态对照</a><a href="#s-qual">03 资质录入</a><a href="#s-ignore">04 忽略并注明原因 · 致命确认说明</a></nav>
|
||||
<p>必填写「(必填)」,错误用赭色;离开输入框时校验,提交时汇总。日期输入「2026-11-20 09:30」,显示「11月20日 09:30」。</p>
|
||||
<nav class="d-toc" aria-label="本页目录"><a href="#s-new">01 新建项目</a><a href="#s-states">02 状态对照</a><a href="#s-qual">03 资质录入</a><a href="#s-ignore">04 忽略原因 · 致命确认说明</a></nav>
|
||||
</div>
|
||||
|
||||
<!-- 01 新建项目 -->
|
||||
<section class="d-sec" id="s-new">
|
||||
<div class="d-sec-h"><span class="no">01</span><h2>新建项目</h2><span class="ref">规范 §7.13 · §8.3 · FR-M2-01</span></div>
|
||||
<div class="d-sec-h"><span class="no">01</span><h2>新建项目</h2><span class="ref">§7.13 · §8.3 · §9.3 · Q10 · Q12</span></div>
|
||||
<div class="d-grid c-side">
|
||||
<form class="card f-card" novalidate data-new>
|
||||
<div class="alert warn err-sum" data-errsum hidden><span class="ico w">!</span><div><p><b>有 <span data-errn>2</span> 处需要修改</b></p><ul data-errlist></ul></div></div>
|
||||
<form class="card flush" novalidate data-new aria-labelledby="h-new">
|
||||
<div class="card-h"><h3 id="h-new">新建项目</h3></div>
|
||||
<div class="alert warn err-sum" role="alert" tabindex="-1" data-errsum hidden><span class="ico w" aria-hidden="true">!</span><div><p><b>有 <span data-errn>2</span> 处需要修改</b></p><ul data-errlist></ul></div></div>
|
||||
<div class="card-b form-b">
|
||||
|
||||
<fieldset class="form-sec">
|
||||
<legend>项目信息<small>招标文件解析后会自动补全,可以先只填名称</small></legend>
|
||||
<div class="form">
|
||||
<div class="field span2" id="f-name"><label for="n-name">项目名称 <small>(必填)</small></label><input class="input" id="n-name" placeholder="例如:某市政务服务中心智慧大厅建设项目"><span class="msg">只在你的工作台里显示,可以用简称</span></div>
|
||||
<div class="field"><label for="n-no">项目编号</label><input class="input" id="n-no" placeholder="招标文件封面上的编号"></div>
|
||||
<div class="field"><label for="n-buyer">采购人</label><input class="input" id="n-buyer" placeholder="例如:某市政务服务管理办公室"></div>
|
||||
<div class="field span2"><label id="l-regime">法律体系</label>
|
||||
<div class="lot-radios" role="radiogroup" aria-labelledby="l-regime">
|
||||
<label class="choice-card"><input type="radio" class="ck" name="regime" checked><span><b>让标博士识别</b><small>按招标文件的依据和格式判断</small></span></label>
|
||||
<label class="choice-card"><input type="radio" class="ck" name="regime"><span><b>政府采购</b><small>结论用「投标无效」</small></span></label>
|
||||
<label class="choice-card"><input type="radio" class="ck" name="regime"><span><b>招标投标</b><small>结论用「否决投标」</small></span></label>
|
||||
</div></div>
|
||||
<div class="field"><label>采购方式</label>
|
||||
<div class="pop" data-select><button type="button" class="select" aria-haspopup="listbox" aria-expanded="false"><span class="val"><span class="ph">解析后识别</span></span><i data-ic="chev-down"></i></button>
|
||||
<ul class="menu" role="listbox"><li role="option" aria-selected="true" data-label='<span class="ph">解析后识别</span>'>解析后识别</li><li class="grp">政府采购</li><li role="option">公开招标</li><li role="option">邀请招标</li><li role="option">竞争性磋商</li><li role="option">竞争性谈判</li><li role="option">询价</li></ul></div></div>
|
||||
<div class="field" id="f-due"><label>投标截止 <small>(必填)</small></label>
|
||||
<div class="dt-pair"><div class="pop" data-date data-marks="2026-11-20" data-new-date><button type="button" class="select date-trigger" aria-haspopup="dialog" aria-expanded="false"><span class="val"><span class="ph">选择日期</span></span><i data-ic="calendar"></i></button><div class="cal"></div></div><input class="input date-trigger" value="09:30" aria-label="截止时间"></div>
|
||||
<span class="msg">用于工作台的倒计时;补遗更正后会提示你核对</span></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="form-sec">
|
||||
<legend>项目信息<small>解析后自动补全,可以先只填名称</small></legend>
|
||||
<div class="form">
|
||||
<div class="field span2" id="f-name"><label for="n-name">项目名称 <small>(必填)</small></label><input class="input" id="n-name" placeholder="例如:某市政务服务中心智慧大厅建设项目" autocomplete="off"><span class="msg">可以用简称</span></div>
|
||||
<div class="field"><label for="n-no">项目编号</label><input class="input" id="n-no" placeholder="招标文件封面上的编号"></div>
|
||||
<div class="field"><label for="n-buyer">采购人</label><input class="input" id="n-buyer" placeholder="例如:某市政务服务管理办公室"></div>
|
||||
<div class="field span2"><label id="l-regime">法律体系</label>
|
||||
<div class="lot-radios" role="radiogroup" aria-labelledby="l-regime">
|
||||
<label class="choice-card"><input type="radio" class="ck" name="regime" checked><span><b>让标博士识别</b><small>按招标文件的依据判断</small></span></label>
|
||||
<label class="choice-card"><input type="radio" class="ck" name="regime"><span><b>政府采购</b><small>结论用「投标无效」</small></span></label>
|
||||
<label class="choice-card"><input type="radio" class="ck" name="regime"><span><b>招标投标</b><small>结论用「否决投标」</small></span></label>
|
||||
</div></div>
|
||||
<div class="field"><label id="l-way">采购方式</label>
|
||||
<div class="pop" data-select><button type="button" class="select" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="l-way"><span class="val"><span class="ph">解析后识别</span></span><i data-ic="chev-down"></i></button>
|
||||
<ul class="menu" role="listbox"><li role="option" aria-selected="true" data-label='<span class="ph">解析后识别</span>'>解析后识别</li><li class="grp">政府采购</li><li role="option">公开招标</li><li role="option">邀请招标</li><li role="option">竞争性磋商</li><li role="option">竞争性谈判</li><li role="option">询价</li><li class="grp">招标投标</li><li role="option" data-value="招标-公开">公开招标</li><li role="option" data-value="招标-邀请">邀请招标</li></ul></div></div>
|
||||
<div class="field" id="f-due"><label for="n-due">投标截止 <small>(必填)</small></label>
|
||||
<div class="dt" data-dt data-time="09:30" data-left data-req="请填写投标截止时间,例如 2026-11-20 09:30">
|
||||
<input class="input" id="n-due" placeholder="例如:2026-11-20 09:30" autocomplete="off">
|
||||
<button type="button" class="icon-btn sm" aria-label="打开日期选择器" aria-haspopup="dialog" aria-expanded="false"><i data-ic="calendar"></i></button>
|
||||
<div class="cal" role="dialog" aria-label="选择投标截止日期" hidden></div>
|
||||
</div>
|
||||
<span class="msg" id="n-due-msg" data-dt-msg>24 小时制</span></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="form-sec">
|
||||
<legend>招标文件<small>PDF、Word 或扫描件;补遗可以以后追加</small></legend>
|
||||
<div class="field fill" id="f-file">
|
||||
<div class="drop" tabindex="0" role="button" aria-label="上传招标文件" data-drop><i data-ic="upload" class="lg"></i><b>拖入或点击上传招标文件</b><span>单个文件不超过 200 MB · 扩展名不可信时以文件内容为准</span></div>
|
||||
<div data-files style="margin-top:var(--sp-3)"></div>
|
||||
</div>
|
||||
<div class="field fill" style="margin-top:var(--sp-5)"><label for="n-note">备注 <small>(选填)</small></label><textarea class="input" id="n-note" placeholder="例如:只投采购包 1,联合体伙伴待定"></textarea></div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-foot">
|
||||
<span class="cost">扣 1 次解析 · 剩 <span class="fig">37</span> 次 · 解析失败、超时自动退回</span>
|
||||
<button type="button" class="btn">取消</button>
|
||||
<fieldset class="form-sec">
|
||||
<legend>招标文件<small>PDF、Word 或扫描件;补遗以后可以追加</small></legend>
|
||||
<div class="field fill" id="f-file"><span class="vh" id="l-file">招标文件</span>
|
||||
<div class="drop" tabindex="0" role="button" aria-labelledby="l-file" aria-describedby="drop-sub" data-drop><i data-ic="upload" class="lg"></i><b>拖入或点击上传</b><span id="drop-sub">单个文件不超过 200 MB</span></div>
|
||||
<div data-files></div>
|
||||
</div>
|
||||
<div class="field fill" style="margin-top:var(--sp-5)"><label for="n-note">备注 <small>(选填)</small></label><textarea class="input" id="n-note" placeholder="例如:只投采购包 1,联合体伙伴待定"></textarea></div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="card-f">
|
||||
<span class="cost">扣 1 次解析 · 剩 <span class="fig">37</span> 次<span class="hint-u tip-start" tabindex="0" data-tip="解析失败、超时或系统异常不扣额度,已扣的自动退回">失败不扣</span></span>
|
||||
<button type="button" class="btn" data-cancel>取消</button>
|
||||
<button type="submit" class="btn primary swap" data-submit><span class="idle">创建并开始解析</span><span class="busy">正在创建…</span></button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -74,19 +101,20 @@
|
||||
<div class="d-box">
|
||||
<h3>试一试</h3>
|
||||
<ol class="d-rules" style="border:0;padding-top:0;margin-top:0">
|
||||
<li>什么都不填,直接点「创建并开始解析」:顶部汇总 + 每个字段下方说明,点汇总里的链接跳到字段。</li>
|
||||
<li>在项目名称里输入再清空,离开输入框时就会校验。</li>
|
||||
<li>直接点「创建并开始解析」:顶部汇总,点汇总里的字段名跳过去。<a class="lnk" href="?state=errors#s-new">直接看汇总</a></li>
|
||||
<li>投标截止键入「2026/11/20 9:30」,离开后自动整理成标准格式;键入「2026-11-20 25:00」看错误。</li>
|
||||
<li>点上传区,会加入两个示意文件,其中一个扩展名和内容不符。</li>
|
||||
<li>全部填好后提交:按钮变成「正在创建…」,宽度不变,表单整体禁用。</li>
|
||||
<li>填好后提交:按钮变成「正在创建…」,宽度不变,整表禁用。</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="d-box">
|
||||
<h3>规则</h3>
|
||||
<ul class="d-rules" style="border:0;padding-top:0;margin-top:0">
|
||||
<li>必填写「(必填)」,不用红色星号;选填项多的表单反过来写「(选填)」。</li>
|
||||
<li>错误说明写清楚怎么改,不写「格式错误」。</li>
|
||||
<li>会扣点的提交按钮,左侧直接写扣多少、还剩多少。</li>
|
||||
<li>文件扩展名和内容不符只提醒,不拦截(样本分析 07 的结论)。</li>
|
||||
<li>必填写「(必填)」,不用红星;选填多时反过来写「(选填)」。</li>
|
||||
<li>错误说明写怎么改,不写「格式错误」。</li>
|
||||
<li>没碰过的字段离开时不报错;报错之后边输入边复查,改对立即消失。</li>
|
||||
<li>会扣点的提交按钮,左侧写扣多少、还剩多少。</li>
|
||||
<li>扩展名和内容不符只提醒,不拦截。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -95,121 +123,138 @@
|
||||
|
||||
<!-- 02 状态对照 -->
|
||||
<section class="d-sec" id="s-states">
|
||||
<div class="d-sec-h"><span class="no">02</span><h2>同一字段的状态对照</h2><span class="ref">规范 §7.13</span></div>
|
||||
<div class="d-sec-h"><span class="no">02</span><h2>状态对照</h2><span class="ref">§7.13 · §7.22 · Q10</span></div>
|
||||
<div class="d-box">
|
||||
<div class="d-states wide">
|
||||
<div class="d-state"><small>正常</small><div class="field"><label>证书编号 <small>(必填)</small></label><input class="input" value="01226IS0458"></div></div>
|
||||
<div class="d-state"><small>聚焦</small><div class="field"><label>证书编号 <small>(必填)</small></label><input class="input is-focus" value="01226IS04"></div></div>
|
||||
<div class="d-state"><small>错误</small><div class="field err"><label>证书编号 <small>(必填)</small></label><input class="input" value="" aria-invalid="true"><span class="msg"><span class="ico w">!</span>请填写证书编号,查标时用它核对附件</span></div></div>
|
||||
<div class="d-state"><small>禁用</small><div class="field dis"><label>证书编号</label><input class="input" value="01226IS0458" disabled><span class="msg">已被 2 个已递交项目引用,不能修改</span></div></div>
|
||||
<span class="d-label">文本输入</span>
|
||||
<div class="st6">
|
||||
<div class="d-state"><small>正常</small><div class="field"><label>证书编号 <small>(必填)</small></label><input class="input" value="01226IS0458" aria-label="证书编号(正常)"></div></div>
|
||||
<div class="d-state"><small>悬停</small><div class="field"><label>证书编号 <small>(必填)</small></label><input class="input is-hover" value="01226IS0458" aria-label="证书编号(悬停)"></div></div>
|
||||
<div class="d-state"><small>聚焦</small><div class="field"><label>证书编号 <small>(必填)</small></label><input class="input is-focus" value="01226IS04" aria-label="证书编号(聚焦)"></div></div>
|
||||
<div class="d-state"><small>错误:要改了才能保存</small><div class="field err"><label>证书编号 <small>(必填)</small></label><input class="input" value="" aria-invalid="true" aria-label="证书编号(错误)"><span class="msg"><span class="ico w" aria-hidden="true">!</span>请填写证书编号</span></div></div>
|
||||
<div class="d-state"><small>提醒:可以保存</small><div class="field"><label>有效期至</label><input class="input" value="2026-11-15" style="font-family:var(--font-figures);font-size:var(--fs-figure-sm)" aria-label="有效期至(提醒)"><span class="hint-warn"><span class="ico w" aria-hidden="true">!</span><span>早于投标截止 11月20日</span></span></div></div>
|
||||
<div class="d-state"><small>禁用</small><div class="field dis"><label>证书编号</label><input class="input" value="01226IS0458" disabled aria-label="证书编号(禁用)"><span class="msg">已被已递交项目引用</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-grid c2" style="margin-top:var(--sp-6)">
|
||||
<div class="card">
|
||||
<div class="card-h"><h3>填写中</h3><span class="more cap muted">正常状态</span></div>
|
||||
<div class="form">
|
||||
<div class="field"><label>名称 <small>(必填)</small></label><input class="input" value="ISO 9001 质量管理体系"></div>
|
||||
<div class="field"><label>证书编号 <small>(必填)</small></label><input class="input" value="01226Q31877"></div>
|
||||
</div>
|
||||
<div class="form-foot"><span class="cost">不扣额度</span><button class="btn">取消</button><button class="btn primary swap"><span class="idle">保存资质</span><span class="busy">正在保存…</span></button></div>
|
||||
<div class="d-box">
|
||||
<span class="d-label">日期时间输入(Q10):输入框写「2026-11-20 09:30」,可直接键入常见写法(左一键入的是「2026/11/20 9:30」),离开时整理;下方显示「11月20日 09:30 · 剩 N 天」</span>
|
||||
<div class="st3">
|
||||
<div class="d-state"><small>已填写</small><div class="field"><label for="s-dt1">投标截止 <small>(必填)</small></label>
|
||||
<div class="dt" data-dt data-time="09:30" data-left><input class="input" id="s-dt1" value="2026/11/20 9:30" autocomplete="off"><button type="button" class="icon-btn sm" aria-label="打开日期选择器" aria-haspopup="dialog" aria-expanded="false"><i data-ic="calendar"></i></button><div class="cal" role="dialog" aria-label="选择日期" hidden></div></div>
|
||||
<span class="msg" data-dt-msg>24 小时制</span></div></div>
|
||||
<div class="d-state"><small>选择器展开:选日期,底部填时间</small><div class="field"><label for="s-dt2">投标截止 <small>(必填)</small></label>
|
||||
<div class="dt d-open" data-dt data-time="09:30" data-marks="2026-11-20" data-min="2026-11-03"><input class="input is-focus" id="s-dt2" value="2026-11-20 09:30" autocomplete="off"><button type="button" class="icon-btn sm" aria-label="日期选择器" aria-haspopup="dialog" aria-expanded="true" tabindex="-1"><i data-ic="calendar"></i></button><div class="cal" role="dialog" aria-label="选择日期"></div></div></div></div>
|
||||
<div class="d-state"><small>错误</small><div class="field err"><label for="s-dt3">投标截止 <small>(必填)</small></label>
|
||||
<div class="dt" data-dt data-time="09:30" data-left><input class="input" id="s-dt3" value="2026-11-20 25:00" autocomplete="off"><button type="button" class="icon-btn sm" aria-label="打开日期选择器" aria-haspopup="dialog" aria-expanded="false"><i data-ic="calendar"></i></button><div class="cal" role="dialog" aria-label="选择日期" hidden></div></div>
|
||||
<span class="msg" data-dt-msg>24 小时制</span></div></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-h"><h3>提交中</h3><span class="more cap muted">整表禁用,按钮换成「正在…」,宽度不变</span></div>
|
||||
</div>
|
||||
<div class="d-grid c2">
|
||||
<div class="card flush">
|
||||
<div class="card-h"><h3>提交前</h3></div>
|
||||
<div class="card-b"><div class="form">
|
||||
<div class="field"><label>名称 <small>(必填)</small></label><input class="input" value="ISO 9001 质量管理体系" aria-label="名称"></div>
|
||||
<div class="field"><label>证书编号 <small>(必填)</small></label><input class="input" value="01226Q31877" aria-label="证书编号"></div>
|
||||
</div></div>
|
||||
<div class="card-f"><span class="cost" style="margin-right:auto">不扣额度</span><button class="btn">取消</button><button class="btn primary swap" data-loading="已保存资质"><span class="idle">保存资质</span><span class="busy">正在保存…</span></button></div>
|
||||
</div>
|
||||
<div class="card flush">
|
||||
<div class="card-h"><h3>提交中</h3><span class="cap muted" style="margin-left:auto">整表禁用,按钮宽度不变</span></div>
|
||||
<fieldset disabled style="border:0;margin:0;padding:0;min-width:0">
|
||||
<div class="form">
|
||||
<div class="field dis"><label>名称 <small>(必填)</small></label><input class="input" value="ISO 9001 质量管理体系" disabled></div>
|
||||
<div class="field dis"><label>证书编号 <small>(必填)</small></label><input class="input" value="01226Q31877" disabled></div>
|
||||
</div>
|
||||
<div class="card-b"><div class="form">
|
||||
<div class="field dis"><label>名称 <small>(必填)</small></label><input class="input" value="ISO 9001 质量管理体系" disabled aria-label="名称"></div>
|
||||
<div class="field dis"><label>证书编号 <small>(必填)</small></label><input class="input" value="01226Q31877" disabled aria-label="证书编号"></div>
|
||||
</div></div>
|
||||
</fieldset>
|
||||
<div class="form-foot"><span class="cost">不扣额度</span><button class="btn" disabled>取消</button><button class="btn primary swap is-loading" aria-busy="true"><span class="idle">保存资质</span><span class="busy">正在保存…</span></button></div>
|
||||
<div class="card-f"><span class="cost" style="margin-right:auto">不扣额度</span><button class="btn" disabled>取消</button><button class="btn primary swap is-loading" aria-busy="true"><span class="idle">保存资质</span><span class="busy">正在保存…</span></button></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 03 资质录入 -->
|
||||
<section class="d-sec" id="s-qual">
|
||||
<div class="d-sec-h"><span class="no">03</span><h2>资质录入</h2><span class="ref">FR-M6 最小资质库 · 红线 5 不编造</span></div>
|
||||
<div class="d-sec-h"><span class="no">03</span><h2>资质录入</h2><span class="ref">最小资质库 · 红线 5 · Q12</span></div>
|
||||
<div class="d-grid c-side">
|
||||
<div class="dialog wide static" role="dialog" aria-labelledby="q-t" style="width:auto">
|
||||
<h2 id="q-t">添加资质</h2>
|
||||
<h2 id="q-t">添加资质</h2><button type="button" class="icon-btn sm x" aria-label="关闭"><i data-ic="x"></i></button>
|
||||
<div class="body">
|
||||
<p class="muted">查标时用来核对证书有没有过期、等级够不够。数据只来自你录入的内容,系统不从外部补数据。</p>
|
||||
<div class="form">
|
||||
<div class="field span2"><label for="q-name">名称 <small>(必填)</small></label><input class="input" id="q-name" value="ISO/IEC 27001 信息安全管理体系认证"></div>
|
||||
<div class="field"><label>类型 <small>(必填)</small></label>
|
||||
<div class="pop" data-select><button type="button" class="select" aria-haspopup="listbox" aria-expanded="false"><span class="val">体系认证</span><i data-ic="chev-down"></i></button>
|
||||
<div class="field"><label id="l-qtype">类型 <small>(必填)</small></label>
|
||||
<div class="pop" data-select><button type="button" class="select" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="l-qtype"><span class="val">体系认证</span><i data-ic="chev-down"></i></button>
|
||||
<ul class="menu" role="listbox"><li role="option">资质证书</li><li role="option" aria-selected="true">体系认证</li><li role="option">软件著作权</li><li role="option">其他</li></ul></div></div>
|
||||
<div class="field"><label for="q-lv">等级 <small>(选填)</small></label><input class="input" id="q-lv" placeholder="例如:CS3、二级"></div>
|
||||
<div class="field"><label for="q-no">证书编号 <small>(必填)</small></label><input class="input" id="q-no" value="01226IS0458"></div>
|
||||
<div class="field"><label for="q-org">发证机构</label><input class="input" id="q-org" value="某认证中心"></div>
|
||||
<div class="field"><label>发证日期</label><div class="pop" data-date data-value="2023-11-16" data-month="2023-11"><button type="button" class="select date-trigger" aria-haspopup="dialog" aria-expanded="false"><span class="val">2023-11-16</span><i data-ic="calendar"></i></button><div class="cal"></div></div></div>
|
||||
<div class="field"><label>有效期至 <small>(必填)</small></label><div class="pop" data-date data-value="2026-11-15" data-marks="2026-11-20"><button type="button" class="select date-trigger" aria-haspopup="dialog" aria-expanded="false"><span class="val">2026-11-15</span><i data-ic="calendar"></i></button><div class="cal"></div></div>
|
||||
<span class="hint-warn"><span class="ico w">!</span><span>早于进行中项目「智慧大厅建设项目」的投标截止日 11月20日</span></span></div>
|
||||
<div class="field span2"><div class="d-row" style="gap:var(--sp-3)"><span id="q-long">长期有效</span><button type="button" class="switch" role="switch" aria-checked="false" aria-labelledby="q-long"><span class="track"></span><span class="st"></span></button><span class="cap muted">打开后不检查有效期</span></div></div>
|
||||
<div class="field span2"><label>证书扫描件</label>
|
||||
<div class="file-row"><span class="ext">PDF</span><div><b>ISO27001 证书 2023.pdf</b><span class="cap muted">2 页 · 1.2 MB</span></div><span class="acts"><button type="button" class="btn sm text">替换</button><button type="button" class="icon-btn sm" aria-label="移除附件" data-tip="移除附件"><i data-ic="x"></i></button></span></div>
|
||||
<div class="field"><label for="q-from">发证日期</label>
|
||||
<div class="dt" data-dt><input class="input" id="q-from" value="2023-11-16" placeholder="例如:2023-11-16" autocomplete="off"><button type="button" class="icon-btn sm" aria-label="打开日期选择器" aria-haspopup="dialog" aria-expanded="false"><i data-ic="calendar"></i></button><div class="cal" role="dialog" aria-label="选择发证日期" hidden></div></div></div>
|
||||
<div class="field" id="f-qexp"><label for="q-exp">有效期至 <small>(必填)</small></label>
|
||||
<div class="dt" data-dt data-marks="2026-11-20" data-note="圆点是进行中项目的投标截止日" data-req="请填写有效期,长期有效请打开下方开关"><input class="input" id="q-exp" value="2026-11-15" placeholder="例如:2026-11-15" autocomplete="off"><button type="button" class="icon-btn sm" aria-label="打开日期选择器" aria-haspopup="dialog" aria-expanded="false"><i data-ic="calendar"></i></button><div class="cal" role="dialog" aria-label="选择有效期" hidden></div></div>
|
||||
<span class="hint-warn" data-exp-hint><span class="ico w" aria-hidden="true">!</span><span>早于「智慧大厅建设项目」的投标截止 <span class="fig">11</span>月<span class="fig">20</span>日</span></span></div>
|
||||
<div class="field span2"><div class="d-row" style="gap:var(--sp-3)"><span id="q-long">长期有效</span><button type="button" class="switch" role="switch" aria-checked="false" aria-labelledby="q-long" data-long><span class="track"></span><span class="st"></span></button></div></div>
|
||||
<div class="field span2"><label>证书扫描件 <small>(可以稍后补录)</small></label>
|
||||
<div class="file-row"><span class="ext">PDF</span><div><b>ISO27001 证书 2023.pdf</b><span class="cap muted"><span class="fig">2</span> 页 · <span class="fig">1.2</span> MB</span></div><span class="acts"><button type="button" class="btn sm text">替换</button><button type="button" class="icon-btn sm" aria-label="移除附件" data-tip="移除附件"><i data-ic="x"></i></button></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="foot"><span class="left cap muted">保存后,进行中项目的查标会用新信息复查</span><button class="btn">取消</button><button class="btn primary">保存资质</button></div>
|
||||
<div class="foot"><span class="left cap muted">不扣额度</span><button class="btn">取消</button><button class="btn primary">保存资质</button></div>
|
||||
</div>
|
||||
<div class="d-stack">
|
||||
<div class="d-box">
|
||||
<h3>打开真实对话框 <small>可以点</small></h3>
|
||||
<button class="btn primary" data-qadd><i data-ic="plus"></i>添加资质</button>
|
||||
<p class="note">空表单直接保存会看到错误状态;「附件」可以稍后补录,列表里显示「待补录」占位,不生成内容。</p>
|
||||
<h3>真实对话框 <small>可以点</small></h3>
|
||||
<button class="btn" data-qadd><i data-ic="plus"></i>添加资质</button>
|
||||
<p class="cap muted" style="margin:var(--sp-3) 0 0">空表单直接保存看错误;附件可以稍后补录,列表里显示「待补录」,不生成内容。</p>
|
||||
</div>
|
||||
<div class="d-box">
|
||||
<h3>提醒和错误的区别</h3>
|
||||
<h3>错误和提醒</h3>
|
||||
<ul class="d-rules" style="border:0;padding-top:0;margin-top:0">
|
||||
<li><b>错误</b>(赭色边框):必须改了才能保存,例如漏填证书编号。</li>
|
||||
<li><b>提醒</b>(只有赭色小字,边框不变):可以保存,但查标会提示,例如证书在投标截止日前到期。</li>
|
||||
<li>到期判断以每个项目的投标截止日为准,不以今天为准。</li>
|
||||
<li><b>错误</b>:赭色边框,改了才能保存,例如漏填证书编号。</li>
|
||||
<li><b>提醒</b>:只有赭色小字,边框不变,可以保存,例如证书在投标截止前到期。</li>
|
||||
<li>到期按每个项目的投标截止日判断,不按今天。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 04 忽略并注明原因 -->
|
||||
<!-- 04 忽略并注明原因 · 致命问题确认说明 -->
|
||||
<section class="d-sec" id="s-ignore">
|
||||
<div class="d-sec-h"><span class="no">04</span><h2>忽略并注明原因 · 致命问题的确认说明</h2><span class="ref">规范 §7.4 · §7.13 · U4</span></div>
|
||||
<div class="d-sec-h"><span class="no">04</span><h2>忽略原因 · 致命问题的确认说明</h2><span class="ref">§7.4 · §7.13 · U4 · Q2</span></div>
|
||||
<div class="results">
|
||||
<article class="finding">
|
||||
<div>
|
||||
<div class="f-head"><span class="sev major">重要</span><span class="f-title">开标一览表与分项价格表合计不一致</span></div>
|
||||
<p class="f-text">开标一览表 287.60 万元,分项价格表合计 286.80 万元,相差 0.80 万元。按招标文件规定,两者不一致时以开标一览表为准,不作无效处理;但会影响价格评审。</p>
|
||||
<div class="u4" data-ign>
|
||||
<p class="f-text">开标一览表 287.60 万元,分项价格表合计 286.80 万元,相差 0.80 万元。招标文件规定以开标一览表为准,不作无效处理,但会影响价格评审。</p>
|
||||
<div class="u4" data-u4="ign">
|
||||
<div class="field err">
|
||||
<label for="ig1">忽略原因 <small>(必填,至少 5 个字)</small></label>
|
||||
<div class="chips"><button type="button" class="btn" data-chip>招标文件允许</button><button type="button" class="btn" data-chip>已在其他分册响应</button><button type="button" class="btn" data-chip>与本项目无关</button></div>
|
||||
<textarea class="input" id="ig1" maxlength="200" aria-invalid="true">已核对</textarea>
|
||||
<div class="msg-row"><span class="msg"><span class="ico w">!</span>至少写 5 个字,说明为什么不处理</span><span class="count"><span data-n>3</span> / 200</span></div>
|
||||
<div class="chips" role="group" aria-label="常用原因"><button type="button" class="btn" data-chip>招标文件允许</button><button type="button" class="btn" data-chip>已在其他分册响应</button><button type="button" class="btn" data-chip>与本项目无关</button></div>
|
||||
<textarea class="input" id="ig1" maxlength="200" aria-invalid="true" aria-describedby="ig1-msg">已核对</textarea>
|
||||
<div class="msg-row"><span class="msg" id="ig1-msg" data-def="报告和处理记录里会显示这段原因"><span class="ico w" aria-hidden="true">!</span>至少写 5 个字,说明为什么不处理</span><span class="count"><span data-n>3</span> / 200</span></div>
|
||||
</div>
|
||||
<div class="row"><button class="btn primary" data-ign-ok>忽略这一项</button><button class="btn text" data-ign-cancel>取消</button><span class="cap muted">「已忽略」不等于确认无误,报告里单独列出</span></div>
|
||||
<div class="row"><button type="button" class="btn sm" data-ok>忽略这一项</button><button type="button" class="btn sm text" data-x>取消</button><span class="cap muted">「已忽略」在报告里单独列出</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<aside>
|
||||
<div class="cite"><a class="cite-src" href="#">报价册 · <span class="fig">第 1 页</span></a><q>投标总价(小写):<mark>2876000.00 元</mark></q></div>
|
||||
<div class="cite"><a class="cite-src" href="#">报价册 · <span class="fig">第 3 页</span></a><q><mark>合计:2868000.00 元</mark></q></div>
|
||||
<div class="cite"><a class="cite-src" href="#" data-demo>报价册 · 第 <span class="fig">1</span> 页</a><q>投标总价(小写):<mark>2876000.00 元</mark></q></div>
|
||||
<div class="cite"><a class="cite-src" href="#" data-demo>报价册 · 第 <span class="fig">3</span> 页</a><q><mark>合计:2868000.00 元</mark></q></div>
|
||||
</aside>
|
||||
</article>
|
||||
<article class="finding is-fatal">
|
||||
<article class="finding">
|
||||
<div>
|
||||
<div class="f-head"><span class="sev fatal">致命</span><span class="f-title">授权委托书的单位签章位置未发现盖章</span><span class="flag box">新增</span></div>
|
||||
<p class="f-text">商务册第 5 页《法定代表人授权委托书》的「投标人(电子签章)」位置是空的。本检查只看有无,不鉴定真伪。</p>
|
||||
<div class="u4">
|
||||
<p class="f-text">商务册第 5 页《法定代表人授权委托书》的「投标人(电子签章)」位置是空的。只看有无,不鉴定真伪。</p>
|
||||
<div class="u4" data-u4="ok">
|
||||
<div class="field">
|
||||
<label for="ig2">确认无误的说明 <small>(必填)</small></label>
|
||||
<textarea class="input" id="ig2">电子签章在递交时由交易平台统一加盖,Word 稿里本来就是空的。</textarea>
|
||||
<div class="msg-row"><span class="msg">对致命问题说「没问题」,需要写一句说明;说明会同时作为误报反馈提交。</span><span class="count"><span>30</span> / 200</span></div>
|
||||
<textarea class="input" id="ig2" maxlength="200" aria-describedby="ig2-msg">电子签章在递交时由交易平台统一加盖,Word 稿里本来就是空的。</textarea>
|
||||
<div class="msg-row"><span class="msg" id="ig2-msg" data-def="说明会同时作为误报反馈提交">说明会同时作为误报反馈提交</span><span class="count"><span data-n>30</span> / 200</span></div>
|
||||
</div>
|
||||
<div class="row"><button class="btn primary swap" data-loading="已确认无误 · 说明已作为反馈提交"><span class="idle">确认无误</span><span class="busy">正在保存…</span></button><button class="btn text">取消</button></div>
|
||||
<div class="row"><button type="button" class="btn sm swap" data-ok><span class="idle">确认无误</span><span class="busy">正在保存…</span></button><button type="button" class="btn sm text" data-x>取消</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<aside>
|
||||
<div class="cite"><a class="cite-src" href="#">招标文件 · <span class="fig">第 24 页</span></a><q>4.《法定代表人授权委托书》(法定代表人签字或盖章并<mark>加盖单位电子签章</mark>)。</q></div>
|
||||
<div class="cite"><a class="cite-src" href="#">商务册 · <span class="fig">第 5 页</span></a><q>法定代表人(签字或盖章):李某某 <mark>投标人(电子签章):(签章位置为空)</mark></q></div>
|
||||
<div class="cite"><a class="cite-src" href="#" data-demo>招标文件 · 第 <span class="fig">24</span> 页</a><q>4.《法定代表人授权委托书》(法定代表人签字或盖章并<mark>加盖单位电子签章</mark>)。</q></div>
|
||||
<div class="cite"><a class="cite-src" href="#" data-demo>商务册 · 第 <span class="fig">5</span> 页</a><q>法定代表人(签字或盖章):李某某 <mark>投标人(电子签章):(签章位置为空)</mark></q></div>
|
||||
</aside>
|
||||
</article>
|
||||
</div>
|
||||
@@ -221,80 +266,146 @@
|
||||
const { $, $$, toast, dialog, icon, esc } = Demo;
|
||||
Demo.demoTop("forms.html");
|
||||
|
||||
/* ---------- 新建项目 ---------- */
|
||||
const F = $("[data-new]");
|
||||
let files = [], dateVal = "";
|
||||
$("[data-new-date]").addEventListener("change", (e) => { dateVal = e.detail; setErr("f-due", ""); });
|
||||
function setErr(id, msg) {
|
||||
const f = document.getElementById(id);
|
||||
f.classList.toggle("err", !!msg);
|
||||
let m = $(":scope > .msg.e", f);
|
||||
if (msg) {
|
||||
if (!m) { m = document.createElement("span"); m.className = "msg e"; f.appendChild(m); }
|
||||
m.innerHTML = `<span class="ico w">!</span>${msg}`;
|
||||
$$(":scope > .msg:not(.e)", f).forEach((x) => x.hidden = true);
|
||||
} else { if (m) m.remove(); $$(":scope > .msg", f).forEach((x) => x.hidden = false); }
|
||||
/* 日期输入(Q10)由 demo.js 的 Demo.dateInput() 处理:可直接键入、离开时整理、下方显示「11月20日 09:30 · 剩 N 天」。
|
||||
* 字段错误用 Demo.fieldError():离开时校验,报错后边输入边复查。 */
|
||||
const setErr = Demo.fieldError;
|
||||
const label = (f) => ($("label", f)?.firstChild.textContent || $(".vh", f)?.textContent || "").trim();
|
||||
|
||||
/* ---------- 01 新建项目 ---------- */
|
||||
const F = $("[data-new]"), sum = $("[data-errsum]"), dueDT = $("#f-due [data-dt]");
|
||||
let files = [];
|
||||
const RULES = {
|
||||
"f-name": () => $("#n-name").value.trim() ? "" : "请填写项目名称,可以先写简称",
|
||||
"f-due": null, // 由日期输入自己校验(必填 + 格式)
|
||||
"f-file": () => files.length ? "" : "请上传招标文件,至少一份"
|
||||
};
|
||||
function check(id) {
|
||||
const ok = id === "f-due" ? dueDT.dt.commit({ required: true }) : setErr(document.getElementById(id), RULES[id]());
|
||||
if (!sum.hidden) summary(false);
|
||||
return ok;
|
||||
}
|
||||
function checkName() { setErr("f-name", $("#n-name").value.trim() ? "" : "请填写项目名称,可以先写简称,之后再改"); }
|
||||
$("#n-name").addEventListener("blur", checkName);
|
||||
function summary(focus) {
|
||||
const errs = Object.keys(RULES).map((id) => document.getElementById(id)).filter((f) => f.classList.contains("err"));
|
||||
sum.hidden = !errs.length;
|
||||
$("[data-errn]").textContent = errs.length;
|
||||
$("[data-errlist]").innerHTML = errs.map((f) => `<li><a href="#${f.id}" data-go="${f.id}">${esc(label(f))}</a>:${esc($(".msg.e", f).textContent.replace(/^!/, ""))}</li>`).join("");
|
||||
if (focus && errs.length) { sum.scrollIntoView({ block: "center" }); sum.focus({ preventScroll: true }); }
|
||||
}
|
||||
let nameDirty = false;
|
||||
$("#n-name").addEventListener("input", () => { nameDirty = true; if ($("#f-name").classList.contains("err") && !RULES["f-name"]()) check("f-name"); });
|
||||
$("#n-name").addEventListener("blur", () => { if (nameDirty || $("#f-name").classList.contains("err")) check("f-name"); });
|
||||
F.addEventListener("dt-change", () => { if (!sum.hidden) summary(false); });
|
||||
sum.addEventListener("click", (e) => { const a = e.target.closest("[data-go]"); if (!a) return; e.preventDefault(); const f = document.getElementById(a.dataset.go); f.scrollIntoView({ block: "center" }); ($("input, [data-drop]", f)).focus({ preventScroll: true }); });
|
||||
|
||||
function renderFiles() {
|
||||
$("[data-files]").innerHTML = files.map((f, i) => `<div class="file-row"><span class="ext">${f.ext}</span><div><b>${esc(f.name)}</b>${f.warn ? `<span class="hint-warn"><span class="ico w">!</span><span>${f.warn}</span></span>` : `<span class="cap muted">${f.meta}</span>`}</div><span class="acts"><button type="button" class="icon-btn sm" aria-label="移除" data-tip="移除" data-rm="${i}">${icon("x")}</button></span></div>`).join("");
|
||||
$("[data-files]").innerHTML = files.map((f, i) => `<div class="file-row" style="margin-top:var(--sp-3)"><span class="ext">${f.ext}</span><div><b>${esc(f.name)}</b>${f.warn ? `<span class="hint-warn"><span class="ico w" aria-hidden="true">!</span><span>${f.warn}</span></span>` : `<span class="cap muted">${f.meta}</span>`}</div><span class="acts"><button type="button" class="icon-btn sm" aria-label="移除 ${esc(f.name)}" data-tip="移除" data-rm="${i}">${icon("x")}</button></span></div>`).join("");
|
||||
}
|
||||
$("[data-drop]").addEventListener("click", () => {
|
||||
files = [{ ext: "PDF", name: "ZC-2026-0418 采购文件.pdf", meta: "186 页 · 8.4 MB" }, { ext: "DOC", name: "关于 ZC-2026-0418 的更正公告.pdf", warn: "扩展名是 .pdf,内容是 Word 文档,会按 Word 解析" }];
|
||||
renderFiles(); setErr("f-file", "");
|
||||
});
|
||||
$("[data-files]").addEventListener("click", (e) => { const b = e.target.closest("[data-rm]"); if (b) { files.splice(+b.dataset.rm, 1); renderFiles(); } });
|
||||
const addFiles = () => {
|
||||
files = [{ ext: "PDF", name: "ZC-2026-0418 采购文件.pdf", meta: `<span class="fig">186</span> 页 · <span class="fig">8.4</span> MB` }, { ext: "DOC", name: "关于 ZC-2026-0418 的更正公告.pdf", warn: "扩展名是 .pdf,内容是 Word 文档,会按 Word 解析" }];
|
||||
renderFiles(); check("f-file");
|
||||
};
|
||||
$("[data-drop]").addEventListener("click", addFiles);
|
||||
$("[data-drop]").addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); addFiles(); } });
|
||||
$("[data-files]").addEventListener("click", (e) => { const b = e.target.closest("[data-rm]"); if (b) { files.splice(+b.dataset.rm, 1); renderFiles(); if (!files.length && !sum.hidden) check("f-file"); } });
|
||||
$("[data-cancel]", F).onclick = () => toast("(演示)取消:回到工作台", { ok: false });
|
||||
|
||||
F.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
checkName();
|
||||
setErr("f-due", dateVal ? "" : "请选择投标截止日期,用于倒计时");
|
||||
setErr("f-file", files.length ? "" : "请上传招标文件,至少一份");
|
||||
const errs = $$(".field.err", F);
|
||||
const sum = $("[data-errsum]");
|
||||
if (errs.length) {
|
||||
$("[data-errn]").textContent = errs.length;
|
||||
$("[data-errlist]").innerHTML = errs.map((f) => `<li><a href="#${f.id}">${$("label", f)?.firstChild.textContent.trim() || "招标文件"}</a>:${$(".msg.e", f).textContent.replace(/^!/, "")}</li>`).join("");
|
||||
sum.hidden = false; sum.scrollIntoView({ block: "center", behavior: "smooth" });
|
||||
return;
|
||||
}
|
||||
const ok = Object.keys(RULES).map(check).every(Boolean);
|
||||
if (!ok) { summary(true); return; }
|
||||
sum.hidden = true;
|
||||
const b = $("[data-submit]");
|
||||
b.classList.add("is-loading"); b.setAttribute("aria-busy", "true");
|
||||
$$("input, textarea, button:not([data-submit]), .drop", F).forEach((x) => { x.disabled = true; x.closest(".field")?.classList.add("dis"); });
|
||||
setTimeout(() => { b.classList.remove("is-loading"); b.removeAttribute("aria-busy"); $$("input, textarea, button", F).forEach((x) => x.disabled = false); $$(".field.dis", F).forEach((x) => x.classList.remove("dis")); toast("项目已创建,正在解析招标文件 · 可以离开本页"); }, 2400);
|
||||
const ctl = $$("input, textarea, button:not([data-submit]), .select", F);
|
||||
ctl.forEach((x) => x.disabled = true);
|
||||
$$(".field", F).forEach((x) => x.classList.add("dis"));
|
||||
$("[data-drop]").setAttribute("aria-disabled", "true"); $("[data-drop]").style.pointerEvents = "none";
|
||||
setTimeout(() => {
|
||||
b.classList.remove("is-loading"); b.removeAttribute("aria-busy");
|
||||
ctl.forEach((x) => x.disabled = false); $$(".field.dis", F).forEach((x) => x.classList.remove("dis"));
|
||||
$("[data-drop]").removeAttribute("aria-disabled"); $("[data-drop]").style.pointerEvents = "";
|
||||
toast("项目已创建,正在解析招标文件,可以离开本页");
|
||||
}, 2400);
|
||||
});
|
||||
addEventListener("DOMContentLoaded", () => { // 等 demo.js 初始化完日期输入
|
||||
if (new URLSearchParams(location.search).get("state") === "errors") {
|
||||
$("#n-due").value = "2026-11-20 25:00";
|
||||
Object.keys(RULES).forEach(check); summary(false);
|
||||
}
|
||||
});
|
||||
|
||||
/* ---------- 资质对话框 ---------- */
|
||||
/* ---------- 03 资质录入:到期按项目的投标截止日判断(智慧大厅 11月20日),只提醒,不拦截 ---------- */
|
||||
const DUE = new Date(2026, 10, 20);
|
||||
const expF = $("#f-qexp"), expHint = $("[data-exp-hint]", expF), expDT = $("[data-dt]", expF);
|
||||
expF.addEventListener("dt-change", (e) => {
|
||||
const d = e.detail, late = d.ok && !d.empty && d.dt < DUE;
|
||||
if (late) delete expHint.dataset.off; else expHint.dataset.off = "";
|
||||
expHint.hidden = !late || expF.classList.contains("err");
|
||||
});
|
||||
$("[data-long]").addEventListener("click", () => setTimeout(() => {
|
||||
const long = $("[data-long]").getAttribute("aria-checked") === "true";
|
||||
$("#q-exp").disabled = long; $(".icon-btn", expDT).disabled = long;
|
||||
if (long) { expHint.dataset.off = ""; expHint.hidden = true; setErr(expF, ""); } else expDT.dt.commit({ required: true });
|
||||
}));
|
||||
|
||||
$("[data-qadd]").onclick = () => dialog({
|
||||
title: "添加资质", wide: true, ok: "保存资质",
|
||||
footLeft: '<span class="cap muted">不扣额度</span>',
|
||||
body: `<p class="muted">数据只来自你录入的内容,系统不从外部补数据。</p><div class="form">
|
||||
<div class="field span2" id="qd-name"><label for="qdn">名称 <small>(必填)</small></label><input class="input" id="qdn" placeholder="例如:信息系统建设和服务能力等级证书"></div>
|
||||
<div class="field"><label>类型 <small>(必填)</small></label><div class="pop" data-select><button type="button" class="select" aria-haspopup="listbox" aria-expanded="false"><span class="val">资质证书</span>${icon("chev-down")}</button><ul class="menu" role="listbox"><li role="option" aria-selected="true">资质证书</li><li role="option">体系认证</li><li role="option">软件著作权</li><li role="option">其他</li></ul></div></div>
|
||||
<div class="field"><label for="qdl">等级 <small>(选填)</small></label><input class="input" id="qdl"></div>
|
||||
<div class="field" id="qd-no"><label for="qdo">证书编号 <small>(必填)</small></label><input class="input" id="qdo"></div>
|
||||
<div class="field"><label>有效期至</label><div class="pop" data-date><button type="button" class="select date-trigger" aria-haspopup="dialog" aria-expanded="false"><span class="val"><span class="ph">选择日期</span></span>${icon("calendar")}</button><div class="cal"></div></div></div>
|
||||
<div class="field span2"><label>证书扫描件 <small>(可以稍后补录)</small></label><div class="drop" style="padding:var(--sp-4)">${icon("upload")}<span>拖入或点击上传 PDF / 图片</span></div></div></div>`,
|
||||
onOk: (s) => {
|
||||
let bad = 0;
|
||||
[["qd-name", "#qdn", "请填写资质名称,和证书上的写法一致"], ["qd-no", "#qdo", "请填写证书编号,查标时用它核对附件"]].forEach(([id, sel, msg]) => {
|
||||
const f = $("#" + id, s), v = $(sel, s).value.trim();
|
||||
f.classList.toggle("err", !v); $(".msg", f)?.remove();
|
||||
if (!v) { bad++; f.insertAdjacentHTML("beforeend", `<span class="msg"><span class="ico w">!</span>${msg}</span>`); }
|
||||
body: `<div class="form">
|
||||
<div class="field span2" id="qd-name"><label for="qdn">名称 <small>(必填)</small></label><input class="input" id="qdn" placeholder="和证书上的写法一致" autocomplete="off"></div>
|
||||
<div class="field"><label id="qdt-l">类型 <small>(必填)</small></label><div class="pop" data-select><button type="button" class="select" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="qdt-l"><span class="val">资质证书</span>${icon("chev-down")}</button><ul class="menu" role="listbox"><li role="option" aria-selected="true">资质证书</li><li role="option">体系认证</li><li role="option">软件著作权</li><li role="option">其他</li></ul></div></div>
|
||||
<div class="field"><label for="qdl">等级 <small>(选填)</small></label><input class="input" id="qdl" placeholder="例如:CS3、二级"></div>
|
||||
<div class="field" id="qd-no"><label for="qdo">证书编号 <small>(必填)</small></label><input class="input" id="qdo" autocomplete="off"></div>
|
||||
<div class="field" id="qd-exp"><label for="qde">有效期至 <small>(必填)</small></label><div class="dt" data-dt data-marks="2026-11-20" data-note="圆点是进行中项目的投标截止日" data-req="请填写有效期"><input class="input" id="qde" placeholder="例如:2026-11-20" autocomplete="off"><button type="button" class="icon-btn sm" aria-label="打开日期选择器" aria-haspopup="dialog" aria-expanded="false">${icon("calendar")}</button><div class="cal" role="dialog" aria-label="选择有效期" hidden></div></div></div>
|
||||
<div class="field span2"><label>证书扫描件 <small>(可以稍后补录)</small></label><div class="drop" tabindex="0" role="button" style="padding:var(--sp-4)">${icon("upload")}<span>拖入或点击上传 PDF / 图片</span></div></div></div>`,
|
||||
onOpen: (s) => {
|
||||
const R = {
|
||||
"qd-name": () => $("#qdn", s).value.trim() ? "" : "请填写资质名称,和证书上的写法一致",
|
||||
"qd-no": () => $("#qdo", s).value.trim() ? "" : "请填写证书编号,查标时用它核对附件"
|
||||
};
|
||||
const exp = $("#qd-exp [data-dt]", s);
|
||||
s._check = () => [...Object.entries(R).map(([id, fn]) => setErr($("#" + id, s), fn())), exp.dt.commit({ required: true })].every(Boolean);
|
||||
Object.entries(R).forEach(([id, fn]) => {
|
||||
const f = $("#" + id, s), inp = $("input", f); let d = false;
|
||||
inp.addEventListener("input", () => { d = true; if (f.classList.contains("err") && !fn()) setErr(f, ""); });
|
||||
inp.addEventListener("blur", () => { if (d || f.classList.contains("err")) setErr(f, fn()); });
|
||||
});
|
||||
if (bad) return false;
|
||||
},
|
||||
onOk: (s) => {
|
||||
if (!s._check()) { $(".field.err input", s).focus(); return false; }
|
||||
toast("已保存资质");
|
||||
}
|
||||
});
|
||||
|
||||
/* ---------- 忽略原因 ---------- */
|
||||
const ig = $("[data-ign]"), ta = $("#ig1", ig), fld = $(".field", ig);
|
||||
const validate = (show) => { const n = ta.value.trim().length; $("[data-n]", ig).textContent = ta.value.length; const bad = n < 5; if (show || !bad) { fld.classList.toggle("err", bad); $(".msg", fld).innerHTML = bad ? '<span class="ico w">!</span>至少写 5 个字,说明为什么不处理' : "报告和处理记录里会显示这段原因"; } return !bad; };
|
||||
ta.addEventListener("input", () => validate(false));
|
||||
ta.addEventListener("blur", () => validate(true));
|
||||
$$("[data-chip]", ig).forEach((c) => c.onclick = () => { ta.value = c.textContent; validate(true); ta.focus(); });
|
||||
$("[data-ign-ok]").onclick = () => { if (validate(true)) toast("已忽略 · 原因已记录", { undo: () => toast("已撤销", { ok: false }) }); };
|
||||
/* ---------- 04 忽略原因 / 致命确认说明 ---------- */
|
||||
const NOW = "11月3日 14:30";
|
||||
$$("[data-u4]").forEach((box) => {
|
||||
const kind = box.dataset.u4, ta = $("textarea", box), fld = $(".field", box), msg = $(".msg", fld), def = msg.dataset.def;
|
||||
const bad = () => ta.value.trim().length < 5;
|
||||
const show = (on) => { fld.classList.toggle("err", on); ta.toggleAttribute("aria-invalid", on); msg.innerHTML = on ? `<span class="ico w" aria-hidden="true">!</span>${kind === "ign" ? "至少写 5 个字,说明为什么不处理" : "请写一句说明,至少 5 个字"}` : def; };
|
||||
ta.addEventListener("input", () => { $("[data-n]", box).textContent = ta.value.length; if (fld.classList.contains("err") && !bad()) show(false); });
|
||||
ta.addEventListener("blur", () => show(bad()));
|
||||
$$("[data-chip]", box).forEach((c) => c.onclick = () => { ta.value = c.textContent; $("[data-n]", box).textContent = ta.value.length; show(false); ta.focus(); });
|
||||
$("[data-x]", box).onclick = () => toast("(演示)取消:收起输入框", { ok: false });
|
||||
const okBtn = $("[data-ok]", box);
|
||||
okBtn.onclick = () => {
|
||||
if (bad()) { show(true); ta.focus(); return; }
|
||||
const done = () => {
|
||||
const html = kind === "ign" // 署名行(共用 .sign):「已忽略」不是确认,用辅助灰和「–」
|
||||
? `<div class="sign ign"><span class="ok" aria-hidden="true">–</span><b>王敏</b><span class="fig">${NOW}</span><span>已忽略</span><button type="button" class="undo" data-undo>撤销</button></div><p class="sign-note">原因:${esc(ta.value.trim())}</p>`
|
||||
: `<div class="sign"><span class="ok" aria-hidden="true">✓</span><b>王敏</b><span class="fig">${NOW}</span><span>确认无误</span><button type="button" class="undo" data-undo>撤销</button></div><p class="sign-note">说明:${esc(ta.value.trim())} · 已作为误报反馈提交</p>`;
|
||||
box.hidden = true;
|
||||
box.insertAdjacentHTML("afterend", `<div class="u4-done">${html}</div>`);
|
||||
const d = box.nextElementSibling;
|
||||
$("[data-undo]", d).onclick = () => { d.remove(); box.hidden = false; ta.focus(); toast("已撤销", { ok: false }); };
|
||||
$("[data-undo]", d).focus();
|
||||
toast(kind === "ign" ? "已忽略 · 原因已记录" : "已确认无误 · 说明已作为反馈提交");
|
||||
};
|
||||
if (kind === "ok") { okBtn.classList.add("is-loading"); okBtn.setAttribute("aria-busy", "true"); setTimeout(() => { okBtn.classList.remove("is-loading"); okBtn.removeAttribute("aria-busy"); done(); }, 900); }
|
||||
else done();
|
||||
};
|
||||
});
|
||||
document.addEventListener("click", (e) => { if (e.target.closest("[data-demo]")) { e.preventDefault(); toast("(演示)在原文查看器里打开这一页", { ok: false }); } });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user