// ===== 收银台(管理员/收银员为玩家代购)===== // 与 admin.php 共享全局作用域,依赖 jsonApi / postApi / toast / escAdmHtml 等全局函数。 let _cashierCart = []; let _cashierCfg = {backpack_rate:0.98, shulker_rate:1.00}; let _cashierSettlement = 'backpack'; let _cashierColor = 'purple'; let _cashierItems = []; let _cashierCategories = []; let _cashierTarget = ''; let _cashierTargetBalance = null; const CASHIER_COLORS = [ {id:'purple', name:'紫色', price:0, css:'#AA00FF'}, {id:'white', name:'白色', price:2, css:'#E8E8E8'}, {id:'black', name:'黑色', price:2, css:'#2A2A2A'}, {id:'red', name:'红色', price:2, css:'#FF4444'}, {id:'blue', name:'蓝色', price:2, css:'#4488FF'}, {id:'green', name:'绿色', price:2, css:'#44FF77'}, {id:'yellow', name:'黄色', price:2, css:'#FFEE44'}, {id:'orange', name:'橙色', price:2, css:'#FF9944'} ]; function cashierMatIcon(mat) { const m = (mat || '').toUpperCase(); if (m.indexOf('DIAMOND') >= 0) return '💎'; if (m.indexOf('IRON') >= 0) return '⛏️'; if (m.indexOf('GOLD') >= 0) return '🪙'; if (m.indexOf('EMERALD') >= 0) return '🟢'; if (m.indexOf('NETHERITE') >= 0) return '⬛'; if (m.indexOf('WOOD') >= 0 || m.indexOf('PLANK') >= 0 || m.indexOf('LOG') >= 0) return '🪵'; if (m.indexOf('STONE') >= 0 || m.indexOf('COBBLE') >= 0) return '🪨'; if (m.indexOf('APPLE') >= 0 || m.indexOf('BREAD') >= 0 || m.indexOf('MEAT') >= 0 || m.indexOf('FISH') >= 0 || m.indexOf('PORK') >= 0 || m.indexOf('BEEF') >= 0 || m.indexOf('CHICKEN') >= 0) return '🍖'; if (m.indexOf('POTION') >= 0 || m.indexOf('BOTTLE') >= 0) return '🧪'; if (m.indexOf('BOOK') >= 0 || m.indexOf('ENCHANT') >= 0) return '📕'; if (m.indexOf('SWORD') >= 0 || m.indexOf('AXE') >= 0 || m.indexOf('PICK') >= 0 || m.indexOf('SHOVEL') >= 0 || m.indexOf('HOE') >= 0) return '🗡️'; if (m.indexOf('HELMET') >= 0 || m.indexOf('CHEST') >= 0 || m.indexOf('LEGGING') >= 0 || m.indexOf('BOOT') >= 0) return '🛡️'; if (m.indexOf('BED') >= 0) return '🛏️'; if (m.indexOf('TNT') >= 0) return '🧨'; return '📦'; } async function loadCashier(el) { el.innerHTML = '
加载中...
'; try { const r = await jsonApi('shop.php?action=cart_config'); if (r && r.success && r.data) _cashierCfg = r.data; } catch (e) {} try { const r = await jsonApi('shop.php?action=list'); _cashierItems = (r && r.data) ? r.data : []; } catch (e) { _cashierItems = []; } _cashierCategories = [...new Set(_cashierItems.map(i => i.category || '默认'))]; _cashierTarget = ''; _cashierTargetBalance = null; _cashierCart = []; _cashierSettlement = 'backpack'; _cashierColor = 'purple'; let catTabs = ''; if (_cashierCategories.length > 0) { catTabs = '
'; _cashierCategories.forEach((cat, idx) => { const active = idx === 0 ? 'color:#fff;background:var(--accent);border-color:var(--accent)' : ''; const cnt = _cashierItems.filter(i => (i.category || '默认') === cat).length; catTabs += '
' + escAdmHtml(cat) + ' (' + cnt + ')
'; }); catTabs += '
'; } el.innerHTML = `

