Files
shanxia2.0/docs/fresh-mint-pl-plan-form-available-qty-demo.html
T
zlwuandCursor 81b1cb9b1d feat(pl): show available contract qty before plan qty in stop products
Add contract-product-arrange-qty API and form UI with green available qty,
red plan qty over-hint, and click-through dialog for usage and plan list.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 17:41:36 +08:00

433 lines
21 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.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;
--bg-page: #f1f5f9;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
--shadow-lg: 0 12px 40px rgba(15, 23, 42, 0.14);
--radius: 10px;
--qty-plan: #dc2626;
--qty-avail: #059669;
--load-bg: #ecfdf5;
--delivery-bg: #f0f9ff;
}
* { box-sizing: border-box; }
body {
margin: 0; min-height: 100vh;
font-family: Inter, -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif;
font-size: 14px; line-height: 1.5; color: var(--text); background: var(--bg-page);
}
.demo-page { max-width: 1100px; margin: 0 auto; padding: 28px 24px 80px; }
.demo-head h1 { margin: 0 0 8px; font-size: 22px; font-weight: 700; }
.demo-head h1 span { color: var(--primary); }
.demo-head p { margin: 0; color: var(--text-muted); max-width: 900px; }
.meta { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.pill {
display: inline-flex; 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.red { background: #fef2f2; color: var(--qty-plan); border-color: #fecaca; }
.pill.green { background: var(--primary-bg); color: var(--qty-avail); border-color: #a7f3d0; }
.brief { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 16px 0; }
@media (max-width: 800px) { .brief { grid-template-columns: 1fr; } }
.card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: 14px 16px; box-shadow: var(--shadow);
}
.card h2 {
margin: 0 0 8px; font-size: 14px; font-weight: 600;
padding-left: 10px; border-left: 3px solid var(--primary); color: var(--primary-dark);
}
.card ul { margin: 0; padding-left: 18px; color: var(--text-muted); font-size: 13px; }
.card li { margin: 4px 0; }
.card li strong { color: var(--text); }
.dialog-shell {
background: var(--surface); border: 1px solid var(--border);
border-radius: 12px; box-shadow: var(--shadow-lg); overflow: hidden;
}
.dialog-header {
display: flex; align-items: center; justify-content: space-between;
padding: 14px 20px; border-bottom: 1px solid var(--border);
}
.dialog-header h3 { margin: 0; font-size: 16px; font-weight: 600; }
.dialog-body { padding: 16px 20px 20px; max-height: 70vh; overflow: auto; }
.dialog-footer {
display: flex; justify-content: flex-end; gap: 8px;
padding: 12px 20px; border-top: 1px solid var(--border);
}
.btn {
appearance: none; display: inline-flex; align-items: center; gap: 6px;
height: 32px; padding: 0 12px; border-radius: 8px; font-size: 13px; font-weight: 500;
border: 1px solid var(--border); background: #fff; color: var(--text); cursor: pointer;
}
.btn.primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.section { border: 1px solid var(--border); border-radius: 10px; margin-bottom: 14px; overflow: hidden; }
.section-head {
display: flex; align-items: center; justify-content: space-between;
padding: 10px 14px; font-weight: 600; font-size: 13px;
}
.section.load .section-head { background: var(--load-bg); color: var(--primary-dark); }
.section.delivery .section-head { background: var(--delivery-bg); color: #0369a1; }
.section-body { padding: 12px 14px 14px; }
.kv { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 12px; }
.kv .label { font-size: 11px; color: var(--text-muted); }
.kv .value { font-size: 13px; font-weight: 500; }
.product-card {
border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; background: #fff; margin-bottom: 10px;
}
.row1, .row2 { display: grid; gap: 10px; align-items: end; }
.row1 { grid-template-columns: 1.4fr 1fr 1fr 1fr 0.7fr; margin-bottom: 10px; }
.row2 { grid-template-columns: 1.1fr 1.1fr 1fr 1fr 1fr; }
@media (max-width: 900px) { .row1, .row2 { grid-template-columns: 1fr 1fr; } }
.field .flabel { display: block; font-size: 11px; color: var(--text-muted); margin-bottom: 4px; }
.field .fval {
height: 36px; display: flex; align-items: center; padding: 0 10px;
border: 1px solid var(--border); border-radius: 8px; background: #f8fafc; font-size: 13px;
}
.field .fval.input {
background: #fff; justify-content: flex-end; font-variant-numeric: tabular-nums; font-weight: 600;
}
.field .fval.input.plan {
color: var(--qty-plan); border-color: #fecaca; background: #fff5f5;
}
.field .fval.input.plan.is-over { box-shadow: 0 0 0 1px var(--qty-plan); }
.over-tip { margin-top: 4px; font-size: 11px; color: var(--qty-plan); }
.avail-wrap { display: flex; align-items: center; gap: 6px; height: 36px; }
.avail-num { font-size: 15px; font-weight: 700; color: var(--qty-avail); font-variant-numeric: tabular-nums; }
.help-btn {
width: 20px; height: 20px; border-radius: 50%;
border: 1px solid #a7f3d0; background: var(--primary-bg);
color: var(--primary-dark); font-size: 12px; font-weight: 700;
cursor: pointer; display: inline-flex; align-items: center; justify-content: center; padding: 0;
}
.help-btn:hover { background: var(--primary); color: #fff; border-color: var(--primary); }
.mask {
position: fixed; inset: 0; background: rgba(15, 23, 42, 0.45);
display: none; align-items: center; justify-content: center;
padding: 24px; z-index: 50; backdrop-filter: none;
}
.mask.show { display: flex; }
.qty-dialog {
width: min(640px, 100%); background: #fff; border-radius: 12px;
border: 1px solid var(--border); box-shadow: var(--shadow-lg); overflow: hidden;
}
.qty-dialog-header {
display: flex; align-items: center; justify-content: space-between;
padding: 14px 18px; border-bottom: 1px solid var(--border);
}
.qty-dialog-header h4 { margin: 0; font-size: 15px; font-weight: 600; color: var(--primary-dark); }
.qty-dialog-body { padding: 16px 18px; max-height: 60vh; overflow: auto; }
.qty-dialog-footer {
display: flex; justify-content: flex-end; gap: 8px;
padding: 12px 18px; border-top: 1px solid var(--border);
}
.stats { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; margin-bottom: 12px; }
.stat { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px; }
.stat .k { font-size: 11px; color: var(--text-muted); }
.stat .v { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; }
.stat .v.green { color: var(--qty-avail); }
.sub { margin: 0 0 8px; font-size: 13px; font-weight: 600; }
.qty-dialog table { width: 100%; border-collapse: collapse; font-size: 13px; }
.qty-dialog th, .qty-dialog td { border: 1px solid var(--border); padding: 8px 10px; text-align: left; }
.qty-dialog th { background: #f8fafc; font-weight: 600; }
.qty-dialog td.num { text-align: right; font-variant-numeric: tabular-nums; }
.qty-dialog a { color: var(--primary-dark); text-decoration: none; font-weight: 500; }
.legend {
margin-top: 14px; padding: 12px 14px; border-radius: 10px;
background: #fff; border: 1px dashed #a7f3d0; font-size: 13px; color: var(--text-muted);
}
.legend strong { color: var(--primary-dark); }
</style>
</head>
<body>
<div class="demo-page">
<header class="demo-head">
<h1>运输计划表单 · <span>可安排数量</span></h1>
<p>
装货/送货产品区:可安排(绿)在计划数量(红)前;超可安排仅标红提示,仍可保存/安排(安排接口硬拦)。
问号<strong>点击弹窗</strong>展示合同数量、已安排、可安排,及已安排该产品的运输计划列表。
</p>
<div class="meta">
<span class="pill muted">/#/pl/plan-module/plan</span>
<span class="pill green">可安排 · 绿</span>
<span class="pill red">计划数量 · 红 · 超量仅提示</span>
<span class="pill">? · 点击弹窗</span>
</div>
</header>
<div class="brief">
<div class="card">
<h2>已确认</h2>
<ul>
<li>计划数量 &gt; 可安排 → <strong>仅标红</strong>,不拦保存/安排</li>
<li>安排时仍走合同数量硬拦接口</li>
<li>问号 → <strong>弹窗</strong>(非气泡悬停)</li>
</ul>
</div>
<div class="card">
<h2>口径</h2>
<ul>
<li>可安排 = 合同数量 − 已安排(编辑扣本计划旧量)</li>
<li>按本段合同分别计算</li>
<li>框架无固定量:可安排「—」,弹窗说明不限额</li>
</ul>
</div>
</div>
<div class="dialog-shell">
<div class="dialog-header">
<h3>编辑运输计划 · YP20260830021</h3>
<button type="button" class="btn" tabindex="-1">×</button>
</div>
<div class="dialog-body">
<section class="section load">
<div class="section-head">
<span>装货信息</span>
<span style="font-weight:500;font-size:12px;">合同 HT-CG-2026-102</span>
</div>
<div class="section-body">
<div class="kv">
<div><div class="label">装货客户</div><div class="value">云栋供应商</div></div>
<div><div class="label">装货地址</div><div class="value">安庆某某园区 A 库</div></div>
<div><div class="label">装货时间</div><div class="value">2026-08-31 09:00</div></div>
</div>
<article class="product-card">
<div class="row1">
<div class="field"><span class="flabel">产品名称</span><div class="fval">液碱 32%</div></div>
<div class="field"><span class="flabel">别名</span><div class="fval">—</div></div>
<div class="field"><span class="flabel">品牌</span><div class="fval">不限品牌</div></div>
<div class="field"><span class="flabel">规格型号</span><div class="fval">—</div></div>
<div class="field"><span class="flabel">单位</span><div class="fval">吨</div></div>
</div>
<div class="row2">
<div class="field">
<span class="flabel">可安排数量</span>
<div class="avail-wrap">
<span class="avail-num">50.000</span>
<button type="button" class="help-btn" data-open="dlg-load" title="查看合同用量">?</button>
</div>
</div>
<div class="field">
<span class="flabel">计划数量</span>
<div class="fval input plan">40.000</div>
</div>
<div class="field"><span class="flabel">包装规格</span><div class="fval">槽罐</div></div>
<div class="field"><span class="flabel">送货方式</span><div class="fval">送货上门</div></div>
<div class="field"><span class="flabel">备注</span><div class="fval">—</div></div>
</div>
</article>
<article class="product-card">
<div class="row1">
<div class="field"><span class="flabel">产品名称</span><div class="fval">98% 硫酸(超量示意)</div></div>
<div class="field"><span class="flabel">别名</span><div class="fval">—</div></div>
<div class="field"><span class="flabel">品牌</span><div class="fval">—</div></div>
<div class="field"><span class="flabel">规格型号</span><div class="fval">工业级</div></div>
<div class="field"><span class="flabel">单位</span><div class="fval">吨</div></div>
</div>
<div class="row2">
<div class="field">
<span class="flabel">可安排数量</span>
<div class="avail-wrap">
<span class="avail-num">20.000</span>
<button type="button" class="help-btn" data-open="dlg-over" title="查看合同用量">?</button>
</div>
</div>
<div class="field">
<span class="flabel">计划数量</span>
<div class="fval input plan is-over">30.000</div>
<div class="over-tip">已超过可安排数量,安排时将被拦截</div>
</div>
<div class="field"><span class="flabel">包装规格</span><div class="fval">槽罐</div></div>
<div class="field"><span class="flabel">送货方式</span><div class="fval">自提</div></div>
<div class="field"><span class="flabel">备注</span><div class="fval">标红仍可暂存</div></div>
</div>
</article>
</div>
</section>
<section class="section delivery">
<div class="section-head">
<span>送货信息 #1</span>
<span style="font-weight:500;font-size:12px;">合同 HT-XS-2026-088</span>
</div>
<div class="section-body">
<div class="kv">
<div><div class="label">送货客户</div><div class="value">某某贸易公司</div></div>
<div><div class="label">送货地址</div><div class="value">合肥某某仓储</div></div>
<div><div class="label">送货时间</div><div class="value">2026-08-31 16:00</div></div>
</div>
<article class="product-card">
<div class="row1">
<div class="field"><span class="flabel">产品名称</span><div class="fval">液碱 32%</div></div>
<div class="field"><span class="flabel">别名</span><div class="fval">—</div></div>
<div class="field"><span class="flabel">品牌</span><div class="fval">不限品牌</div></div>
<div class="field"><span class="flabel">规格型号</span><div class="fval">—</div></div>
<div class="field"><span class="flabel">单位</span><div class="fval">吨</div></div>
</div>
<div class="row2">
<div class="field">
<span class="flabel">可安排数量</span>
<div class="avail-wrap">
<span class="avail-num">140.000</span>
<button type="button" class="help-btn" data-open="dlg-del" title="查看合同用量">?</button>
</div>
</div>
<div class="field">
<span class="flabel">计划数量</span>
<div class="fval input plan">40.000</div>
</div>
<div class="field"><span class="flabel">包装规格</span><div class="fval">槽罐</div></div>
<div class="field"><span class="flabel">送货方式</span><div class="fval">送货上门</div></div>
<div class="field"><span class="flabel">备注</span><div class="fval">—</div></div>
</div>
</article>
</div>
</section>
</div>
<div class="dialog-footer">
<button type="button" class="btn">✕ 取消</button>
<button type="button" class="btn primary">✓ 保存并安排</button>
</div>
</div>
<p class="legend">
<strong>交互:</strong>点击「?」打开二级弹窗(无背景模糊)。超量行仅红色强调 + 小字提示,不禁止保存。
</p>
</div>
<!-- 用量明细弹窗:装货正常 -->
<div class="mask" id="dlg-load" role="dialog" aria-modal="true">
<div class="qty-dialog">
<div class="qty-dialog-header">
<h4>合同用量 · 液碱 32%</h4>
<button type="button" class="btn" data-close>×</button>
</div>
<div class="qty-dialog-body">
<p style="margin:0 0 10px;font-size:13px;color:var(--text-muted);">云栋供应商 - HT-CG-2026-102(装货)</p>
<div class="stats">
<div class="stat"><div class="k">合同数量</div><div class="v">200.000</div></div>
<div class="stat"><div class="k">已安排</div><div class="v">150.000</div></div>
<div class="stat"><div class="k">可安排</div><div class="v green">50.000</div></div>
</div>
<div class="sub">已安排本产品的运输计划</div>
<table>
<thead>
<tr><th>计划编号</th><th>计划日期</th><th class="num">计划数量</th><th>状态</th></tr>
</thead>
<tbody>
<tr><td><a href="javascript:;">YP20260812003</a></td><td>2026-08-12</td><td class="num">80.000</td><td>已归档</td></tr>
<tr><td><a href="javascript:;">YP20260820008</a></td><td>2026-08-20</td><td class="num">70.000</td><td>已安排</td></tr>
</tbody>
</table>
</div>
<div class="qty-dialog-footer">
<button type="button" class="btn primary" data-close>✓ 知道了</button>
</div>
</div>
</div>
<div class="mask" id="dlg-over" role="dialog" aria-modal="true">
<div class="qty-dialog">
<div class="qty-dialog-header">
<h4>合同用量 · 98% 硫酸</h4>
<button type="button" class="btn" data-close>×</button>
</div>
<div class="qty-dialog-body">
<p style="margin:0 0 10px;font-size:13px;color:var(--text-muted);">云栋供应商 - HT-CG-2026-102(装货)</p>
<div class="stats">
<div class="stat"><div class="k">合同数量</div><div class="v">100.000</div></div>
<div class="stat"><div class="k">已安排</div><div class="v">80.000</div></div>
<div class="stat"><div class="k">可安排</div><div class="v green">20.000</div></div>
</div>
<div class="sub">已安排本产品的运输计划</div>
<table>
<thead>
<tr><th>计划编号</th><th>计划日期</th><th class="num">计划数量</th><th>状态</th></tr>
</thead>
<tbody>
<tr><td><a href="javascript:;">YP20260810001</a></td><td>2026-08-10</td><td class="num">80.000</td><td>已安排</td></tr>
</tbody>
</table>
</div>
<div class="qty-dialog-footer">
<button type="button" class="btn primary" data-close>✓ 知道了</button>
</div>
</div>
</div>
<div class="mask" id="dlg-del" role="dialog" aria-modal="true">
<div class="qty-dialog">
<div class="qty-dialog-header">
<h4>合同用量 · 液碱 32%</h4>
<button type="button" class="btn" data-close>×</button>
</div>
<div class="qty-dialog-body">
<p style="margin:0 0 10px;font-size:13px;color:var(--text-muted);">某某贸易公司 - HT-XS-2026-088(送货)</p>
<div class="stats">
<div class="stat"><div class="k">合同数量</div><div class="v">180.000</div></div>
<div class="stat"><div class="k">已安排</div><div class="v">40.000</div></div>
<div class="stat"><div class="k">可安排</div><div class="v green">140.000</div></div>
</div>
<div class="sub">已安排本产品的运输计划</div>
<table>
<thead>
<tr><th>计划编号</th><th>计划日期</th><th class="num">计划数量</th><th>状态</th></tr>
</thead>
<tbody>
<tr><td><a href="javascript:;">YP20260815002</a></td><td>2026-08-15</td><td class="num">40.000</td><td>已安排</td></tr>
</tbody>
</table>
</div>
<div class="qty-dialog-footer">
<button type="button" class="btn primary" data-close>✓ 知道了</button>
</div>
</div>
</div>
<script>
(function () {
function openMask(id) {
var el = document.getElementById(id)
if (el) el.classList.add('show')
}
function closeAll() {
document.querySelectorAll('.mask').forEach(function (m) { m.classList.remove('show') })
}
document.querySelectorAll('[data-open]').forEach(function (btn) {
btn.addEventListener('click', function () { openMask(btn.getAttribute('data-open')) })
})
document.querySelectorAll('[data-close]').forEach(function (btn) {
btn.addEventListener('click', closeAll)
})
document.querySelectorAll('.mask').forEach(function (mask) {
mask.addEventListener('click', function (e) {
if (e.target === mask) closeAll()
})
})
})()
</script>
</body>
</html>