Files
zkxw/docs/unused-material-demos/03-collapsible.html
T
2026-06-14 17:20:52 +08:00

290 lines
11 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>03 折叠摘要行 · 登记未用完 Demo</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="_shared.css" />
<style>
.accordion-list {
display: flex;
flex-direction: column;
gap: 8px;
max-height: 520px;
overflow-y: auto;
padding-right: 2px;
}
.acc-item {
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--surface);
overflow: hidden;
}
.acc-item.is-expanded {
border-color: #a7f3d0;
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.08);
}
.acc-summary {
display: grid;
grid-template-columns: 32px 1fr auto auto auto 72px 32px;
gap: 12px;
align-items: center;
padding: 10px 12px;
cursor: pointer;
user-select: none;
}
.acc-summary:hover {
background: #fafafa;
}
.acc-item.is-expanded .acc-summary {
background: var(--primary-bg);
border-bottom: 1px solid #d1fae5;
}
.acc-chevron {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
transition: transform 0.2s;
font-size: 12px;
}
.acc-item.is-expanded .acc-chevron {
transform: rotate(90deg);
color: var(--primary-dark);
}
.acc-name {
font-weight: 600;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.acc-meta {
font-size: 12px;
color: var(--text-muted);
}
.acc-inline-qty {
width: 72px;
}
.acc-body {
display: none;
padding: 12px 14px 14px;
background: #fafafa;
}
.acc-item.is-expanded .acc-body {
display: block;
}
.acc-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 10px 14px;
}
.acc-col-span-2 {
grid-column: span 2;
}
.acc-col-span-4 {
grid-column: 1 / -1;
}
.bulk-bar {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 12px;
flex-wrap: wrap;
}
.bulk-bar .input {
width: 140px;
}
@media (max-width: 960px) {
.acc-summary {
grid-template-columns: 28px 1fr auto 72px 28px;
}
.acc-summary .acc-meta:nth-child(4),
.acc-summary .acc-meta:nth-child(5) {
display: none;
}
.acc-grid {
grid-template-columns: repeat(2, 1fr);
}
}
</style>
</head>
<body>
<header class="demo-banner">
<div>
<h1><span class="demo-badge">03 折叠摘要行</span> 登记未用完物料</h1>
<p>默认一行摘要 + 行内改数量 · 点击展开完整字段</p>
</div>
<div class="demo-banner__actions">
<a href="index.html">← 选型索引</a>
<a href="01-inline-table.html">01 表格</a>
<a class="is-current" href="03-collapsible.html">03 折叠</a>
<a href="04-quick-add.html">04 检索</a>
</div>
</header>
<div class="page-wrap">
<div class="dialog-backdrop">
<div class="dialog dialog--wide" role="dialog">
<div class="dialog__head">
<h2 class="dialog__title">登记未用完物料</h2>
<button type="button" class="dialog__close">×</button>
</div>
<div class="dialog__body">
<div class="section-head">
<div class="section-head__left">
<div class="section-title">未用完明细</div>
<div class="section-hint">折叠态可快速改数量;需改批次或手工字段时再展开。支持全部展开/收起。</div>
</div>
<div class="section-tools">
<span class="stat-pill">共 <strong id="line-count">0</strong> 项</span>
<button type="button" class="btn btn-sm" id="expand-all">全部展开</button>
<button type="button" class="btn btn-sm" id="collapse-all">全部收起</button>
<button type="button" class="btn btn-sm">+ 新增一行</button>
</div>
</div>
<div class="bulk-bar">
<span style="font-size: 12px; color: var(--text-muted)">批量填充持有人:</span>
<input type="text" class="input" id="bulk-holder" placeholder="输入姓名" />
<button type="button" class="btn btn-sm" id="apply-holder">应用到全部</button>
<input type="search" class="input" style="width: 200px; margin-left: auto" placeholder="过滤…" id="filter" />
</div>
<div class="accordion-list" id="accordion"></div>
</div>
<div class="dialog__foot">
<button type="button" class="btn btn-ghost">取消</button>
<button type="button" class="btn btn-primary">保存</button>
</div>
</div>
</div>
<p class="style-note">
<strong>适用场景:</strong>多数行只需改数量/持有人,少数行需展开核对批次。
<ul>
<li>比现状卡片省约 70% 纵向空间,10 条折叠态一屏可见</li>
<li>摘要行右侧 inline 数量框,无需展开即可改</li>
<li>展开区 4 列栅格,比现状 5~6 列略疏,控件 32px</li>
</ul>
</p>
</div>
<script src="_demo-data.js"></script>
<script>
let rows = [...DEMO_UNUSED_LINES]
const expanded = new Set([rows[0]?.id])
const accordion = document.getElementById('accordion')
function render() {
const kw = (document.getElementById('filter').value || '').trim().toLowerCase()
const filtered = rows.filter((r) => !kw || materialSummary(r).toLowerCase().includes(kw))
accordion.innerHTML = filtered
.map((row, i) => {
const isExp = expanded.has(row.id)
const amt = formatMoney(calcAmount(row.unitPrice, row.unusedQty))
return `<div class="acc-item ${isExp ? 'is-expanded' : ''}" data-id="${row.id}">
<div class="acc-summary" data-toggle="${row.id}">
<span class="acc-chevron">▸</span>
<span class="row-index">${rows.indexOf(row) + 1}</span>
<span class="acc-name" title="${row.materialName}">${row.materialName}</span>
<span class="acc-meta">${row.batchNo || '—'}</span>
<span class="acc-meta">${row.holder || '—'}</span>
<span class="acc-meta money">${amt} 元</span>
<input class="input acc-inline-qty qty-inline" type="number" min="0.01" step="0.01" value="${row.unusedQty}" data-id="${row.id}" onclick="event.stopPropagation()" />
<button type="button" class="btn btn-danger-link btn-sm del-btn" data-id="${row.id}" onclick="event.stopPropagation()">×</button>
</div>
<div class="acc-body">
<div class="acc-grid">
<div class="acc-col-span-2">
<label class="field-label">批次检索</label>
<select class="select" ${row.fromBatch ? 'disabled' : ''}><option>${row.fromBatch ? row.stockInNo + ' · ' + row.materialName : '检索或手工'}</option></select>
</div>
<div><label class="field-label">入库编号</label><input class="input input--readonly" readonly value="${row.stockInNo || ''}" /></div>
<div><label class="field-label">批号</label><input class="input input--readonly" readonly value="${row.batchNo || ''}" /></div>
<div><label class="field-label">物料编码</label><input class="input input--readonly" readonly value="${row.materialCode || ''}" /></div>
<div><label class="field-label">货号</label><input class="input input--readonly" readonly value="${row.catalogNo || ''}" /></div>
<div><label class="field-label">规格</label><input class="input input--readonly" readonly value="${row.specModel || ''}" /></div>
<div><label class="field-label">单位</label><input class="input input--readonly" readonly value="${row.unit || ''}" /></div>
<div><label class="field-label">厂家</label><input class="input input--readonly" readonly value="${row.manufacturer || ''}" /></div>
<div><label class="field-label">单价</label><input class="input input--readonly" readonly value="${formatMoney(row.unitPrice)}" /></div>
<div><label class="field-label">持有人</label><input class="input holder-field" data-id="${row.id}" value="${row.holder || ''}" /></div>
<div class="acc-col-span-4"><label class="field-label">备注</label><input class="input remark-field" data-id="${row.id}" value="${row.remark || ''}" /></div>
</div>
</div>
</div>`
})
.join('')
document.getElementById('line-count').textContent = rows.length
}
accordion.addEventListener('click', (e) => {
if (e.target.classList.contains('del-btn')) {
const id = Number(e.target.dataset.id)
rows = rows.filter((r) => r.id !== id)
expanded.delete(id)
render()
return
}
const toggle = e.target.closest('[data-toggle]')
if (toggle && !e.target.classList.contains('qty-inline')) {
const id = Number(toggle.dataset.toggle)
if (expanded.has(id)) expanded.delete(id)
else expanded.add(id)
render()
}
})
accordion.addEventListener('input', (e) => {
const id = Number(e.target.dataset.id)
const row = rows.find((r) => r.id === id)
if (!row) return
if (e.target.classList.contains('qty-inline')) row.unusedQty = Number(e.target.value) || 0
if (e.target.classList.contains('holder-field')) row.holder = e.target.value
if (e.target.classList.contains('remark-field')) row.remark = e.target.value
if (e.target.classList.contains('qty-inline')) render()
})
document.getElementById('expand-all').addEventListener('click', () => {
rows.forEach((r) => expanded.add(r.id))
render()
})
document.getElementById('collapse-all').addEventListener('click', () => {
expanded.clear()
render()
})
document.getElementById('apply-holder').addEventListener('click', () => {
const v = document.getElementById('bulk-holder').value.trim()
if (!v) return
rows.forEach((r) => (r.holder = v))
render()
})
document.getElementById('filter').addEventListener('input', render)
render()
</script>
</body>
</html>