Files
zkxw/docs/mockups/qc-all-third-party-one-card-demo.html
T
2026-09-03 17:12:38 +08:00

658 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>第三方质检 · 全公司合并为一卡 — Demo</title>
<style>
:root {
--primary: #10b981;
--primary-dark: #059669;
--primary-bg: #ecfdf5;
--text: #0f172a;
--muted: #64748b;
--border: #e2e8f0;
--panel: #fff;
--page: #f1f5f9;
--warn: #d97706;
--danger: #dc2626;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Inter, "PingFang SC", "Helvetica Neue", system-ui, sans-serif;
font-size: 14px;
line-height: 1.5;
color: var(--text);
background: var(--page);
padding: 24px;
}
h1 {
margin: 0 0 8px;
font-size: 20px;
font-weight: 700;
}
.lead {
margin: 0 0 20px;
color: var(--muted);
max-width: 920px;
}
.compare {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 20px;
}
@media (max-width: 960px) {
.compare { grid-template-columns: 1fr; }
}
.panel {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
}
.panel-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 12px;
padding-bottom: 10px;
border-bottom: 1px solid var(--border);
}
.panel-head h2 {
margin: 0;
font-size: 15px;
display: flex;
align-items: center;
gap: 8px;
}
.panel-head h2::before {
content: "";
width: 3px;
height: 14px;
border-radius: 2px;
background: var(--primary);
}
.badge {
display: inline-flex;
align-items: center;
padding: 2px 8px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
}
.badge-old { background: #f1f5f9; color: var(--muted); }
.badge-new { background: var(--primary-bg); color: var(--primary-dark); }
.batch-hint {
font-size: 12px;
color: var(--muted);
margin-bottom: 10px;
}
.cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.card {
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px;
background: #fff;
cursor: default;
min-height: 88px;
display: flex;
flex-direction: column;
gap: 8px;
}
.card--lab { background: #fafafa; }
.card--company {
border-color: #a7f3d0;
background: linear-gradient(180deg, #f0fdf4 0%, #fff 48%);
}
.card--clickable {
cursor: pointer;
transition: box-shadow .15s, border-color .15s;
}
.card--clickable:hover {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(16, 185, 129, .15);
}
.card--clickable:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 2px;
}
.card__name {
font-weight: 600;
font-size: 14px;
color: #1e293b;
}
.card__sub {
font-size: 12px;
color: var(--muted);
}
.card__tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.tag {
display: inline-flex;
padding: 1px 7px;
border-radius: 4px;
font-size: 12px;
line-height: 1.6;
}
.tag.success { background: var(--primary-bg); color: var(--primary-dark); }
.tag.warning { background: #fff7ed; color: var(--warn); }
.tag.info { background: #f1f5f9; color: #475569; }
.tag.primary { background: #eff6ff; color: #2563eb; }
.card__foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-top: auto;
}
.assignees { font-size: 12px; color: var(--muted); }
.btn {
appearance: none;
border: 1px solid var(--border);
background: #fff;
color: var(--text);
border-radius: 8px;
height: 28px;
padding: 0 10px;
font-size: 12px;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 4px;
}
.btn-main {
background: var(--primary);
border-color: var(--primary);
color: #fff;
}
.note {
margin-top: 12px;
padding: 10px 12px;
border-radius: 8px;
background: #fffbeb;
border: 1px solid #fde68a;
color: #92400e;
font-size: 13px;
}
.note strong { font-weight: 600; }
.arrow-row {
text-align: center;
color: var(--muted);
font-size: 13px;
margin: 4px 0 16px;
}
/* Dialog mock */
.overlay {
position: fixed;
inset: 0;
background: rgba(15, 23, 42, .45);
display: none;
align-items: center;
justify-content: center;
padding: 24px;
z-index: 50;
}
.overlay.is-open { display: flex; }
.dialog {
width: min(920px, 100%);
max-height: calc(100vh - 48px);
background: #fff;
border-radius: 10px;
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow: 0 20px 50px rgba(15, 23, 42, .2);
}
.dialog__head {
padding: 14px 20px;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.dialog__title { font-size: 16px; font-weight: 650; margin: 0; }
.dialog__body {
padding: 16px 20px;
overflow: auto;
background: #f8fafc;
}
.dialog__foot {
padding: 12px 20px;
border-top: 1px solid var(--border);
display: flex;
justify-content: flex-end;
gap: 8px;
background: #fff;
}
.item-block {
background: #fafafa;
border-radius: 8px;
padding: 16px 18px;
margin-bottom: 12px;
border: 1px solid #f1f5f9;
}
.item-block__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
}
.item-block__name {
font-size: 15px;
font-weight: 600;
color: #1e293b;
min-width: 0;
}
.item-block__company {
color: var(--muted);
font-weight: 500;
font-size: 13px;
}
.section-title {
position: relative;
font-size: 12px;
font-weight: 600;
color: #334155;
margin: 0 0 10px;
padding: 0 0 8px 8px;
border-bottom: 1px solid #ebeef5;
}
.section-title::before {
content: "";
position: absolute;
left: 0;
top: 3px;
width: 2px;
height: 10px;
border-radius: 1px;
background: #047857;
}
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.field label {
display: block;
font-size: 12px;
color: #475569;
margin-bottom: 6px;
}
.radios {
display: flex;
flex-wrap: wrap;
gap: 12px;
font-size: 13px;
}
.shared {
background: #fafafa;
border-radius: 8px;
padding: 16px 18px;
border: 1px solid #f1f5f9;
}
textarea, .fake-upload {
width: 100%;
border: 1px solid var(--border);
border-radius: 8px;
padding: 8px 10px;
font: inherit;
background: #fff;
}
.fake-upload {
min-height: 72px;
color: var(--muted);
display: flex;
align-items: center;
justify-content: center;
border-style: dashed;
border-color: #a7f3d0;
background: #f0fdf4;
}
.close-x {
border: none;
background: transparent;
font-size: 20px;
line-height: 1;
color: var(--muted);
cursor: pointer;
padding: 4px 8px;
}
.legend {
display: flex;
flex-wrap: wrap;
gap: 10px 16px;
margin-bottom: 16px;
font-size: 13px;
color: var(--muted);
}
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.swatch {
width: 14px; height: 14px; border-radius: 4px; border: 1px solid var(--border);
}
.swatch.lab { background: #fafafa; }
.swatch.tp { background: #f0fdf4; border-color: #a7f3d0; }
</style>
</head>
<body>
<h1>第三方质检 · 全公司合并为一张卡</h1>
<p class="lead">
需求示意:列表里不再「一家公司一张卡」,改为批次内所有第三方任务合成 <strong>一张「第三方质检项目」卡</strong>;
点击后弹窗列出全部第三方检测项目,项目名后带上公司名。检测参数区保持隐藏(仅状态/结论 + 共享备注材料)。
</p>
<div class="legend">
<span><i class="swatch lab"></i>实验室自检(仍一项目一卡)</span>
<span><i class="swatch tp"></i>第三方聚合卡</span>
<span>右侧提案卡可点击打开弹窗 Demo</span>
</div>
<div class="compare">
<!-- 现状 -->
<section class="panel">
<div class="panel-head">
<h2>现状 · 按公司分卡</h2>
<span class="badge badge-old">当前实现</span>
</div>
<div class="batch-hint">检测批次 A · 任务区</div>
<div class="cards">
<div class="card card--lab">
<div class="card__name">细胞活力检测</div>
<div class="card__tags">
<span class="tag warning">进行中</span>
<span class="tag info">实验室自检</span>
</div>
<div class="card__foot">
<span class="assignees">张三</span>
<button class="btn btn-main" type="button" tabindex="-1">填写结果</button>
</div>
</div>
<div class="card card--company">
<div class="card__name">金域医学</div>
<div class="card__sub">第三方 · 2 个检测项目</div>
<div class="card__tags">
<span class="tag warning">1/2 已完成</span>
<span class="tag primary">第三方检测</span>
</div>
<div class="card__foot">
<span class="assignees">李四、王五</span>
<button class="btn btn-main" type="button" tabindex="-1">填写结果</button>
</div>
</div>
<div class="card card--company">
<div class="card__name">迪安诊断</div>
<div class="card__sub">第三方 · 1 个检测项目</div>
<div class="card__tags">
<span class="tag info">0/1 已完成</span>
<span class="tag primary">第三方检测</span>
</div>
<div class="card__foot">
<span class="assignees">赵六</span>
<button class="btn btn-main" type="button" tabindex="-1">填写结果</button>
</div>
</div>
<div class="card card--company">
<div class="card__name">华大基因</div>
<div class="card__sub">第三方 · 2 个检测项目</div>
<div class="card__tags">
<span class="tag success">2/2 已完成</span>
<span class="tag primary">第三方检测</span>
</div>
<div class="card__foot">
<span class="assignees">钱七</span>
<button class="btn" type="button" tabindex="-1">查看</button>
</div>
</div>
</div>
<p class="note"><strong>痛点:</strong>公司多时卡片碎;要分别点开多家公司弹窗才能填完。</p>
</section>
<!-- 提案 -->
<section class="panel">
<div class="panel-head">
<h2>提案 · 全第三方合一卡</h2>
<span class="badge badge-new">需求方向</span>
</div>
<div class="batch-hint">检测批次 A · 任务区 · 点击绿色卡体验弹窗</div>
<div class="cards">
<div class="card card--lab">
<div class="card__name">细胞活力检测</div>
<div class="card__tags">
<span class="tag warning">进行中</span>
<span class="tag info">实验室自检</span>
</div>
<div class="card__foot">
<span class="assignees">张三</span>
<button class="btn btn-main" type="button" tabindex="-1">填写结果</button>
</div>
</div>
<div
class="card card--company card--clickable"
id="open-dialog-card"
role="button"
tabindex="0"
data-option="open-all-third-dialog"
aria-label="打开第三方质检项目弹窗"
>
<div class="card__name">第三方质检项目</div>
<div class="card__sub">第三方 · 5 个检测项目 · 3 家公司</div>
<div class="card__tags">
<span class="tag warning">3/5 已完成</span>
<span class="tag primary">第三方检测</span>
</div>
<div class="card__foot">
<span class="assignees">李四、王五、赵六、钱七</span>
<button class="btn btn-main" type="button" id="open-dialog-btn">填写结果</button>
</div>
</div>
</div>
<p class="note"><strong>目标:</strong>一张卡汇总全部第三方;弹窗内项目名后标注公司,一次填完本批次可见的第三方任务。</p>
</section>
</div>
<p class="arrow-row">下方为弹窗结构说明(也可点右侧卡片打开可交互层)</p>
<!-- static preview of dialog content -->
<section class="panel" style="max-width: 920px; margin: 0 auto;">
<div class="panel-head">
<h2>弹窗结构预览 · 填写检测结果 · 第三方质检项目</h2>
<button class="btn btn-main" type="button" id="open-dialog-btn-2">打开可交互弹窗</button>
</div>
<div class="item-block">
<div class="item-block__head">
<div class="item-block__name">
1. HIV-1 RNA 定量
<span class="item-block__company">· 金域医学</span>
</div>
<span class="tag warning">待执行</span>
</div>
<div class="section-title">检测结论</div>
<div class="grid-2">
<div class="field"><label>任务状态</label><div class="radios">○ 待执行 ● 已完成</div></div>
<div class="field"><label>结论</label><div class="radios">○ 待结论 ● 通过 ○ 不通过</div></div>
</div>
</div>
<div class="item-block">
<div class="item-block__head">
<div class="item-block__name">
2. 乙肝五项
<span class="item-block__company">· 金域医学</span>
</div>
<span class="tag success">已完成</span>
</div>
<div class="section-title">检测结论</div>
<div class="grid-2">
<div class="field"><label>任务状态</label><div class="radios">○ 待执行 ● 已完成</div></div>
<div class="field"><label>结论</label><div class="radios">○ 待结论 ● 通过 ○ 不通过</div></div>
</div>
</div>
<div class="item-block">
<div class="item-block__head">
<div class="item-block__name">
3. 支原体检测
<span class="item-block__company">· 迪安诊断</span>
</div>
<span class="tag info">待执行</span>
</div>
<div class="section-title">检测结论</div>
<div class="grid-2">
<div class="field"><label>任务状态</label><div class="radios">● 待执行 ○ 已完成</div></div>
<div class="field"><label>结论</label><div class="radios">● 待结论 ○ 通过 ○ 不通过</div></div>
</div>
</div>
<div class="item-block">
<div class="item-block__head">
<div class="item-block__name">
4. 染色体核型分析
<span class="item-block__company">· 华大基因</span>
</div>
<span class="tag success">已完成</span>
</div>
</div>
<div class="item-block">
<div class="item-block__head">
<div class="item-block__name">
5. STR 比对
<span class="item-block__company">· 华大基因</span>
</div>
<span class="tag success">已完成</span>
</div>
</div>
<div class="shared">
<div class="section-title">备注与检测材料(整弹窗一份,同步到本次可写任务)</div>
<div class="field" style="margin-bottom:12px">
<label>备注</label>
<textarea rows="2" placeholder="选填"></textarea>
</div>
<div class="field">
<label>检测材料</label>
<div class="fake-upload">上传区示意 · 任一项「已完成」时必填</div>
</div>
</div>
</section>
<div class="overlay" id="overlay" aria-hidden="true">
<div class="dialog" role="dialog" aria-modal="true" aria-labelledby="dlg-title">
<div class="dialog__head">
<h3 class="dialog__title" id="dlg-title">填写检测结果 · 第三方质检项目</h3>
<button class="close-x" type="button" id="close-dialog" aria-label="关闭">×</button>
</div>
<div class="dialog__body">
<div class="item-block">
<div class="item-block__head">
<div class="item-block__name">1. HIV-1 RNA 定量 <span class="item-block__company">· 金域医学</span></div>
<span class="tag warning">待执行</span>
</div>
<div class="section-title">检测结论</div>
<div class="grid-2">
<div class="field"><label>任务状态 *</label><div class="radios"><label><input type="radio" name="s1" checked /> 待执行</label><label><input type="radio" name="s1" /> 已完成</label></div></div>
<div class="field"><label>结论</label><div class="radios"><label><input type="radio" name="c1" checked /> 待结论</label><label><input type="radio" name="c1" /> 通过</label><label><input type="radio" name="c1" /> 不通过</label></div></div>
</div>
</div>
<div class="item-block">
<div class="item-block__head">
<div class="item-block__name">2. 乙肝五项 <span class="item-block__company">· 金域医学</span></div>
<span class="tag success">已完成</span>
</div>
<div class="section-title">检测结论</div>
<div class="grid-2">
<div class="field"><label>任务状态 *</label><div class="radios"><label><input type="radio" name="s2" /> 待执行</label><label><input type="radio" name="s2" checked /> 已完成</label></div></div>
<div class="field"><label>结论 *</label><div class="radios"><label><input type="radio" name="c2" /> 待结论</label><label><input type="radio" name="c2" checked /> 通过</label><label><input type="radio" name="c2" /> 不通过</label></div></div>
</div>
</div>
<div class="item-block">
<div class="item-block__head">
<div class="item-block__name">3. 支原体检测 <span class="item-block__company">· 迪安诊断</span></div>
<span class="tag info">待执行</span>
</div>
<div class="section-title">检测结论</div>
<div class="grid-2">
<div class="field"><label>任务状态 *</label><div class="radios"><label><input type="radio" name="s3" checked /> 待执行</label><label><input type="radio" name="s3" /> 已完成</label></div></div>
<div class="field"><label>结论</label><div class="radios"><label><input type="radio" name="c3" checked /> 待结论</label><label><input type="radio" name="c3" /> 通过</label><label><input type="radio" name="c3" /> 不通过</label></div></div>
</div>
</div>
<div class="item-block">
<div class="item-block__head">
<div class="item-block__name">4. 染色体核型分析 <span class="item-block__company">· 华大基因</span></div>
<span class="tag success">已完成</span>
</div>
<div class="section-title">检测结论</div>
<div class="grid-2">
<div class="field"><label>任务状态 *</label><div class="radios"><label><input type="radio" name="s4" /> 待执行</label><label><input type="radio" name="s4" checked /> 已完成</label></div></div>
<div class="field"><label>结论 *</label><div class="radios"><label><input type="radio" name="c4" /> 待结论</label><label><input type="radio" name="c4" checked /> 通过</label><label><input type="radio" name="c4" /> 不通过</label></div></div>
</div>
</div>
<div class="item-block">
<div class="item-block__head">
<div class="item-block__name">5. STR 比对 <span class="item-block__company">· 华大基因</span></div>
<span class="tag success">已完成</span>
</div>
<div class="section-title">检测结论</div>
<div class="grid-2">
<div class="field"><label>任务状态 *</label><div class="radios"><label><input type="radio" name="s5" /> 待执行</label><label><input type="radio" name="s5" checked /> 已完成</label></div></div>
<div class="field"><label>结论 *</label><div class="radios"><label><input type="radio" name="c5" /> 待结论</label><label><input type="radio" name="c5" checked /> 通过</label><label><input type="radio" name="c5" /> 不通过</label></div></div>
</div>
</div>
<div class="shared">
<div class="section-title">备注与检测材料</div>
<div class="field" style="margin-bottom:12px">
<label>备注</label>
<textarea rows="3" placeholder="选填(同步到本次弹窗可写任务)"></textarea>
</div>
<div class="field">
<label>检测材料</label>
<div class="fake-upload">点击上传检测材料(示意)</div>
</div>
</div>
</div>
<div class="dialog__foot">
<button class="btn" type="button" id="close-dialog-2">取消</button>
<button class="btn btn-main" type="button" data-option="demo-save-ok">保存</button>
</div>
</div>
</div>
<script>
(function () {
var overlay = document.getElementById('overlay')
function openDlg() {
overlay.classList.add('is-open')
overlay.setAttribute('aria-hidden', 'false')
}
function closeDlg() {
overlay.classList.remove('is-open')
overlay.setAttribute('aria-hidden', 'true')
}
document.getElementById('open-dialog-card').addEventListener('click', openDlg)
document.getElementById('open-dialog-card').addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openDlg() }
})
document.getElementById('open-dialog-btn').addEventListener('click', function (e) {
e.stopPropagation(); openDlg()
})
document.getElementById('open-dialog-btn-2').addEventListener('click', openDlg)
document.getElementById('close-dialog').addEventListener('click', closeDlg)
document.getElementById('close-dialog-2').addEventListener('click', closeDlg)
overlay.addEventListener('click', function (e) {
if (e.target === overlay) closeDlg()
})
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') closeDlg()
})
})()
</script>
</body>
</html>