Files
zkxw/mobile-ui/zkxw-mobile-oa-protocol-form-beautify-demo.html
T
2026-10-15 23:21:07 +08:00

1073 lines
44 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, maximum-scale=1" />
<title>协议单 · 新增/编辑表单美化 Demo(销售专员 / 地区经理)</title>
<style>
:root {
--brand: #0a9f6d;
--brand-dark: #087f57;
--brand-soft: #e9f9f2;
--brand-mid: #b8ebd4;
--barley: #d6a343;
--barley-soft: #fff7e8;
--text: #1f2a26;
--text-muted: #6b7b74;
--border: #dcebe4;
--page-bg: #f5f8f7;
--surface: #ffffff;
--danger: #d14343;
--warn: #cf8a19;
--shadow: 0 8px 28px rgba(15, 42, 34, 0.06);
--radius-card: 16px;
--radius-ctl: 12px;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
font-family: "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", -apple-system, system-ui, sans-serif;
color: var(--text);
background:
radial-gradient(900px 420px at 8% -8%, rgba(10, 159, 109, 0.14), transparent 55%),
radial-gradient(700px 380px at 92% 0%, rgba(214, 163, 67, 0.1), transparent 50%),
linear-gradient(180deg, #f8fffc 0%, #f4f7fb 45%, #f7f8fa 100%);
line-height: 1.45;
}
.stage { max-width: 1280px; margin: 0 auto; padding: 28px 16px 72px; }
.hero h1 { margin: 0 0 8px; font-size: 24px; font-weight: 720; letter-spacing: -0.02em; }
.hero p { margin: 0; max-width: 860px; color: #64748b; font-size: 14px; }
.hero .meta {
margin-top: 12px; display: flex; flex-wrap: wrap; gap: 8px;
}
.hero .chip {
display: inline-flex; align-items: center; gap: 6px;
padding: 6px 12px; border-radius: 999px; font-size: 12px; font-weight: 600;
background: #fff; border: 1px solid #e2e8f0; color: #475569;
}
.hero .chip strong { color: var(--brand-dark); }
.pain {
margin: 18px 0 0;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
}
.pain-item {
background: #fff;
border: 1px solid #fecaca;
border-radius: 12px;
padding: 10px 12px;
font-size: 12px;
color: #7f1d1d;
}
.pain-item b { color: #b91c1c; }
.tabs {
display: flex; flex-wrap: wrap; gap: 10px; margin: 22px 0 18px;
}
.tab {
appearance: none; border: 1px solid #e2e8f0; background: #fff;
border-radius: 999px; padding: 10px 16px; font-size: 13px;
font-weight: 600; color: #475569; cursor: pointer;
transition: .15s border-color, .15s box-shadow, .15s color, .15s background;
}
.tab:hover { border-color: #94a3b8; }
.tab.is-active {
color: var(--brand-dark); border-color: var(--brand);
background: var(--brand-soft);
box-shadow: 0 0 0 3px rgba(10, 159, 109, 0.18);
}
.layout {
display: grid;
grid-template-columns: minmax(280px, 360px) 1fr;
gap: 22px;
align-items: start;
}
@media (max-width: 960px) {
.layout { grid-template-columns: 1fr; }
}
.panel {
background: rgba(255,255,255,.92);
border: 1px solid #e2e8f0;
border-radius: 18px;
padding: 18px;
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.05);
position: sticky;
top: 16px;
}
.panel h2 { margin: 0 0 4px; font-size: 16px; }
.panel .sub { margin: 0 0 14px; font-size: 12px; color: #64748b; }
.panel ol { margin: 0; padding-left: 18px; font-size: 13px; color: #475569; }
.panel li { margin: 0 0 8px; }
.panel li strong { color: var(--brand-dark); }
.legend {
margin-top: 16px; padding-top: 14px; border-top: 1px dashed #e2e8f0;
display: grid; gap: 8px; font-size: 12px; color: #64748b;
}
.legend span {
display: flex; align-items: center; gap: 8px;
}
.swatch {
width: 14px; height: 14px; border-radius: 4px; flex: none;
}
.phones {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 20px;
}
.phone-wrap { display: flex; flex-direction: column; gap: 10px; }
.phone-label {
display: flex; align-items: center; justify-content: space-between;
padding: 0 4px;
}
.phone-label h3 { margin: 0; font-size: 15px; font-weight: 700; }
.phone-label .tag {
font-size: 11px; font-weight: 700; padding: 4px 8px; border-radius: 999px;
}
.tag-old { background: #f1f5f9; color: #64748b; }
.tag-new { background: var(--brand-soft); color: var(--brand-dark); }
.phone {
width: 100%;
max-width: 390px;
margin: 0 auto;
height: 780px;
border-radius: 36px;
border: 10px solid #0f172a;
background: var(--page-bg);
box-shadow: 0 24px 60px rgba(15, 23, 42, 0.18);
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone::before {
content: "";
position: absolute;
top: 8px; left: 50%; transform: translateX(-50%);
width: 108px; height: 22px; border-radius: 12px;
background: #0f172a; z-index: 5;
}
/* ========== 当前样式(对照) ========== */
.old-page {
flex: 1; overflow: auto; padding: 44px 12px 88px;
background: #f5f8f7;
}
.old-nav {
position: sticky; top: 0; z-index: 2;
margin: -44px -12px 12px; padding: 40px 14px 10px;
background: #fff; border-bottom: 1px solid var(--border);
text-align: center; font-size: 16px; font-weight: 650;
}
.old-section {
margin-bottom: 12px; padding: 16px;
border: 1px solid var(--border); border-radius: 16px; background: #fff;
}
.old-section h4 {
margin: 0 0 14px; padding-left: 10px;
border-left: 3px solid var(--brand); font-size: 15px; font-weight: 650;
}
.old-field { display: grid; gap: 7px; margin-bottom: 14px; color: var(--text-muted); font-size: 13px; }
.old-field .ctl {
min-height: 44px; border: 1px solid var(--border); border-radius: 10px;
background: #fff; padding: 0 12px; display: flex; align-items: center;
color: #94a3b8; font-size: 14px;
}
.old-field .ctl.has-val { color: var(--text); }
.old-tabs { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.old-tabs button {
min-height: 44px; border: 1px solid var(--border); border-radius: 10px;
background: #fff; color: var(--text); font-size: 14px;
}
.old-tabs button.on {
border-color: var(--brand); background: var(--brand-soft);
color: var(--brand-dark); font-weight: 600;
}
.old-bottom {
position: absolute; left: 0; right: 0; bottom: 0;
display: flex; gap: 10px; justify-content: center;
padding: 10px 14px 18px;
border-top: 1px solid var(--border); background: rgb(255 255 255 / 96%);
}
.old-bottom .btn {
min-height: 44px; min-width: 120px; max-width: 168px; flex: 0 1 168px;
border-radius: 999px; font-size: 15px; font-weight: 600; border: 1px solid var(--border);
background: #fff; color: var(--text);
}
.old-bottom .btn.primary {
border-color: var(--brand); background: var(--brand); color: #fff;
}
/* ========== 美化方案 ========== */
.new-page {
flex: 1; overflow: auto;
padding: 0 0 96px;
background:
linear-gradient(180deg, #e9f9f2 0%, #f5f8f7 120px, #f5f8f7 100%);
}
.new-nav {
position: sticky; top: 0; z-index: 3;
padding: 36px 14px 0;
background: linear-gradient(180deg, #d8f5e8 0%, #e9f9f2 70%, rgba(233,249,242,0.92) 100%);
backdrop-filter: blur(8px);
}
.new-nav__bar {
display: grid; grid-template-columns: 40px 1fr 40px;
align-items: center; margin-bottom: 10px;
}
.new-nav__back {
width: 36px; height: 36px; border-radius: 12px;
display: grid; place-items: center;
background: rgba(255,255,255,.72); border: 1px solid rgba(10,159,109,.18);
color: var(--brand-dark); font-size: 22px; line-height: 1;
}
.new-nav__title { text-align: center; font-size: 17px; font-weight: 720; color: var(--text); }
.new-nav__mode {
justify-self: end;
font-size: 11px; font-weight: 700; padding: 4px 8px; border-radius: 999px;
background: #fff; color: var(--brand-dark); border: 1px solid var(--brand-mid);
}
.new-steps {
display: flex; gap: 6px; overflow-x: auto; padding: 0 0 12px;
scrollbar-width: none;
}
.new-steps::-webkit-scrollbar { display: none; }
.step-chip {
flex: none; display: inline-flex; align-items: center; gap: 6px;
padding: 7px 12px; border-radius: 999px;
font-size: 12px; font-weight: 650; white-space: nowrap;
background: rgba(255,255,255,.65); color: var(--text-muted);
border: 1px solid transparent;
}
.step-chip .n {
width: 18px; height: 18px; border-radius: 50%;
display: grid; place-items: center; font-size: 10px; font-weight: 800;
background: #e2e8f0; color: #64748b;
}
.step-chip.is-active {
background: #fff; color: var(--brand-dark);
border-color: var(--brand-mid);
box-shadow: 0 2px 8px rgba(10,159,109,.12);
}
.step-chip.is-active .n {
background: var(--brand); color: #fff;
}
.step-chip.is-done .n {
background: var(--brand-mid); color: var(--brand-dark);
}
.new-body { padding: 4px 12px 12px; display: grid; gap: 12px; }
.card {
background: var(--surface);
border-radius: var(--radius-card);
box-shadow: var(--shadow);
border: 1px solid rgba(220, 235, 228, 0.85);
overflow: hidden;
}
.card__head {
display: flex; align-items: center; gap: 10px;
padding: 14px 14px 0;
}
.card__badge {
width: 28px; height: 28px; border-radius: 10px;
display: grid; place-items: center;
font-size: 12px; font-weight: 800; color: #fff;
background: linear-gradient(145deg, #12b981, #087f57);
box-shadow: 0 4px 10px rgba(10,159,109,.28);
}
.card__title {
flex: 1; margin: 0; font-size: 15px; font-weight: 720;
}
.card__hint {
margin: 4px 14px 0; font-size: 11px; color: var(--text-muted);
}
.card__body { padding: 12px 14px 14px; display: grid; gap: 12px; }
.field { display: grid; gap: 6px; }
.field.is-span2 { grid-column: 1 / -1; }
.field__label {
display: flex; align-items: center; gap: 4px;
font-size: 12px; font-weight: 650; color: #3d524a;
}
.field__label .req { color: var(--danger); font-weight: 800; }
.field__label .tip {
margin-left: 4px; font-size: 11px; font-weight: 500; color: var(--text-muted);
}
.field__ctl {
min-height: 46px;
border-radius: var(--radius-ctl);
border: 1px solid transparent;
background: #f4faf7;
padding: 0 14px;
display: flex; align-items: center; justify-content: space-between;
gap: 8px;
font-size: 14px; color: #94a3b8;
transition: border-color .15s, background .15s, box-shadow .15s;
}
.field__ctl.has-val { color: var(--text); background: #f7fbf9; border-color: #e3f0ea; }
.field__ctl:focus-within,
.field__ctl.is-focus {
background: #fff;
border-color: var(--brand);
box-shadow: 0 0 0 3px rgba(10, 159, 109, 0.15);
color: var(--text);
}
.field__ctl .chev { color: #9cb0a7; font-size: 16px; }
.field__ctl.textarea {
min-height: 84px; align-items: flex-start; padding: 12px 14px;
color: var(--text);
}
.field-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.seg {
display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
padding: 4px; border-radius: 14px; background: #eef6f2;
}
.seg button {
min-height: 40px; border: 0; border-radius: 11px;
background: transparent; color: var(--text-muted);
font-size: 13px; font-weight: 650; cursor: pointer;
}
.seg button.on {
background: #fff; color: var(--brand-dark);
box-shadow: 0 2px 8px rgba(15, 42, 34, 0.08);
}
.subgroup {
margin-top: 2px;
padding: 12px;
border-radius: 14px;
background: linear-gradient(180deg, #f8fffc 0%, #f4faf7 100%);
border: 1px solid #e3f0ea;
display: grid; gap: 12px;
}
.subgroup__title {
display: flex; align-items: center; gap: 8px;
font-size: 12px; font-weight: 750; color: var(--brand-dark);
}
.subgroup__title::before {
content: "";
width: 6px; height: 6px; border-radius: 50%;
background: var(--brand);
box-shadow: 0 0 0 3px var(--brand-soft);
}
.money-sync {
display: flex; align-items: center; justify-content: space-between;
gap: 10px; padding: 10px 12px; border-radius: 12px;
background: var(--barley-soft); border: 1px solid #f0e0bc;
font-size: 12px; color: #8a6419;
}
.money-sync strong { color: #92400e; font-size: 14px; }
.sample-card {
padding: 12px; border-radius: 14px;
border: 1px solid #e3f0ea; background: #fff;
display: grid; gap: 8px;
}
.sample-card__head {
display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.sample-card__head strong { font-size: 14px; }
.tag-status {
font-size: 11px; font-weight: 700; padding: 3px 8px; border-radius: 999px;
background: var(--brand-soft); color: var(--brand-dark);
}
.sample-card__meta {
display: flex; flex-wrap: wrap; gap: 6px 10px;
font-size: 12px; color: var(--text-muted);
}
.sample-card__actions { display: flex; gap: 8px; }
.mini-btn {
min-height: 26px; padding: 0 10px; border-radius: 999px;
border: 0; background: #5f8f7d;
font-size: 11px; font-weight: 700; color: #fff;
box-shadow: 0 2px 5px rgba(15,42,34,.12);
}
.mini-btn.danger {
background: linear-gradient(135deg, #e35d5d, #d14343);
box-shadow: 0 2px 5px rgba(209,67,67,.2);
}
.mini-btn.primary {
background: linear-gradient(135deg, #12b981, #087f57);
box-shadow: 0 2px 6px rgba(10,159,109,.28);
}
.mini-btn.warn {
background: linear-gradient(135deg, #e5a63a, #cf8a19);
box-shadow: 0 2px 5px rgba(207,138,25,.22);
}
.toolbar-row {
display: flex; flex-wrap: wrap; gap: 8px; margin-top: 2px;
}
.upload-row {
display: flex; flex-wrap: wrap; gap: 10px;
}
.upload-card {
width: 96px; height: 72px; border-radius: 12px;
border: 1.5px dashed #a7f3d0;
background: #f7fffb;
display: grid; place-items: center; text-align: center;
font-size: 11px; color: var(--brand-dark); font-weight: 650;
gap: 2px;
}
.upload-card .plus { font-size: 20px; line-height: 1; color: var(--brand); }
.upload-file {
width: 96px; height: 72px; border-radius: 12px;
border: 1px solid var(--border); background: #fff;
padding: 8px; display: grid; align-content: space-between;
font-size: 11px; color: var(--text);
}
.upload-file .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upload-file .rm { color: var(--danger); font-weight: 650; }
.confirm-row {
display: flex; align-items: flex-start; gap: 10px;
padding: 12px; border-radius: 14px;
background: var(--brand-soft); border: 1px solid var(--brand-mid);
font-size: 13px; color: var(--text); font-weight: 600;
}
.confirm-row .box {
width: 20px; height: 20px; border-radius: 6px; flex: none; margin-top: 1px;
border: 2px solid var(--brand); background: var(--brand);
color: #fff; display: grid; place-items: center; font-size: 12px;
}
.new-bottom {
position: absolute; left: 0; right: 0; bottom: 0; z-index: 4;
display: flex; gap: 10px;
padding: 12px 14px calc(12px + env(safe-area-inset-bottom, 8px));
background: linear-gradient(180deg, rgba(245,248,247,0), #fff 28%);
}
.new-bottom::before {
content: "";
position: absolute; inset: 0 0 auto 0; height: 1px;
background: linear-gradient(90deg, transparent, rgba(10,159,109,.18), transparent);
}
.new-bottom .btn {
position: relative;
min-height: 40px; flex: 1; border-radius: 12px;
font-size: 14px; font-weight: 700; border: 0; cursor: pointer;
color: #fff;
}
.new-bottom .btn.ghost {
background: #5f8f7d;
box-shadow: 0 3px 8px rgba(15, 42, 34, 0.14);
}
.new-bottom .btn.primary {
background: linear-gradient(135deg, #12b981 0%, #087f57 100%);
box-shadow: 0 6px 16px rgba(10, 159, 109, 0.3);
}
.variant-b .card__head {
padding: 12px 14px;
background: linear-gradient(90deg, #ecfdf5, #fff 60%);
border-bottom: 1px solid #eef6f2;
}
.variant-b .card__body { padding-top: 14px; }
.variant-b .field__ctl {
background: #fff;
border: 1px solid #e3f0ea;
}
.variant-b .field__ctl.has-val { background: #fff; }
.variant-b .seg { background: #f0f7f4; }
.note-bar {
margin-top: 18px;
padding: 14px 16px;
border-radius: 14px;
background: #fff;
border: 1px solid #e2e8f0;
font-size: 13px;
color: #475569;
}
.note-bar code {
font-size: 12px; padding: 2px 6px; border-radius: 6px;
background: #f1f5f9; color: #0f766e;
}
</style>
</head>
<body>
<div class="stage">
<header class="hero">
<h1>协议单 · 新增 / 编辑表单美化</h1>
<p>
面向 <strong>销售专员 / 地区经理</strong>(mobile-oa-h5 市场模块)。
字段与分区顺序:协议信息 → 采集信息 → 客户信息 → 样本及运输 → 缴费 → 附件 → <strong>提交确认(最后)</strong>。
Token 使用三墙绿 <code style="font-size:12px;background:#e9f9f2;padding:2px 6px;border-radius:6px;">#0a9f6d</code>,不混用管理端薄荷主色。
</p>
<div class="meta">
<span class="chip"><strong>工程</strong> mobile-oa-h5</span>
<span class="chip"><strong>入口</strong> 市场 · 协议单</span>
<span class="chip"><strong>模式</strong> 新增 / 编辑</span>
</div>
<div class="pain">
<div class="pain-item"><b>现状问题</b>:白卡片 + 细描边扁平,长表单缺乏节奏</div>
<div class="pain-item"><b>现状问题</b>:label 灰色过淡,字段堆叠一列,滚动过长</div>
<div class="pain-item"><b>现状问题</b>:客户区母亲/父亲/新生儿无分组,扫读成本高</div>
<div class="pain-item"><b>现状问题</b>:底栏与内容粘连感弱,主按钮不够醒目</div>
</div>
</header>
<div class="tabs" role="tablist">
<button type="button" class="tab is-active" data-tab="compare">对照:现状 vs 方案 A</button>
<button type="button" class="tab" data-tab="a">方案 A · 软填充(推荐)</button>
<button type="button" class="tab" data-tab="b">方案 B · 白底描边</button>
</div>
<div class="layout">
<aside class="panel" id="side-panel">
<h2>方案 A · 设计要点</h2>
<p class="sub">推荐落地;与现有 Vant 组件可渐进替换样式</p>
<ol>
<li><strong>顶区进度芯片</strong>:长表单可横向跳读分区</li>
<li><strong>编号卡片头</strong>:分区层级一眼可辨</li>
<li><strong>软填充控件</strong>:#f4faf7 底 + 聚焦描边,减少「框框墙」</li>
<li><strong>客户三分组</strong>:母亲 / 父亲 / 新生儿独立浅底块</li>
<li><strong>短字段双列</strong>:金额、性别、胎次、评分等降滚动</li>
<li><strong>实心小按钮</strong>:表单内操作统一实心,尺寸偏小;底栏取消/保存亦为实心</li>
</ol>
<div class="legend">
<span><i class="swatch" style="background:#0a9f6d"></i> 主色 / 进度 / 主按钮</span>
<span><i class="swatch" style="background:#e9f9f2"></i> 软底 / 选中态</span>
<span><i class="swatch" style="background:#fff7e8"></i> 金额同步提示</span>
<span><i class="swatch" style="background:#f4faf7;border:1px solid #dcebe4"></i> 输入控件默认底</span>
</div>
</aside>
<div class="phones" id="phone-stage">
<!-- 对照:现状 -->
<div class="phone-wrap" data-view="compare">
<div class="phone-label">
<h3>当前样式</h3>
<span class="tag tag-old">AS-IS</span>
</div>
<div class="phone">
<div class="old-page">
<div class="old-nav">新增协议单</div>
<section class="old-section">
<h4>协议信息</h4>
<div class="old-field">
<span>业务来源*</span>
<div class="old-tabs">
<button type="button" class="on">细胞存储</button>
<button type="button">科研捐赠</button>
</div>
</div>
<div class="old-field"><span>协议编号</span><div class="ctl">请输入</div></div>
<div class="old-field"><span>登记日期</span><div class="ctl">请选择</div></div>
<div class="old-field"><span>采集专员</span><div class="ctl">请选择</div></div>
<div class="old-field"><span>协议总金额</span><div class="ctl">请输入</div></div>
<div class="old-field"><span>备注</span><div class="ctl">请输入</div></div>
</section>
<section class="old-section">
<h4>采集信息</h4>
<div class="old-field"><span>市场专员</span><div class="ctl has-val">张专员</div></div>
<div class="old-field"><span>采集地区</span><div class="ctl">请选择</div></div>
<div class="old-field"><span>采集医院</span><div class="ctl">请选择</div></div>
</section>
<section class="old-section">
<h4>客户信息</h4>
<div class="old-field"><span>母亲姓名*</span><div class="ctl has-val">王女士</div></div>
<div class="old-field"><span>联系电话</span><div class="ctl">请输入</div></div>
<div class="old-field"><span>父亲姓名</span><div class="ctl">请输入</div></div>
<div class="old-field"><span>新生儿姓名</span><div class="ctl">请输入</div></div>
</section>
</div>
<div class="old-bottom">
<button type="button" class="btn">取消</button>
<button type="button" class="btn primary">保存</button>
</div>
</div>
</div>
<!-- 方案 A -->
<div class="phone-wrap" data-view="compare a">
<div class="phone-label">
<h3>方案 A · 软填充</h3>
<span class="tag tag-new">推荐</span>
</div>
<div class="phone" id="phone-a">
<div class="new-page">
<header class="new-nav">
<div class="new-nav__bar">
<div class="new-nav__back">‹</div>
<div class="new-nav__title">新增协议单</div>
<span class="new-nav__mode">专员</span>
</div>
<div class="new-steps">
<span class="step-chip is-active"><span class="n">1</span>协议</span>
<span class="step-chip"><span class="n">2</span>采集</span>
<span class="step-chip"><span class="n">3</span>客户</span>
<span class="step-chip"><span class="n">4</span>样本</span>
<span class="step-chip"><span class="n">5</span>缴费</span>
<span class="step-chip"><span class="n">6</span>附件</span>
<span class="step-chip"><span class="n">7</span>确认</span>
</div>
</header>
<div class="new-body">
<!-- 1 协议 -->
<section class="card" id="sec-protocol">
<div class="card__head">
<span class="card__badge">1</span>
<h4 class="card__title">协议信息</h4>
</div>
<div class="card__body">
<div class="field">
<div class="field__label">业务来源<span class="req">*</span></div>
<div class="seg">
<button type="button" class="on">细胞存储</button>
<button type="button">科研捐赠</button>
</div>
</div>
<div class="field-grid">
<div class="field">
<div class="field__label">协议编号</div>
<div class="field__ctl">选填 / 可自动</div>
</div>
<div class="field">
<div class="field__label">登记日期</div>
<div class="field__ctl has-val">2026-10-15 <span class="chev">›</span></div>
</div>
</div>
<div class="field">
<div class="field__label">采集专员</div>
<div class="field__ctl">请选择 <span class="chev">›</span></div>
</div>
<div class="field">
<div class="field__label">协议总金额<span class="tip">元</span></div>
<div class="field__ctl has-val is-focus">128000.00</div>
</div>
<div class="field">
<div class="field__label">备注</div>
<div class="field__ctl textarea">客户要求加急入库说明…</div>
</div>
</div>
</section>
<!-- 2 采集 -->
<section class="card">
<div class="card__head">
<span class="card__badge">2</span>
<h4 class="card__title">采集信息</h4>
</div>
<div class="card__body">
<div class="field">
<div class="field__label">市场专员</div>
<div class="field__ctl has-val">张专员 <span class="chev">›</span></div>
</div>
<div class="field-grid">
<div class="field">
<div class="field__label">采集地区</div>
<div class="field__ctl has-val">合肥 <span class="chev">›</span></div>
</div>
<div class="field">
<div class="field__label">采集医院</div>
<div class="field__ctl">请选择 <span class="chev">›</span></div>
</div>
</div>
<div class="field">
<div class="field__label">地区经理</div>
<div class="field__ctl has-val">李经理 <span class="chev">›</span></div>
</div>
<div class="field-grid">
<div class="field">
<div class="field__label">样本采集时间</div>
<div class="field__ctl">请选择 <span class="chev">›</span></div>
</div>
<div class="field">
<div class="field__label">母血采集时间</div>
<div class="field__ctl">请选择 <span class="chev">›</span></div>
</div>
</div>
</div>
</section>
<!-- 3 客户 -->
<section class="card">
<div class="card__head">
<span class="card__badge">3</span>
<h4 class="card__title">客户信息</h4>
</div>
<p class="card__hint">可搜索客户档案回填;身份证可自动推算出生年月</p>
<div class="card__body">
<div class="subgroup">
<div class="subgroup__title">母亲</div>
<div class="field">
<div class="field__label">母亲姓名<span class="req">*</span></div>
<div class="field__ctl has-val">王女士 🔍</div>
</div>
<div class="field-grid">
<div class="field">
<div class="field__label">联系电话</div>
<div class="field__ctl has-val">138****8899</div>
</div>
<div class="field">
<div class="field__label">出生年月</div>
<div class="field__ctl has-val">1992-06</div>
</div>
</div>
<div class="field">
<div class="field__label">母亲身份证号</div>
<div class="field__ctl has-val">3401**********1234</div>
</div>
<div class="field">
<div class="field__label">家庭住址</div>
<div class="field__ctl textarea" style="min-height:64px">合肥市蜀山区××路××号</div>
</div>
</div>
<div class="subgroup">
<div class="subgroup__title">父亲</div>
<div class="field-grid">
<div class="field">
<div class="field__label">父亲姓名</div>
<div class="field__ctl">搜索档案 🔍</div>
</div>
<div class="field">
<div class="field__label">父亲电话</div>
<div class="field__ctl">请输入</div>
</div>
</div>
</div>
<div class="subgroup">
<div class="subgroup__title">新生儿</div>
<div class="field-grid">
<div class="field">
<div class="field__label">新生儿姓名</div>
<div class="field__ctl">请输入</div>
</div>
<div class="field">
<div class="field__label">性别</div>
<div class="seg">
<button type="button" class="on">男</button>
<button type="button">女</button>
</div>
</div>
</div>
<div class="field-grid">
<div class="field">
<div class="field__label">预产期</div>
<div class="field__ctl">请选择 <span class="chev">›</span></div>
</div>
<div class="field">
<div class="field__label">分娩方式</div>
<div class="field__ctl">请选择 <span class="chev">›</span></div>
</div>
</div>
<div class="field-grid">
<div class="field">
<div class="field__label">胎次</div>
<div class="field__ctl">请输入</div>
</div>
<div class="field">
<div class="field__label">体重(克)</div>
<div class="field__ctl">请输入</div>
</div>
</div>
<div class="field-grid">
<div class="field">
<div class="field__label">评分 1分钟</div>
<div class="field__ctl">请输入</div>
</div>
<div class="field">
<div class="field__label">评分 5分钟</div>
<div class="field__ctl">请输入</div>
</div>
</div>
</div>
</div>
</section>
<!-- 4 样本 -->
<section class="card">
<div class="card__head">
<span class="card__badge">4</span>
<h4 class="card__title">样本及运输</h4>
</div>
<p class="card__hint">规则与管理端一致</p>
<div class="card__body">
<div class="toolbar-row">
<button type="button" class="mini-btn primary">+ 添加样本</button>
<button type="button" class="mini-btn">配置运费</button>
<button type="button" class="mini-btn warn">取消运费</button>
</div>
<article class="sample-card">
<div class="sample-card__head">
<strong>☐ YB20261015001</strong>
<span class="tag-status">已采集</span>
</div>
<div class="sample-card__meta">
<span>脐带血</span>
<span>优惠价 ¥68,000</span>
<span>顺丰 · ¥120</span>
<span>ETA 10-18</span>
</div>
<div class="sample-card__actions">
<button type="button" class="mini-btn">编辑</button>
<button type="button" class="mini-btn danger">删除</button>
</div>
</article>
</div>
</section>
<!-- 5 缴费 -->
<section class="card">
<div class="card__head">
<span class="card__badge">5</span>
<h4 class="card__title">缴费信息</h4>
</div>
<div class="card__body">
<div class="money-sync">
<span>样本合同合计</span>
<strong>¥68,000.00</strong>
</div>
<article class="sample-card">
<div class="sample-card__head">
<strong>YB20261015001 · 脐带血</strong>
<span class="tag-status">分期</span>
</div>
<div class="field">
<div class="field__label">样本合同金额</div>
<div class="field__ctl has-val">68000</div>
</div>
<div class="field-grid">
<div class="field">
<div class="field__label">期数</div>
<div class="field__ctl has-val">3</div>
</div>
<div class="field">
<div class="field__label">间隔(月)</div>
<div class="field__ctl has-val">3</div>
</div>
</div>
</article>
</div>
</section>
<!-- 6 附件 -->
<section class="card">
<div class="card__head">
<span class="card__badge">6</span>
<h4 class="card__title">附件</h4>
</div>
<div class="card__body">
<div class="field">
<div class="field__label">协议附件</div>
<div class="upload-row">
<div class="upload-card"><span class="plus">+</span>上传</div>
<div class="upload-file">
<span class="name">协议扫描.pdf</span>
<span class="rm">删除</span>
</div>
</div>
</div>
<div class="field">
<div class="field__label">知情同意书</div>
<div class="upload-row">
<div class="upload-card"><span class="plus">+</span>上传</div>
</div>
</div>
<div class="field">
<div class="field__label">健康调查表</div>
<div class="upload-row">
<div class="upload-card"><span class="plus">+</span>上传</div>
</div>
</div>
</div>
</section>
<!-- 7 提交确认(始终最后) -->
<section class="card">
<div class="card__head">
<span class="card__badge">7</span>
<h4 class="card__title">提交确认</h4>
</div>
<p class="card__hint">请核对协议、样本、缴费与附件后再勾选</p>
<div class="card__body">
<div class="confirm-row">
<span class="box">✓</span>
<span>我已核对以上信息,确认无误后保存</span>
</div>
</div>
</section>
</div>
</div>
<div class="new-bottom">
<button type="button" class="btn ghost">取消</button>
<button type="button" class="btn primary">保存协议单</button>
</div>
</div>
</div>
<!-- 方案 B(单独 tab 时显示) -->
<div class="phone-wrap" data-view="b" hidden>
<div class="phone-label">
<h3>方案 B · 白底描边</h3>
<span class="tag tag-new">备选</span>
</div>
<div class="phone variant-b">
<div class="new-page">
<header class="new-nav">
<div class="new-nav__bar">
<div class="new-nav__back">‹</div>
<div class="new-nav__title">编辑协议单</div>
<span class="new-nav__mode">经理</span>
</div>
<div class="new-steps">
<span class="step-chip is-done"><span class="n">✓</span>协议</span>
<span class="step-chip is-active"><span class="n">2</span>采集</span>
<span class="step-chip"><span class="n">3</span>客户</span>
<span class="step-chip"><span class="n">4</span>样本</span>
</div>
</header>
<div class="new-body">
<section class="card">
<div class="card__head">
<span class="card__badge">2</span>
<h4 class="card__title">采集信息</h4>
</div>
<div class="card__body">
<div class="field">
<div class="field__label">市场专员</div>
<div class="field__ctl has-val">张专员 <span class="chev">›</span></div>
</div>
<div class="field-grid">
<div class="field">
<div class="field__label">采集地区</div>
<div class="field__ctl has-val">合肥 <span class="chev">›</span></div>
</div>
<div class="field">
<div class="field__label">采集医院</div>
<div class="field__ctl has-val">安徽省立医院 <span class="chev">›</span></div>
</div>
</div>
<div class="field">
<div class="field__label">地区经理</div>
<div class="field__ctl has-val">李经理 <span class="chev">›</span></div>
</div>
<div class="seg">
<button type="button" class="on">细胞存储</button>
<button type="button">科研捐赠</button>
</div>
</div>
</section>
<section class="card">
<div class="card__head">
<span class="card__badge">3</span>
<h4 class="card__title">客户信息 · 母亲</h4>
</div>
<div class="card__body">
<div class="field">
<div class="field__label">母亲姓名<span class="req">*</span></div>
<div class="field__ctl has-val">王女士</div>
</div>
<div class="field-grid">
<div class="field">
<div class="field__label">联系电话</div>
<div class="field__ctl has-val">138****8899</div>
</div>
<div class="field">
<div class="field__label">民族</div>
<div class="field__ctl has-val">汉族 <span class="chev">›</span></div>
</div>
</div>
</div>
</section>
</div>
</div>
<div class="new-bottom">
<button type="button" class="btn ghost">取消</button>
<button type="button" class="btn primary">保存修改</button>
</div>
</div>
</div>
</div>
</div>
<div class="note-bar">
文件路径:<code>mobile-ui/zkxw-mobile-oa-protocol-form-beautify-demo.html</code>
· 浏览器直接打开即可预览。确认方案后,可再落到
<code>mobile-oa-h5/src/styles/market.css</code> 与协议相关 Panel 组件。
业务字段不改,仅样式与信息层级。
<br />样本运输 / 缴费明细专项深化:
<code>mobile-ui/zkxw-mobile-oa-protocol-sample-pay-beautify-demo.html</code>
</div>
</div>
<script>
const tabs = document.querySelectorAll('.tab')
const wraps = document.querySelectorAll('.phone-wrap')
const panel = document.getElementById('side-panel')
const copy = {
compare: {
title: '对照说明',
sub: '左侧现状、右侧方案 A;结构字段不变,只改视觉层级',
html: `<ol>
<li><strong>左侧</strong>:当前 market-form-section 扁平卡片</li>
<li><strong>右侧</strong>:推荐软填充 + 进度芯片 + 客户分组</li>
<li><strong>不改</strong>:校验规则、接口字段、专员/经理权限</li>
<li><strong>可改</strong>:CSS Token、卡片头、双列栅格、底栏</li>
</ol>`
},
a: {
title: '方案 A · 软填充(推荐)',
sub: '与现有 Vant 组件可渐进替换样式',
html: `<ol>
<li><strong>顶区进度芯片</strong>:长表单可横向跳读分区</li>
<li><strong>编号卡片头</strong>:分区层级一眼可辨</li>
<li><strong>软填充控件</strong>:#f4faf7 底 + 聚焦描边</li>
<li><strong>客户三分组</strong>:母亲 / 父亲 / 新生儿</li>
<li><strong>短字段双列</strong>:降滚动长度</li>
<li><strong>实心小按钮</strong>:表单内操作统一实心偏小</li>
</ol>`
},
b: {
title: '方案 B · 白底描边',
sub: '卡片头浅绿条 + 白底输入,更接近「表单纸」质感',
html: `<ol>
<li><strong>白底描边控件</strong>:更传统、对比更强</li>
<li><strong>卡片头色带</strong>:分区识别靠顶条而非填充输入</li>
<li><strong>适合</strong>:更偏好「清晰边框」的团队</li>
<li><strong>相对 A</strong>:长列表时框线略密,建议少用双线叠加</li>
</ol>`
}
}
function show(tab) {
tabs.forEach((t) => t.classList.toggle('is-active', t.dataset.tab === tab))
wraps.forEach((w) => {
const views = (w.dataset.view || '').split(/\s+/)
w.hidden = !views.includes(tab)
})
const c = copy[tab] || copy.a
panel.querySelector('h2').textContent = c.title
panel.querySelector('.sub').textContent = c.sub
const ol = panel.querySelector('ol')
ol.outerHTML = c.html
}
tabs.forEach((t) => t.addEventListener('click', () => show(t.dataset.tab)))
</script>
</body>
</html>