🧾 收银台 为玩家代购商品

① 选择目标玩家

输入玩家名后查询其债券余额

② 选择商品

${catTabs}

🛒 代购清单

`; document.querySelectorAll('.cashier-cat-tab').forEach(tab => { tab.addEventListener('click', () => cashierSwitchCat(tab.dataset.cat)); }); cashierSwitchCat(_cashierCategories[0] || ''); renderCashierCart(); } function cashierSwitchCat(cat) { document.querySelectorAll('.cashier-cat-tab').forEach(c => { const active = c.dataset.cat === cat; c.style.background = active ? 'var(--accent)' : ''; c.style.color = active ? '#fff' : ''; c.style.borderColor = active ? 'var(--accent)' : ''; }); const wrap = document.getElementById('cashierItems'); if (!wrap) return; const items = _cashierItems.filter(i => (i.category || '默认') === cat); if (items.length === 0) { wrap.innerHTML = '
该分类暂无商品
'; return; } let h = ''; items.forEach(i => { const price = parseInt(i.buy_price) || 0; const stock = parseInt(i.stock); const stockTxt = stock > 0 ? ('库存 ' + stock) : '无限'; h += '
'; h += '
' + cashierMatIcon(i.material) + '' + escAdmHtml(i.display_name || i.id) + '
'; h += '
' + price + ' 债券 · ' + stockTxt + '
'; h += '
'; h += ''; h += ''; h += ''; h += ''; h += '
'; }); wrap.innerHTML = h; wrap.querySelectorAll('[data-minus]').forEach(b => b.addEventListener('click', () => cashierItemQty(b.dataset.minus, -1))); wrap.querySelectorAll('[data-plus]').forEach(b => b.addEventListener('click', () => cashierItemQty(b.dataset.plus, 1))); wrap.querySelectorAll('[data-add]').forEach(b => b.addEventListener('click', () => cashierAddItem(b.dataset.add))); } function cashierItemQty(id, delta) { const inp = document.getElementById('cqty_' + id); if (!inp) return; let v = parseInt(inp.value) || 1; v = Math.max(1, v + delta); inp.value = v; } function cashierAddItem(id) { const item = _cashierItems.find(i => i.id == id); if (!item) return; const inp = document.getElementById('cqty_' + id); let amount = parseInt(inp ? inp.value : 1) || 1; amount = Math.max(1, amount); const price = parseInt(item.buy_price) || 0; const stock = parseInt(item.stock); const exist = _cashierCart.find(c => c.item_id == id); if (exist) { const max = stock > 0 ? stock : 9999; exist.amount = Math.min(exist.amount + amount, max); } else { _cashierCart.push({item_id: id, name: item.display_name || item.id, price: price, material: item.material || '', stock: stock, amount: amount}); } renderCashierCart(); toast('已加入代购清单: ' + (item.display_name || item.id), 'ok'); } function cashierChangeQty(id, delta) { const i = _cashierCart.find(c => c.item_id == id); if (!i) return; const max = i.stock > 0 ? i.stock : 9999; i.amount = Math.min(Math.max(i.amount + delta, 1), max); renderCashierCart(); } function cashierRemoveItem(id) { _cashierCart = _cashierCart.filter(c => c.item_id != id); renderCashierCart(); } function cashierClearCart() { _cashierCart = []; renderCashierCart(); } function cashierColorFee() { const c = CASHIER_COLORS.find(x => x.id === _cashierColor); return c ? c.price : 0; } function cashierSubtotal() { return _cashierCart.reduce((s, i) => s + i.price * i.amount, 0); } function cashierTotal() { const sub = cashierSubtotal(); const rate = _cashierSettlement === 'shulker' ? _cashierCfg.shulker_rate : _cashierCfg.backpack_rate; return Math.round(sub * rate) + (_cashierSettlement === 'shulker' ? cashierColorFee() : 0); } function cashierModeHtml(mode, title, total, desc) { const sel = _cashierSettlement === mode; return '
' + title + '' + total + ' 债券
' + desc + '
'; } function renderCashierCart() { const list = document.getElementById('cashierCartList'); const settleWrap = document.getElementById('cashierSettleWrap'); if (!list || !settleWrap) return; if (_cashierCart.length === 0) { list.innerHTML = '
清单为空,请从左侧添加商品
'; } else { let h = ''; _cashierCart.forEach(i => { h += '
'; h += '' + cashierMatIcon(i.material) + ''; h += '
' + escAdmHtml(i.name) + '
'; h += '
' + i.price + ' 债券/个
'; h += '
'; h += '' + i.amount + ''; h += '
'; h += '
' + (i.price * i.amount) + '
'; h += '
'; }); list.innerHTML = h; } const sub = cashierSubtotal(); const total = cashierTotal(); let html = ''; html += '
原价合计' + sub + ' 债券
'; html += '
'; const bpTotal = Math.round(sub * _cashierCfg.backpack_rate); const shTotal = Math.round(sub * _cashierCfg.shulker_rate) + cashierColorFee(); html += cashierModeHtml('backpack', '🎒 塞背包', bpTotal, '享 ' + (_cashierCfg.backpack_rate * 10).toFixed(1) + ' 折'); html += cashierModeHtml('shulker', '📦 潜影盒打包', shTotal, '加价 ' + ((_cashierCfg.shulker_rate - 1) * 100).toFixed(0) + '%' + (cashierColorFee() > 0 ? (' +' + cashierColorFee() + '元') : '')); html += '
'; if (_cashierSettlement === 'shulker') { html += '
'; html += '
🎨 潜影盒颜色 (紫色免费,其它+2元)
'; html += '
'; CASHIER_COLORS.forEach(c => { const sel = c.id === _cashierColor; const extra = c.price > 0 ? ' (+' + c.price + ')' : ''; html += ''; }); html += '
'; } html += '
预估总价' + total + ' 债券
'; html += '
'; html += '
'; settleWrap.innerHTML = html; const balHint = document.getElementById('cashierBalanceHint'); if (balHint) { if (!_cashierTarget) balHint.textContent = '未选择目标玩家'; else if (_cashierTargetBalance != null) { balHint.textContent = '目标余额: ' + _cashierTargetBalance + ' 债券' + (_cashierTargetBalance < total ? '(余额不足!)' : ''); balHint.style.color = _cashierTargetBalance < total ? 'var(--red)' : 'var(--dim)'; } } } function cashierSelectMode(mode) { _cashierSettlement = mode; renderCashierCart(); } function cashierSelectColor(id) { _cashierColor = id; renderCashierCart(); } async function cashierCheckPlayer() { const inp = document.getElementById('cashierPlayer'); const info = document.getElementById('cashierTargetInfo'); if (!inp || !info) return; const player = inp.value.trim(); if (!player) { info.textContent = '请输入玩家名'; info.style.color = 'var(--red)'; return; } info.textContent = '查询中...'; info.style.color = 'var(--dim)'; try { const r = await postApi('cashier_player_check', {player: player}); if (r && r.success && r.data) { _cashierTarget = r.data.player; _cashierTargetBalance = r.data.balance; const online = r.data.online ? ' ●在线' : ' ○离线'; info.innerHTML = '目标玩家: ' + escAdmHtml(r.data.player) + '' + online + ' 债券余额: ' + r.data.balance + ''; info.style.color = 'var(--text)'; } else { _cashierTarget = ''; _cashierTargetBalance = null; info.textContent = '查询失败: ' + (r ? r.message : '未知错误'); info.style.color = 'var(--red)'; } } catch (e) { _cashierTarget = ''; _cashierTargetBalance = null; info.textContent = '查询失败: ' + e.message; info.style.color = 'var(--red)'; } renderCashierCart(); } async function cashierSettle() { if (_cashierCart.length === 0) { toast('代购清单为空', 'err'); return; } if (!_cashierTarget) { toast('请先选择并查询目标玩家', 'err'); return; } const total = cashierTotal(); const sub = cashierSubtotal(); if (_cashierTargetBalance != null && _cashierTargetBalance < total) { toast('目标玩家余额不足(' + _cashierTargetBalance + ' < ' + total + ')', 'err'); return; } const lines = _cashierCart.map(i => '· ' + i.name + ' x' + i.amount + ' = ' + (i.price * i.amount) + ' 债券').join('\n'); const modeName = _cashierSettlement === 'shulker' ? ('潜影盒打包(' + (CASHIER_COLORS.find(c => c.id === _cashierColor) ? CASHIER_COLORS.find(c => c.id === _cashierColor).name : '') + ')') : '塞背包'; const summary = '目标玩家: ' + _cashierTarget + '\n结算方式: ' + modeName + '\n原价: ' + sub + ' 债券\n预估总价: ' + total + ' 债券\n\n' + lines; cashierShowConfirm(total, summary); } function cashierShowConfirm(total, summary) { const needPwd = total > 1000; let overlay = document.getElementById('cashierConfirmOverlay'); if (overlay) overlay.remove(); overlay = document.createElement('div'); overlay.id = 'cashierConfirmOverlay'; overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.5);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);z-index:6000;display:flex;justify-content:center;align-items:center;animation:glassFadeIn .25s ease'; overlay.onclick = (e) => { if (e.target === overlay) overlay.remove(); }; let html = '
'; html += '

🧾 确认代购

'; html += '
' + escAdmHtml(summary) + '
'; if (needPwd) { html += ''; html += ''; } html += '
'; html += '
'; overlay.innerHTML = html; document.body.appendChild(overlay); if (needPwd) { const p = document.getElementById('cashierPwd'); if (p) p.focus(); } } async function cashierConfirmBuy() { const total = cashierTotal(); const pwdEl = document.getElementById('cashierPwd'); const password = pwdEl ? pwdEl.value : ''; if (total > 1000 && !password) { toast('请输入管理员密码确认', 'err'); if (pwdEl) pwdEl.focus(); return; } const btn = document.getElementById('cashierConfirmBtn'); if (btn) { btn.disabled = true; btn.textContent = '代购中...'; } await cashierDoBuy(total, password); const ov = document.getElementById('cashierConfirmOverlay'); if (ov) ov.remove(); } async function cashierDoBuy(total, password) { try { // 1) 获取管理员代购令牌(all 权限,单次使用) const tR = await postApi('gen_token', {player: _cashierTarget, purpose: 'all'}); if (!tR || !tR.success || !tR.data || !tR.data.token) { toast('获取代购令牌失败', 'err'); return; } const token = tR.data.token; const items = _cashierCart.map(i => ({item_id: i.item_id, amount: i.amount})); const body = { token: token, items: JSON.stringify(items), settlement: _cashierSettlement, player: _cashierTarget, password: password || undefined }; if (_cashierSettlement === 'shulker') body.shulker_color = _cashierColor; const res = await fetch('api/shop.php?action=buy_cart', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(body) }); const data = await res.json(); if (data && data.need_password) { toast('请重新输入密码确认', 'err'); cashierShowConfirm(total, '目标玩家: ' + _cashierTarget + '\n(需密码确认)'); return; } if (data && data.success) { toast('代购成功:' + _cashierTarget + ' 已获得商品', 'ok'); _cashierCart = []; renderCashierCart(); cashierCheckPlayer(); } else { toast('代购失败: ' + (data ? data.message : '未知错误'), 'err'); } } catch (e) { toast('代购异常: ' + e.message, 'err'); } }