| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803 |
- <!doctype html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Fresh Mint · 运输计划合同完善 Demo</title>
- <link rel="preconnect" href="https://fonts.googleapis.com" />
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
- <style>
- :root {
- --primary: #10b981;
- --primary-dark: #059669;
- --primary-bg: #ecfdf5;
- --text: #1e293b;
- --text-muted: #64748b;
- --border: #d9e8e2;
- --surface: #ffffff;
- --table-head: #f8fafc;
- --bg-page: #f8fafc;
- --row-alt: #fafafa;
- --row-hover: #f1f5f9;
- --shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
- --shadow-lg: 0 12px 40px rgba(15, 23, 42, 0.12);
- --radius: 10px;
- --warn: #d97706;
- --warn-bg: #fffbeb;
- --danger: #dc2626;
- --danger-bg: #fef2f2;
- }
- * { box-sizing: border-box; }
- body {
- margin: 0;
- min-height: 100vh;
- font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;
- font-size: 14px;
- line-height: 1.5;
- color: var(--text);
- background: var(--bg-page);
- }
- .demo-page { max-width: 1280px; margin: 0 auto; padding: 28px 24px 100px; }
- .demo-head { margin-bottom: 24px; }
- .demo-head h1 { margin: 0 0 8px; font-size: 24px; font-weight: 700; letter-spacing: -0.02em; }
- .demo-head h1 span { color: var(--primary); }
- .demo-head p { margin: 0 0 8px; color: var(--text-muted); max-width: 880px; }
- .meta { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
- .pill {
- display: inline-flex; align-items: center; gap: 6px;
- padding: 4px 10px; border-radius: 999px; font-size: 12px; font-weight: 500;
- background: var(--primary-bg); color: var(--primary-dark); border: 1px solid #a7f3d0;
- }
- .pill.muted { background: #f1f5f9; color: var(--text-muted); border-color: #e2e8f0; }
- .pill.warn { background: var(--warn-bg); color: var(--warn); border-color: rgba(217,119,6,.25); }
- .brief {
- display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 22px;
- }
- @media (max-width: 900px) { .brief { grid-template-columns: 1fr; } }
- .card {
- background: var(--surface); border: 1px solid var(--border);
- border-radius: var(--radius); padding: 16px 18px; box-shadow: var(--shadow);
- }
- .card h2 {
- margin: 0 0 10px; font-size: 15px; font-weight: 600;
- padding-left: 10px; border-left: 3px solid var(--primary);
- color: var(--primary-dark); line-height: 1.2;
- }
- .card ul { margin: 0; padding-left: 18px; color: var(--text-muted); }
- .card li { margin: 5px 0; }
- .card li strong { color: var(--text); }
- .scheme-tabs {
- display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap;
- }
- .scheme-tab {
- appearance: none; border: 1px solid var(--border); background: #fff;
- padding: 8px 14px; border-radius: 8px; font-size: 13px; font-weight: 500;
- color: var(--text-muted); cursor: pointer;
- }
- .scheme-tab.active {
- color: var(--primary-dark); border-color: #6ee7b7; background: var(--primary-bg);
- }
- .scheme-pane { display: none; }
- .scheme-pane.active { display: block; }
- .approach-note {
- margin: 0 0 14px; padding: 10px 14px; border-radius: 8px;
- background: #f8fafc; border: 1px dashed #cbd5e1; color: var(--text-muted); font-size: 13px;
- }
- .approach-note strong { color: var(--text); }
- .approach-note.rec {
- background: var(--primary-bg); border-color: #6ee7b7; border-style: solid;
- color: var(--primary-dark);
- }
- /* ── 列表壳(对齐 web-admin panel) ── */
- .panel {
- background: var(--surface); border: 1px solid var(--border);
- border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
- }
- .panel-top {
- display: flex; align-items: center; justify-content: space-between;
- gap: 12px; padding: 16px 20px 12px; border-bottom: 1px solid #eef2f6;
- }
- .panel-top h2 {
- margin: 0; font-size: 16px; font-weight: 600;
- display: flex; align-items: center; gap: 0;
- }
- .panel-top h2::before {
- content: ""; display: inline-block; width: 3px; height: 16px;
- background: var(--primary); border-radius: 2px; margin-right: 10px;
- }
- .stat-chip {
- display: inline-flex; align-items: center; gap: 6px;
- padding: 4px 10px; border-radius: 8px; font-size: 12px; font-weight: 600;
- background: var(--warn-bg); color: var(--warn); border: 1px solid rgba(217,119,6,.2);
- }
- .filter-block { padding: 14px 20px 6px; }
- .filter-grid {
- display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px 16px;
- }
- @media (max-width: 1100px) { .filter-grid { grid-template-columns: repeat(2, 1fr); } }
- .field label {
- display: block; font-size: 11px; color: var(--text-muted); margin-bottom: 4px;
- }
- .field input, .field select {
- width: 100%; height: 36px; border: 1px solid var(--border); border-radius: 8px;
- padding: 0 10px; font-size: 14px; color: var(--text); background: #fff;
- }
- .filter-actions {
- display: flex; justify-content: flex-end; gap: 8px; padding: 10px 0 4px;
- }
- .list-toolbar {
- display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
- padding: 10px 20px; border-top: 1px solid #eef2f6;
- }
- .list-toolbar .hint { color: var(--text-muted); font-size: 13px; margin-right: auto; }
- .btn {
- appearance: none; display: inline-flex; align-items: center; gap: 6px;
- height: 36px; padding: 0 14px; border-radius: 8px; font-size: 13px; font-weight: 500;
- cursor: pointer; border: 1px solid var(--border); background: #fff; color: var(--text);
- }
- .btn:hover { background: #f8fafc; }
- .btn-main {
- background: var(--primary); border-color: var(--primary); color: #fff;
- }
- .btn-main:hover { background: var(--primary-dark); }
- .btn-ghost { background: #fff; }
- .btn-link {
- height: auto; padding: 0; border: none; background: transparent;
- color: var(--primary-dark); font-weight: 500;
- }
- .btn-link:hover { text-decoration: underline; background: transparent; }
- .btn-sm { height: 30px; padding: 0 10px; font-size: 12px; }
- .btn.warn-outline {
- border-color: rgba(217,119,6,.35); color: var(--warn); background: var(--warn-bg);
- }
- .list-body { border-top: 1px solid #eef2f6; }
- table.list-table {
- width: 100%; border-collapse: collapse; table-layout: fixed;
- }
- .list-table th {
- background: var(--table-head); text-align: left; font-weight: 600; font-size: 12px;
- color: var(--text-muted); padding: 10px 12px; border-bottom: 1px solid #eef2f6;
- white-space: nowrap;
- }
- .list-table td {
- padding: 12px; border-bottom: 1px solid #f1f5f9; vertical-align: top;
- font-size: 13px;
- }
- .list-table tbody tr:nth-child(even) { background: var(--row-alt); }
- .list-table tbody tr:hover { background: var(--row-hover); }
- .list-table .mono { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--primary-dark); }
- .list-table .muted { color: var(--text-muted); }
- .list-table .party { display: flex; flex-direction: column; gap: 2px; }
- .list-table .party .name { font-weight: 500; }
- .list-table .party .sub { font-size: 12px; color: var(--text-muted); }
- .tag {
- display: inline-flex; align-items: center; padding: 2px 8px;
- border-radius: 6px; font-size: 12px; font-weight: 500; white-space: nowrap;
- }
- .tag.success { background: #059669; color: #fff; }
- .tag.warn { background: #d97706; color: #fff; }
- .tag.danger { background: #dc2626; color: #fff; }
- .tag.info { background: transparent; color: #1e293b; border: 1px solid #e2e8f0; }
- .tag.muted { background: #f1f5f9; color: var(--text-muted); }
- .gap-tags { display: flex; flex-wrap: wrap; gap: 6px; }
- .ops { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
- .pager {
- display: flex; align-items: center; justify-content: flex-end; gap: 12px;
- padding: 12px 20px; border-top: 1px solid #eef2f6; color: var(--text-muted); font-size: 13px;
- }
- /* 方案 B:行内展开补合同 */
- .expand-row td { background: #f8fffb !important; padding: 0 !important; }
- .expand-panel { padding: 14px 16px 16px; border-top: 1px dashed #a7f3d0; }
- .expand-grid {
- display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
- }
- @media (max-width: 900px) { .expand-grid { grid-template-columns: 1fr; } }
- .side-box {
- background: #fff; border: 1px solid var(--border); border-radius: 8px; padding: 12px;
- }
- .side-box.need { border-color: rgba(217,119,6,.35); background: #fffdf8; }
- .side-box.ok { border-color: #a7f3d0; background: #f6fffb; }
- .side-box.exempt { border-color: #e2e8f0; background: #f8fafc; }
- .side-box h4 {
- margin: 0 0 8px; font-size: 13px; display: flex; align-items: center;
- justify-content: space-between; gap: 8px;
- }
- .side-box .customer { font-size: 13px; margin-bottom: 10px; }
- .contract-chip {
- display: flex; align-items: center; justify-content: space-between; gap: 8px;
- padding: 8px 10px; border: 1px solid #e2e8f0; border-radius: 8px;
- background: #fff; margin-bottom: 8px; font-size: 12px;
- }
- .contract-chip .no { font-weight: 600; color: var(--primary-dark); }
- .empty-slot {
- padding: 14px; border: 1px dashed #cbd5e1; border-radius: 8px;
- text-align: center; color: var(--text-muted); font-size: 12px; margin-bottom: 8px;
- }
- .side-actions { display: flex; gap: 8px; flex-wrap: wrap; }
- /* 弹窗 */
- .modal-mask {
- display: none; position: fixed; inset: 0; background: rgba(15, 23, 42, 0.45);
- z-index: 1000; align-items: center; justify-content: center; padding: 24px;
- backdrop-filter: none;
- }
- .modal-mask.open { display: flex; }
- .dialog {
- width: min(920px, 100%); max-height: calc(100vh - 100px);
- background: #fff; border-radius: 12px; box-shadow: var(--shadow-lg);
- display: flex; flex-direction: column; overflow: hidden;
- }
- .dialog-head {
- display: flex; align-items: center; justify-content: space-between;
- padding: 14px 20px; border-bottom: 1px solid #eef2f6; flex-shrink: 0;
- }
- .dialog-head h3 { margin: 0; font-size: 16px; font-weight: 600; }
- .dialog-body { padding: 18px 20px; overflow: auto; flex: 1; }
- .dialog-foot {
- display: flex; justify-content: flex-end; gap: 8px;
- padding: 12px 20px; border-top: 1px solid #eef2f6; flex-shrink: 0;
- }
- .plan-summary {
- display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
- margin-bottom: 16px; padding: 12px; background: #f8fafc; border-radius: 8px;
- border: 1px solid #eef2f6;
- }
- @media (max-width: 800px) { .plan-summary { grid-template-columns: 1fr 1fr; } }
- .kv .k { font-size: 11px; color: var(--text-muted); }
- .kv .v { font-size: 13px; font-weight: 500; margin-top: 2px; }
- .party-sections { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
- @media (max-width: 800px) { .party-sections { grid-template-columns: 1fr; } }
- .selector-list { max-height: 220px; overflow: auto; border: 1px solid var(--border); border-radius: 8px; }
- .selector-item {
- display: flex; align-items: flex-start; gap: 10px;
- padding: 10px 12px; border-bottom: 1px solid #f1f5f9; cursor: pointer;
- }
- .selector-item:last-child { border-bottom: none; }
- .selector-item:hover { background: var(--primary-bg); }
- .selector-item.selected { background: var(--primary-bg); }
- .selector-item .meta-line { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
- .selector-item input { margin-top: 3px; }
- .legend {
- margin-top: 22px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;
- }
- @media (max-width: 900px) { .legend { grid-template-columns: 1fr; } }
- .legend .card h3 { margin: 0 0 8px; font-size: 14px; }
- .legend code {
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
- font-size: 12px; background: #f1f5f9; padding: 1px 6px; border-radius: 4px;
- }
- .toast {
- position: fixed; top: 20px; left: 50%; transform: translateX(-50%) translateY(-120%);
- background: #059669; color: #fff; padding: 10px 18px; border-radius: 8px;
- font-size: 13px; font-weight: 500; box-shadow: var(--shadow-lg);
- transition: transform .25s ease; z-index: 2000;
- }
- .toast.show { transform: translateX(-50%) translateY(0); }
- </style>
- </head>
- <body>
- <div class="demo-page">
- <header class="demo-head">
- <h1>运输计划 · <span>合同完善</span> 界面 Demo</h1>
- <p>
- 仅展示「缺合同」的运输计划。判定规则:供需双方若非内置公司
- (安庆市山峡化工有限公司 / 安徽云栋化工有限公司),该侧至少需关联 1 份合同;
- 内置公司该侧合同可免。本页只做<strong>关联已有合同</strong>,不支持新增/编辑合同。
- </p>
- <div class="meta">
- <span class="pill">Fresh Mint · web-admin</span>
- <span class="pill muted">参考模式:P02 复杂列表 + 关联弹窗</span>
- <span class="pill warn">可点击交互 Demo</span>
- <span class="pill muted">菜单建议:运输计划模块下</span>
- </div>
- </header>
- <section class="brief">
- <div class="card">
- <h2>业务目标</h2>
- <ul>
- <li>创建计划时合同改为非必填,事后用本页<strong>快速补齐</strong></li>
- <li>列表默认只显示缺口:缺供方 / 缺需方 / 两侧都缺</li>
- <li>行内操作「补充合同」→ 打开选择器关联 CRM 已有合同</li>
- <li>补齐后自动从本列表消失(或刷新后消失)</li>
- </ul>
- </div>
- <div class="card">
- <h2>本 Demo 两种交互</h2>
- <ul>
- <li><strong>方案 A(推荐)</strong>:列表 +「补充合同」弹窗,两侧分区关联</li>
- <li><strong>方案 B</strong>:列表行展开,直接在行内补两侧合同</li>
- <li>下方可切换方案,并点击按钮体验弹窗</li>
- </ul>
- </div>
- </section>
- <div class="scheme-tabs" role="tablist">
- <button type="button" class="scheme-tab active" data-scheme="a">方案 A · 列表 + 弹窗(推荐)</button>
- <button type="button" class="scheme-tab" data-scheme="b">方案 B · 行内展开</button>
- </div>
- <!-- ═══ 方案 A ═══ -->
- <div class="scheme-pane active" id="scheme-a">
- <p class="approach-note rec">
- <strong>推荐:</strong>与现有「危化品证上传」「运输计划」列表一致;弹窗内可专注选合同,
- 不打断表格滚动;便于后续加菜单角标(待完善数量)。
- </p>
- <section class="panel">
- <div class="panel-top">
- <h2>运输计划合同完善</h2>
- <span class="stat-chip">待完善 3 条</span>
- </div>
- <div class="filter-block">
- <div class="filter-grid">
- <div class="field">
- <label>计划编号</label>
- <input placeholder="搜索计划编号" />
- </div>
- <div class="field">
- <label>缺口类型</label>
- <select>
- <option>全部缺口</option>
- <option>仅缺供方合同</option>
- <option>仅缺需方合同</option>
- <option>两侧均缺</option>
- </select>
- </div>
- <div class="field">
- <label>供方 / 需方</label>
- <input placeholder="客户名称" />
- </div>
- <div class="field">
- <label>计划日期</label>
- <input type="text" placeholder="开始日期 - 结束日期" />
- </div>
- </div>
- <div class="filter-actions">
- <button type="button" class="btn btn-ghost">重置</button>
- <button type="button" class="btn btn-main">搜索</button>
- </div>
- </div>
- <div class="list-toolbar">
- <span class="hint">仅展示仍缺合同的运输计划 · 内置公司侧已自动豁免</span>
- <button type="button" class="btn btn-ghost">刷新</button>
- </div>
- <div class="list-body">
- <table class="list-table">
- <colgroup>
- <col style="width: 12%" />
- <col style="width: 9%" />
- <col style="width: 18%" />
- <col style="width: 18%" />
- <col style="width: 14%" />
- <col style="width: 12%" />
- <col style="width: 17%" />
- </colgroup>
- <thead>
- <tr>
- <th>计划编号</th>
- <th>计划日期</th>
- <th>供方</th>
- <th>需方</th>
- <th>合同缺口</th>
- <th>跟进状态</th>
- <th>操作</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td><a class="mono btn-link" href="javascript:void(0)">YP20260820001</a></td>
- <td>2026-08-20</td>
- <td>
- <div class="party">
- <span class="name">合肥某某商贸有限公司</span>
- <span class="sub">供方合同:未关联</span>
- </div>
- </td>
- <td>
- <div class="party">
- <span class="name">安庆市山峡化工有限公司</span>
- <span class="sub">内置 · 合同可免</span>
- </div>
- </td>
- <td>
- <div class="gap-tags">
- <span class="tag warn">缺供方合同</span>
- </div>
- </td>
- <td><span class="tag success">已安排</span></td>
- <td>
- <div class="ops">
- <button type="button" class="btn btn-sm warn-outline" data-open-dialog="row1">补充合同</button>
- <button type="button" class="btn-link btn-sm">查看计划</button>
- </div>
- </td>
- </tr>
- <tr>
- <td><a class="mono btn-link" href="javascript:void(0)">YP20260819088</a></td>
- <td>2026-08-19</td>
- <td>
- <div class="party">
- <span class="name">安庆市山峡化工有限公司</span>
- <span class="sub">内置 · 合同可免</span>
- </div>
- </td>
- <td>
- <div class="party">
- <span class="name">芜湖某某材料有限公司</span>
- <span class="sub">需方合同:未关联</span>
- </div>
- </td>
- <td>
- <div class="gap-tags">
- <span class="tag warn">缺需方合同</span>
- </div>
- </td>
- <td><span class="tag info">待安排</span></td>
- <td>
- <div class="ops">
- <button type="button" class="btn btn-sm warn-outline" data-open-dialog="row2">补充合同</button>
- <button type="button" class="btn-link btn-sm">查看计划</button>
- </div>
- </td>
- </tr>
- <tr>
- <td><a class="mono btn-link" href="javascript:void(0)">YP20260818042</a></td>
- <td>2026-08-18</td>
- <td>
- <div class="party">
- <span class="name">马鞍山某某化工有限公司</span>
- <span class="sub">供方合同:未关联</span>
- </div>
- </td>
- <td>
- <div class="party">
- <span class="name">铜陵某某实业有限公司</span>
- <span class="sub">需方合同:未关联</span>
- </div>
- </td>
- <td>
- <div class="gap-tags">
- <span class="tag danger">两侧均缺</span>
- </div>
- </td>
- <td><span class="tag success">运输中</span></td>
- <td>
- <div class="ops">
- <button type="button" class="btn btn-sm warn-outline" data-open-dialog="row3">补充合同</button>
- <button type="button" class="btn-link btn-sm">查看计划</button>
- </div>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- <div class="pager">共 3 条 · 每页 20 条 · 1 / 1</div>
- </section>
- </div>
- <!-- ═══ 方案 B ═══ -->
- <div class="scheme-pane" id="scheme-b">
- <p class="approach-note">
- <strong>备选:</strong>适合一次补几条、不想频繁开弹窗的场景;表格变高,窄屏体验略差。
- </p>
- <section class="panel">
- <div class="panel-top">
- <h2>运输计划合同完善</h2>
- <span class="stat-chip">待完善 2 条(示例)</span>
- </div>
- <div class="list-toolbar">
- <span class="hint">点击「展开补充」在行内关联合同</span>
- </div>
- <div class="list-body">
- <table class="list-table">
- <thead>
- <tr>
- <th style="width:12%">计划编号</th>
- <th style="width:10%">计划日期</th>
- <th style="width:22%">供方</th>
- <th style="width:22%">需方</th>
- <th style="width:14%">合同缺口</th>
- <th style="width:20%">操作</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="mono">YP20260820001</td>
- <td>2026-08-20</td>
- <td>合肥某某商贸有限公司</td>
- <td>安庆市山峡化工有限公司</td>
- <td><span class="tag warn">缺供方合同</span></td>
- <td><button type="button" class="btn btn-sm warn-outline" id="toggle-expand">收起补充</button></td>
- </tr>
- <tr class="expand-row" id="expand-demo">
- <td colspan="6">
- <div class="expand-panel">
- <div class="expand-grid">
- <div class="side-box need">
- <h4>
- 供方合同
- <span class="tag warn">待补充</span>
- </h4>
- <div class="customer">合肥某某商贸有限公司</div>
- <div class="empty-slot">暂未关联合同</div>
- <div class="side-actions">
- <button type="button" class="btn btn-sm btn-main" data-open-dialog="row1">选择合同</button>
- </div>
- </div>
- <div class="side-box exempt">
- <h4>
- 需方合同
- <span class="tag muted">可免</span>
- </h4>
- <div class="customer">安庆市山峡化工有限公司(内置)</div>
- <div class="empty-slot" style="border-style:solid;background:#f8fafc;">
- 内置公司,无需关联合同
- </div>
- </div>
- </div>
- </div>
- </td>
- </tr>
- <tr>
- <td class="mono">YP20260818042</td>
- <td>2026-08-18</td>
- <td>马鞍山某某化工有限公司</td>
- <td>铜陵某某实业有限公司</td>
- <td><span class="tag danger">两侧均缺</span></td>
- <td><button type="button" class="btn btn-sm warn-outline" data-open-dialog="row3">展开补充</button></td>
- </tr>
- </tbody>
- </table>
- </div>
- </section>
- </div>
- <section class="legend">
- <div class="card">
- <h3>缺口判定(与内置名单一致)</h3>
- <ul>
- <li>名称 ∈ {山峡, 云栋} → 该侧<strong>不要求</strong>合同</li>
- <li>否则 → 该侧 <code>contracts.length ≥ 1</code></li>
- <li>任一侧不满足 → 进入本列表</li>
- </ul>
- </div>
- <div class="card">
- <h3>本页能力边界</h3>
- <ul>
- <li>✅ 筛选、列表、关联已有合同、保存</li>
- <li>✅ 跳转查看运输计划(只读)</li>
- <li>❌ 不在此页新增 / 编辑 / 审批合同</li>
- </ul>
- </div>
- <div class="card">
- <h3>待你确认(下一步提问)</h3>
- <ul>
- <li>是否含已作废计划?归档计划?</li>
- <li>菜单角标是否显示待完善数量?</li>
- <li>关联时是否允许多份合同?</li>
- </ul>
- </div>
- </section>
- </div>
- <!-- 补充合同弹窗 -->
- <div class="modal-mask" id="dialog-mask" role="dialog" aria-modal="true">
- <div class="dialog">
- <div class="dialog-head">
- <h3>补充合同 · <span id="dlg-plan-no">YP20260820001</span></h3>
- <button type="button" class="btn-link" id="dlg-close" aria-label="关闭">✕</button>
- </div>
- <div class="dialog-body">
- <div class="plan-summary">
- <div class="kv"><div class="k">计划日期</div><div class="v" id="dlg-date">2026-08-20</div></div>
- <div class="kv"><div class="k">跟进状态</div><div class="v"><span class="tag success">已安排</span></div></div>
- <div class="kv"><div class="k">供方</div><div class="v" id="dlg-supplier">合肥某某商贸有限公司</div></div>
- <div class="kv"><div class="k">需方</div><div class="v" id="dlg-demander">安庆市山峡化工有限公司</div></div>
- </div>
- <div class="party-sections">
- <div class="side-box need" id="dlg-supplier-box">
- <h4>供方合同 <span class="tag warn" id="dlg-supplier-tag">待补充</span></h4>
- <div class="customer" id="dlg-supplier-name">合肥某某商贸有限公司</div>
- <div id="dlg-supplier-selected" class="empty-slot">暂未选择 · 下方勾选后确认</div>
- <div class="selector-list" id="dlg-supplier-list">
- <label class="selector-item">
- <input type="checkbox" name="sc" value="c1" />
- <div>
- <div class="no" style="font-weight:600;color:var(--primary-dark)">HT-2026-0312</div>
- <div class="meta-line">销售合同 · 执行中 · 液碱 32%</div>
- </div>
- </label>
- <label class="selector-item">
- <input type="checkbox" name="sc" value="c2" />
- <div>
- <div class="no" style="font-weight:600;color:var(--primary-dark)">HT-2026-0288</div>
- <div class="meta-line">框架合同 · 已审批 · 盐酸</div>
- </div>
- </label>
- </div>
- </div>
- <div class="side-box exempt" id="dlg-demander-box">
- <h4>需方合同 <span class="tag muted" id="dlg-demander-tag">可免</span></h4>
- <div class="customer" id="dlg-demander-name">安庆市山峡化工有限公司(内置)</div>
- <div class="empty-slot" style="border-style:solid;">内置公司,无需选择合同</div>
- <div class="selector-list" id="dlg-demander-list" style="display:none;"></div>
- </div>
- </div>
- </div>
- <div class="dialog-foot">
- <button type="button" class="btn btn-ghost" id="dlg-cancel">取消</button>
- <button type="button" class="btn btn-main" id="dlg-save">保存关联</button>
- </div>
- </div>
- </div>
- <div class="toast" id="toast">已保存合同关联(Demo)</div>
- <script>
- const scenarios = {
- row1: {
- planNo: 'YP20260820001',
- date: '2026-08-20',
- supplier: '合肥某某商贸有限公司',
- demander: '安庆市山峡化工有限公司',
- supplierNeed: true,
- demanderNeed: false,
- supplierExempt: false,
- demanderExempt: true
- },
- row2: {
- planNo: 'YP20260819088',
- date: '2026-08-19',
- supplier: '安庆市山峡化工有限公司',
- demander: '芜湖某某材料有限公司',
- supplierNeed: false,
- demanderNeed: true,
- supplierExempt: true,
- demanderExempt: false
- },
- row3: {
- planNo: 'YP20260818042',
- date: '2026-08-18',
- supplier: '马鞍山某某化工有限公司',
- demander: '铜陵某某实业有限公司',
- supplierNeed: true,
- demanderNeed: true,
- supplierExempt: false,
- demanderExempt: false
- }
- }
- const mask = document.getElementById('dialog-mask')
- function paintSide(prefix, need, exempt, name) {
- const box = document.getElementById('dlg-' + prefix + '-box')
- const tag = document.getElementById('dlg-' + prefix + '-tag')
- const list = document.getElementById('dlg-' + prefix + '-list')
- const selected = document.getElementById('dlg-' + prefix + '-selected')
- document.getElementById('dlg-' + prefix + '-name').textContent =
- exempt ? name + '(内置)' : name
- box.classList.remove('need', 'ok', 'exempt')
- if (exempt) {
- box.classList.add('exempt')
- tag.className = 'tag muted'
- tag.textContent = '可免'
- if (selected) {
- selected.className = 'empty-slot'
- selected.style.borderStyle = 'solid'
- selected.textContent = '内置公司,无需选择合同'
- }
- if (list) list.style.display = 'none'
- } else if (need) {
- box.classList.add('need')
- tag.className = 'tag warn'
- tag.textContent = '待补充'
- if (selected) {
- selected.className = 'empty-slot'
- selected.style.borderStyle = ''
- selected.textContent = '暂未选择 · 下方勾选后确认'
- }
- if (list) {
- list.style.display = ''
- list.innerHTML = `
- <label class="selector-item">
- <input type="checkbox" name="${prefix}" value="a" />
- <div>
- <div style="font-weight:600;color:var(--primary-dark)">HT-2026-0312</div>
- <div class="meta-line">销售合同 · 执行中 · 示例产品 A</div>
- </div>
- </label>
- <label class="selector-item">
- <input type="checkbox" name="${prefix}" value="b" />
- <div>
- <div style="font-weight:600;color:var(--primary-dark)">HT-2026-0288</div>
- <div class="meta-line">框架合同 · 已审批 · 示例产品 B</div>
- </div>
- </label>`
- }
- } else {
- box.classList.add('ok')
- tag.className = 'tag success'
- tag.textContent = '已齐'
- if (list) list.style.display = 'none'
- }
- }
- function openDialog(key) {
- const s = scenarios[key] || scenarios.row1
- document.getElementById('dlg-plan-no').textContent = s.planNo
- document.getElementById('dlg-date').textContent = s.date
- document.getElementById('dlg-supplier').textContent = s.supplier
- document.getElementById('dlg-demander').textContent = s.demander
- paintSide('supplier', s.supplierNeed, s.supplierExempt, s.supplier)
- paintSide('demander', s.demanderNeed, s.demanderExempt, s.demander)
- mask.classList.add('open')
- }
- function closeDialog() {
- mask.classList.remove('open')
- }
- document.querySelectorAll('.scheme-tab').forEach(tab => {
- tab.addEventListener('click', () => {
- document.querySelectorAll('.scheme-tab').forEach(t => t.classList.remove('active'))
- document.querySelectorAll('.scheme-pane').forEach(p => p.classList.remove('active'))
- tab.classList.add('active')
- document.getElementById('scheme-' + tab.dataset.scheme).classList.add('active')
- })
- })
- document.querySelectorAll('[data-open-dialog]').forEach(btn => {
- btn.addEventListener('click', () => openDialog(btn.dataset.openDialog))
- })
- document.getElementById('dlg-close').addEventListener('click', closeDialog)
- document.getElementById('dlg-cancel').addEventListener('click', closeDialog)
- mask.addEventListener('click', e => { if (e.target === mask) closeDialog() })
- document.getElementById('dlg-save').addEventListener('click', () => {
- closeDialog()
- const toast = document.getElementById('toast')
- toast.classList.add('show')
- setTimeout(() => toast.classList.remove('show'), 1800)
- })
- const expandBtn = document.getElementById('toggle-expand')
- const expandRow = document.getElementById('expand-demo')
- if (expandBtn && expandRow) {
- expandBtn.addEventListener('click', () => {
- const hidden = expandRow.style.display === 'none'
- expandRow.style.display = hidden ? '' : 'none'
- expandBtn.textContent = hidden ? '收起补充' : '展开补充'
- })
- }
- </script>
- </body>
- </html>
|