Capture confirmed UI (always-on checkboxes) and serial remove via existing occupancy API for implementation planning. Co-authored-by: Cursor <cursoragent@cursor.com>
667 lines
21 KiB
HTML
667 lines
21 KiB
HTML
<!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: #1e293b;
|
||
--muted: #64748b;
|
||
--border: #e2e8f0;
|
||
--danger: #ef4444;
|
||
--danger-bg: #fef2f2;
|
||
--danger-border: #fecaca;
|
||
--orange: #ea580c;
|
||
--orange-border: #fed7aa;
|
||
--orange-soft: #fff7ed;
|
||
}
|
||
* { box-sizing: border-box; }
|
||
body {
|
||
margin: 0;
|
||
font-family: Inter, "PingFang SC", "Helvetica Neue", Arial, sans-serif;
|
||
background: #f1f5f9;
|
||
color: var(--text);
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
}
|
||
.page { max-width: 1100px; margin: 0 auto; padding: 20px 16px 80px; }
|
||
.intro {
|
||
background: #fff;
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
padding: 16px 18px;
|
||
margin-bottom: 16px;
|
||
}
|
||
.intro h1 { margin: 0 0 6px; font-size: 18px; }
|
||
.intro p { margin: 0; color: var(--muted); font-size: 13px; }
|
||
.approaches {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 12px;
|
||
margin-bottom: 16px;
|
||
}
|
||
@media (max-width: 900px) {
|
||
.approaches { grid-template-columns: 1fr; }
|
||
}
|
||
.approach {
|
||
background: #fff;
|
||
border: 2px solid var(--border);
|
||
border-radius: 10px;
|
||
padding: 14px;
|
||
cursor: pointer;
|
||
transition: border-color .15s, box-shadow .15s;
|
||
}
|
||
.approach:hover { border-color: #a7f3d0; }
|
||
.approach.active {
|
||
border-color: var(--primary);
|
||
box-shadow: 0 0 0 3px rgba(16,185,129,.15);
|
||
}
|
||
.approach .badge {
|
||
display: inline-block;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
padding: 2px 8px;
|
||
border-radius: 999px;
|
||
background: var(--primary-bg);
|
||
color: var(--primary-dark);
|
||
margin-bottom: 8px;
|
||
}
|
||
.approach .badge.rec { background: #d1fae5; }
|
||
.approach h3 { margin: 0 0 6px; font-size: 15px; }
|
||
.approach p { margin: 0; font-size: 12px; color: var(--muted); }
|
||
.panel {
|
||
background: #fff;
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
padding: 16px 18px 20px;
|
||
display: flex;
|
||
gap: 16px;
|
||
min-height: 520px;
|
||
}
|
||
.aside {
|
||
width: 220px;
|
||
flex-shrink: 0;
|
||
border-right: 1px solid var(--border);
|
||
padding-right: 14px;
|
||
}
|
||
.aside h2 {
|
||
margin: 0 0 12px;
|
||
font-size: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.aside h2::before {
|
||
content: "";
|
||
width: 3px;
|
||
height: 14px;
|
||
background: var(--primary);
|
||
border-radius: 2px;
|
||
}
|
||
.tree-node {
|
||
padding: 8px 10px;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
color: var(--muted);
|
||
}
|
||
.tree-node.active {
|
||
background: var(--primary-bg);
|
||
color: var(--primary-dark);
|
||
font-weight: 600;
|
||
}
|
||
.tree-node .leaf {
|
||
float: right;
|
||
font-size: 11px;
|
||
color: var(--primary);
|
||
background: #d1fae5;
|
||
padding: 0 6px;
|
||
border-radius: 4px;
|
||
}
|
||
.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
||
.header { margin-bottom: 12px; padding-bottom: 10px; border-bottom: 1px solid #f0f0f0; }
|
||
.header h3 { margin: 0; font-size: 15px; }
|
||
.stats { font-size: 12px; color: var(--muted); margin-top: 4px; }
|
||
.stats strong { color: var(--text); }
|
||
.filter {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
.filter input {
|
||
width: 100%;
|
||
max-width: 360px;
|
||
height: 36px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
padding: 0 12px;
|
||
font-size: 13px;
|
||
}
|
||
.toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 14px;
|
||
flex-wrap: wrap;
|
||
min-height: 36px;
|
||
}
|
||
.toolbar .selected-hint {
|
||
font-size: 13px;
|
||
color: var(--muted);
|
||
margin-right: 4px;
|
||
}
|
||
.toolbar .selected-hint em {
|
||
font-style: normal;
|
||
color: var(--primary-dark);
|
||
font-weight: 600;
|
||
}
|
||
.btn {
|
||
height: 32px;
|
||
padding: 0 14px;
|
||
border-radius: 8px;
|
||
border: 1px solid var(--border);
|
||
background: #fff;
|
||
color: var(--text);
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
.btn:disabled { opacity: .45; cursor: not-allowed; }
|
||
.btn-primary {
|
||
background: var(--primary);
|
||
border-color: var(--primary);
|
||
color: #fff;
|
||
}
|
||
.btn-primary:hover:not(:disabled) { background: var(--primary-dark); }
|
||
.btn-danger {
|
||
background: #fff;
|
||
border-color: #fca5a5;
|
||
color: #dc2626;
|
||
}
|
||
.btn-danger:hover:not(:disabled) { background: var(--danger-bg); }
|
||
.btn-ghost:hover:not(:disabled) { background: #f8fafc; }
|
||
.mode-banner {
|
||
display: none;
|
||
align-items: center;
|
||
gap: 10px;
|
||
background: #fff7ed;
|
||
border: 1px solid #fdba74;
|
||
color: #9a3412;
|
||
border-radius: 8px;
|
||
padding: 8px 12px;
|
||
font-size: 13px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.mode-banner.show { display: flex; }
|
||
.mode-banner strong { color: #c2410c; }
|
||
.shelf { display: flex; flex-direction: column; gap: 10px; }
|
||
.row { display: flex; align-items: stretch; }
|
||
.row-label {
|
||
width: 36px;
|
||
flex-shrink: 0;
|
||
background: var(--orange);
|
||
color: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 600;
|
||
border-radius: 4px 0 0 4px;
|
||
font-size: 14px;
|
||
}
|
||
.cells { display: flex; flex-wrap: wrap; gap: 6px; }
|
||
.cell {
|
||
width: 80px;
|
||
height: 80px;
|
||
border: 1px solid var(--orange-border);
|
||
border-radius: 4px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
position: relative;
|
||
padding: 4px 2px;
|
||
gap: 1px;
|
||
background: #fff;
|
||
transition: all .15s;
|
||
user-select: none;
|
||
}
|
||
.cell:hover { background: var(--orange-soft); }
|
||
.cell.occupied {
|
||
background: var(--danger-bg);
|
||
border-color: var(--danger-border);
|
||
}
|
||
.cell.occupied:hover { background: #fee2e2; }
|
||
.cell .name {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: #991b1b;
|
||
text-align: center;
|
||
line-height: 1.2;
|
||
max-width: 72px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.cell .type {
|
||
font-size: 10px;
|
||
color: #b91c1c;
|
||
max-width: 72px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.cell .empty { font-size: 12px; font-weight: 600; color: #303133; }
|
||
.cell .check {
|
||
display: none;
|
||
position: absolute;
|
||
top: 3px;
|
||
left: 3px;
|
||
width: 18px;
|
||
height: 18px;
|
||
border-radius: 4px;
|
||
border: 1.5px solid #cbd5e1;
|
||
background: #fff;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 11px;
|
||
color: transparent;
|
||
z-index: 2;
|
||
}
|
||
body.select-mode .cell.occupied .check { display: flex; }
|
||
body.select-mode .cell.empty-slot {
|
||
opacity: .4;
|
||
cursor: not-allowed;
|
||
}
|
||
body.select-mode .cell.occupied.selected {
|
||
outline: 2px solid var(--primary);
|
||
outline-offset: 1px;
|
||
background: #ecfdf5;
|
||
border-color: #a7f3d0;
|
||
}
|
||
body.select-mode .cell.occupied.selected .name,
|
||
body.select-mode .cell.occupied.selected .type { color: var(--primary-dark); }
|
||
body.select-mode .cell.occupied.selected .check {
|
||
background: var(--primary);
|
||
border-color: var(--primary);
|
||
color: #fff;
|
||
}
|
||
body.select-mode .cell.occupied:not(.selected):hover .check {
|
||
border-color: var(--primary);
|
||
}
|
||
.toast {
|
||
position: fixed;
|
||
top: 20px;
|
||
left: 50%;
|
||
transform: translateX(-50%) translateY(-20px);
|
||
background: #065f46;
|
||
color: #fff;
|
||
padding: 10px 18px;
|
||
border-radius: 8px;
|
||
font-size: 13px;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: all .25s;
|
||
z-index: 100;
|
||
}
|
||
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
|
||
.mask {
|
||
display: none;
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(15, 23, 42, .35);
|
||
z-index: 90;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.mask.show { display: flex; }
|
||
.dialog {
|
||
background: #fff;
|
||
border-radius: 10px;
|
||
width: 420px;
|
||
max-width: 92vw;
|
||
padding: 20px;
|
||
box-shadow: 0 20px 40px rgba(0,0,0,.12);
|
||
}
|
||
.dialog h4 { margin: 0 0 8px; font-size: 16px; }
|
||
.dialog p { margin: 0 0 14px; color: var(--muted); font-size: 13px; }
|
||
.dialog .list {
|
||
max-height: 160px;
|
||
overflow: auto;
|
||
background: #f8fafc;
|
||
border-radius: 8px;
|
||
padding: 8px 10px;
|
||
margin-bottom: 16px;
|
||
font-size: 12px;
|
||
}
|
||
.dialog .list div { padding: 3px 0; }
|
||
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; }
|
||
.hint-bar {
|
||
margin-top: 14px;
|
||
font-size: 12px;
|
||
color: var(--muted);
|
||
background: #f8fafc;
|
||
border-radius: 8px;
|
||
padding: 10px 12px;
|
||
}
|
||
.hint-bar code {
|
||
background: #e2e8f0;
|
||
padding: 1px 5px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="page">
|
||
<div class="intro">
|
||
<h1>库位管理 · 批量出库交互 Demo</h1>
|
||
<p>先点下方方案切换预览,再在货架上试操作。推荐方案 A(工具栏进入多选模式)。</p>
|
||
</div>
|
||
|
||
<div class="approaches">
|
||
<div class="approach active" data-choice="a" onclick="pickApproach(this)">
|
||
<span class="badge rec">推荐 · A</span>
|
||
<h3>工具栏「批量出库」→ 多选格子</h3>
|
||
<p>点按钮进入选择模式,只可选占用格;确认后一次性出库。空格不可选。</p>
|
||
</div>
|
||
<div class="approach" data-choice="b" onclick="pickApproach(this)">
|
||
<span class="badge">B</span>
|
||
<h3>格子常驻勾选框</h3>
|
||
<p>占用格左上角始终显示勾选框,选完后点工具栏「出库」。更直接,但日常浏览稍吵。</p>
|
||
</div>
|
||
<div class="approach" data-choice="c" onclick="pickApproach(this)">
|
||
<span class="badge">C</span>
|
||
<h3>框选 / 起止区间</h3>
|
||
<p>拖拽或选起止格批量勾选同盒连续占用格。适合密集出库,交互更重。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="panel">
|
||
<aside class="aside">
|
||
<h2>库位</h2>
|
||
<div class="tree-node">A区冷库</div>
|
||
<div class="tree-node" style="padding-left:22px">1层</div>
|
||
<div class="tree-node active" style="padding-left:34px">
|
||
盒-01 <span class="leaf">存</span>
|
||
</div>
|
||
</aside>
|
||
|
||
<div class="main">
|
||
<div class="header">
|
||
<h3>A区冷库 / 1层 / 盒-01</h3>
|
||
<div class="stats">
|
||
总数 <strong id="statTotal">12</strong>
|
||
· 空闲 <strong id="statEmpty">4</strong>
|
||
· 占用 <strong id="statOcc">8</strong>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="filter">
|
||
<input type="text" placeholder="物品名称 / 产品类型 / 批次号" disabled />
|
||
</div>
|
||
|
||
<div class="mode-banner" id="modeBanner">
|
||
<strong>批量出库模式</strong>
|
||
<span>请勾选要出库的占用储物格,空格不可选。</span>
|
||
</div>
|
||
|
||
<div class="toolbar" id="toolbar"></div>
|
||
|
||
<div class="shelf" id="shelf"></div>
|
||
|
||
<div class="hint-bar" id="hint">
|
||
当前方案 A:先点「批量出库」进入选择模式,再点占用格勾选,最后确认出库。
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="toast" id="toast"></div>
|
||
|
||
<div class="mask" id="mask">
|
||
<div class="dialog">
|
||
<h4>批量出库确认</h4>
|
||
<p id="confirmText">确认将选中储物格中的物品出库?</p>
|
||
<div class="list" id="confirmList"></div>
|
||
<div class="dialog-actions">
|
||
<button class="btn btn-ghost" onclick="closeConfirm()">取消</button>
|
||
<button class="btn btn-primary" onclick="doOutbound()">确认出库</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const HINTS = {
|
||
a: '当前方案 A:先点「批量出库」进入选择模式,再点占用格勾选,最后确认出库。',
|
||
b: '当前方案 B:占用格左上角常驻勾选框,勾选后点工具栏「出库已选」。',
|
||
c: '当前方案 C:演示简化为「按行全选占用格」+ 手动点选;正式版可再做拖拽框选/起止区间。'
|
||
}
|
||
|
||
let approach = 'a'
|
||
let selectMode = false
|
||
let selected = new Set()
|
||
|
||
const initialCells = [
|
||
// row A
|
||
{ id: 'A1', row: 'A', name: 'CP-1001', type: 'MSC', occupied: true },
|
||
{ id: 'A2', row: 'A', name: 'CP-1002', type: 'MSC', occupied: true },
|
||
{ id: 'A3', row: 'A', name: '', type: '', occupied: false },
|
||
{ id: 'A4', row: 'A', name: 'CP-1004', type: 'NK', occupied: true },
|
||
// row B
|
||
{ id: 'B1', row: 'B', name: 'CP-2001', type: 'CAR-T', occupied: true },
|
||
{ id: 'B2', row: 'B', name: '', type: '', occupied: false },
|
||
{ id: 'B3', row: 'B', name: 'CP-2003', type: 'MSC', occupied: true },
|
||
{ id: 'B4', row: 'B', name: 'CP-2004', type: 'MSC', occupied: true },
|
||
// row C
|
||
{ id: 'C1', row: 'C', name: '', type: '', occupied: false },
|
||
{ id: 'C2', row: 'C', name: 'CP-3002', type: 'NK', occupied: true },
|
||
{ id: 'C3', row: 'C', name: 'CP-3003', type: 'NK', occupied: true },
|
||
{ id: 'C4', row: 'C', name: '', type: '', occupied: false }
|
||
]
|
||
|
||
let cells = JSON.parse(JSON.stringify(initialCells))
|
||
|
||
function pickApproach(el) {
|
||
document.querySelectorAll('.approach').forEach(a => a.classList.remove('active'))
|
||
el.classList.add('active')
|
||
approach = el.dataset.choice
|
||
selectMode = approach === 'b' // B always selectable
|
||
selected.clear()
|
||
document.getElementById('hint').textContent = HINTS[approach]
|
||
render()
|
||
if (window.toggleSelect) {
|
||
// companion helper if present
|
||
try { window.toggleSelect(el) } catch (_) {}
|
||
}
|
||
}
|
||
|
||
function updateStats() {
|
||
const total = cells.length
|
||
const occ = cells.filter(c => c.occupied).length
|
||
document.getElementById('statTotal').textContent = total
|
||
document.getElementById('statEmpty').textContent = total - occ
|
||
document.getElementById('statOcc').textContent = occ
|
||
}
|
||
|
||
function renderToolbar() {
|
||
const tb = document.getElementById('toolbar')
|
||
const n = selected.size
|
||
if (approach === 'a') {
|
||
if (!selectMode) {
|
||
tb.innerHTML = `
|
||
<button class="btn btn-primary" onclick="enterSelectMode()">⬆ 批量出库</button>
|
||
<button class="btn btn-ghost" disabled>批量生成格子</button>
|
||
<button class="btn btn-ghost" disabled>清空格子</button>
|
||
`
|
||
} else {
|
||
tb.innerHTML = `
|
||
<span class="selected-hint">已选 <em>${n}</em> 项</span>
|
||
<button class="btn btn-primary" ${n ? '' : 'disabled'} onclick="openConfirm()">确认出库</button>
|
||
<button class="btn btn-ghost" onclick="selectAllOccupied()">全选占用</button>
|
||
<button class="btn btn-ghost" onclick="clearSelection()">清空选择</button>
|
||
<button class="btn btn-ghost" onclick="exitSelectMode()">取消</button>
|
||
`
|
||
}
|
||
} else if (approach === 'b') {
|
||
tb.innerHTML = `
|
||
<span class="selected-hint">已选 <em>${n}</em> 项</span>
|
||
<button class="btn btn-primary" ${n ? '' : 'disabled'} onclick="openConfirm()">⬆ 出库已选</button>
|
||
<button class="btn btn-ghost" onclick="selectAllOccupied()">全选占用</button>
|
||
<button class="btn btn-ghost" onclick="clearSelection()">清空选择</button>
|
||
`
|
||
} else {
|
||
tb.innerHTML = `
|
||
<span class="selected-hint">已选 <em>${n}</em> 项</span>
|
||
<button class="btn btn-primary" ${n ? '' : 'disabled'} onclick="openConfirm()">⬆ 确认出库</button>
|
||
<button class="btn btn-ghost" onclick="selectRow('A')">全选 A 行</button>
|
||
<button class="btn btn-ghost" onclick="selectRow('B')">全选 B 行</button>
|
||
<button class="btn btn-ghost" onclick="selectRow('C')">全选 C 行</button>
|
||
<button class="btn btn-ghost" onclick="clearSelection()">清空选择</button>
|
||
`
|
||
}
|
||
}
|
||
|
||
function renderShelf() {
|
||
const shelf = document.getElementById('shelf')
|
||
const rows = ['A', 'B', 'C']
|
||
shelf.innerHTML = rows.map(row => {
|
||
const rowCells = cells.filter(c => c.row === row)
|
||
return `
|
||
<div class="row">
|
||
<div class="row-label">${row}</div>
|
||
<div class="cells">
|
||
${rowCells.map(c => {
|
||
const occ = c.occupied
|
||
const sel = selected.has(c.id)
|
||
return `
|
||
<div class="cell ${occ ? 'occupied' : 'empty-slot'} ${sel ? 'selected' : ''}"
|
||
data-id="${c.id}"
|
||
onclick="onCellClick('${c.id}')"
|
||
title="${occ ? c.name + ' · 点击选择/取消' : '空格不可出库'}">
|
||
<span class="check">✓</span>
|
||
${occ
|
||
? `<span class="name">${c.name}</span><span class="type">${c.type}</span>`
|
||
: `<span class="empty">—</span>`}
|
||
</div>
|
||
`
|
||
}).join('')}
|
||
</div>
|
||
</div>
|
||
`
|
||
}).join('')
|
||
}
|
||
|
||
function render() {
|
||
const showBanner = approach === 'a' && selectMode
|
||
document.getElementById('modeBanner').classList.toggle('show', showBanner)
|
||
document.body.classList.toggle('select-mode', approach === 'a' ? selectMode : true)
|
||
updateStats()
|
||
renderToolbar()
|
||
renderShelf()
|
||
}
|
||
|
||
function enterSelectMode() {
|
||
selectMode = true
|
||
selected.clear()
|
||
render()
|
||
toast('已进入批量出库模式')
|
||
}
|
||
|
||
function exitSelectMode() {
|
||
selectMode = false
|
||
selected.clear()
|
||
render()
|
||
}
|
||
|
||
function onCellClick(id) {
|
||
const cell = cells.find(c => c.id === id)
|
||
if (!cell || !cell.occupied) {
|
||
if (approach === 'a' && selectMode) toast('空格不可出库')
|
||
else if (approach !== 'a') toast('空格不可出库')
|
||
return
|
||
}
|
||
// 方案 A:非选择模式 → 模拟打开详情(提示)
|
||
if (approach === 'a' && !selectMode) {
|
||
toast(`打开储物格明细 — ${cell.name}(现有行为)`)
|
||
return
|
||
}
|
||
if (selected.has(id)) selected.delete(id)
|
||
else selected.add(id)
|
||
render()
|
||
}
|
||
|
||
function selectAllOccupied() {
|
||
cells.filter(c => c.occupied).forEach(c => selected.add(c.id))
|
||
render()
|
||
}
|
||
|
||
function selectRow(row) {
|
||
cells.filter(c => c.row === row && c.occupied).forEach(c => selected.add(c.id))
|
||
render()
|
||
}
|
||
|
||
function clearSelection() {
|
||
selected.clear()
|
||
render()
|
||
}
|
||
|
||
function openConfirm() {
|
||
if (!selected.size) return
|
||
const list = [...selected].map(id => {
|
||
const c = cells.find(x => x.id === id)
|
||
return `<div>${id} ${c.name} ${c.type}</div>`
|
||
}).join('')
|
||
document.getElementById('confirmText').textContent =
|
||
`确认将选中的 ${selected.size} 个储物格物品出库?`
|
||
document.getElementById('confirmList').innerHTML = list
|
||
document.getElementById('mask').classList.add('show')
|
||
}
|
||
|
||
function closeConfirm() {
|
||
document.getElementById('mask').classList.remove('show')
|
||
}
|
||
|
||
function doOutbound() {
|
||
const ids = [...selected]
|
||
cells.forEach(c => {
|
||
if (ids.includes(c.id)) {
|
||
c.occupied = false
|
||
c.name = ''
|
||
c.type = ''
|
||
}
|
||
})
|
||
selected.clear()
|
||
if (approach === 'a') selectMode = false
|
||
closeConfirm()
|
||
render()
|
||
toast(`出库成功,共 ${ids.length} 项`)
|
||
}
|
||
|
||
function toast(msg) {
|
||
const el = document.getElementById('toast')
|
||
el.textContent = msg
|
||
el.classList.add('show')
|
||
clearTimeout(toast._t)
|
||
toast._t = setTimeout(() => el.classList.remove('show'), 1800)
|
||
}
|
||
|
||
// reset helper for demo
|
||
window.resetDemo = function () {
|
||
cells = JSON.parse(JSON.stringify(initialCells))
|
||
selected.clear()
|
||
selectMode = approach === 'b'
|
||
render()
|
||
toast('已重置货架数据')
|
||
}
|
||
|
||
render()
|
||
</script>
|
||
</body>
|
||
</html>
|