docs(architecture): adopt authorized single-pass purchase
This commit is contained in:
@@ -191,8 +191,8 @@
|
||||
|
||||
<main id="main">
|
||||
<header class="page-head">
|
||||
<div><h1>采购任务</h1><p class="subtitle">创建后先保存为待开始;勾选后统一进入第一趟试选队列。</p></div>
|
||||
<p class="safety-note"><strong>只开始试选,不会下单。</strong> 下单必须在试选完成并经人工确认后另行授权,付款始终由人完成。</p>
|
||||
<div><h1>采购任务</h1><p class="subtitle">创建后先保存为待开始;勾选并开始采购后,采购工具按授权逐条执行。</p></div>
|
||||
<p class="safety-note"><strong>开始采购会授权创建待付款订单。</strong> 系统校验规格、数量和金额后最多提交一次,任何时候都不会自动付款。</p>
|
||||
</header>
|
||||
|
||||
<section class="workbench" aria-label="采购任务表格工作台">
|
||||
@@ -213,13 +213,13 @@
|
||||
<form class="filter-form" id="filter-form" role="search">
|
||||
<div class="filter-field"><label for="keyword">标题关键词</label><input id="keyword" type="search" placeholder="输入采购任务标题" autocomplete="off"></div>
|
||||
<div class="filter-actions">
|
||||
<details class="advanced-filters" id="advanced-filters"><summary>更多条件</summary><div class="filter-popover"><div><label for="status-filter">状态</label><select id="status-filter"><option value="all">全部状态</option><option value="DRAFT">待开始</option><option value="PENDING">待领取</option><option value="WAITING_CONFIRMATION">等待确认</option><option value="WAITING_PAYMENT">待付款</option><option value="RECONCILIATION_REQUIRED">结果待核查</option><option value="SUCCEEDED">已完成</option></select></div><div><label for="time-filter">创建时间</label><select id="time-filter"><option value="all">全部时间</option><option value="today">今天</option><option value="week">最近 7 天</option></select></div></div></details>
|
||||
<details class="advanced-filters" id="advanced-filters"><summary>更多条件</summary><div class="filter-popover"><div><label for="status-filter">状态</label><select id="status-filter"><option value="all">全部状态</option><option value="DRAFT">待开始</option><option value="PENDING">已授权待领取</option><option value="ORDERING">采购执行中</option><option value="NEEDS_MANUAL">待人工处理</option><option value="WAITING_PAYMENT">待付款</option><option value="RECONCILIATION_REQUIRED">结果待核查</option><option value="SUCCEEDED">已完成</option></select></div><div><label for="time-filter">创建时间</label><select id="time-filter"><option value="all">全部时间</option><option value="today">今天</option><option value="week">最近 7 天</option></select></div></div></details>
|
||||
<div class="toolbar-group"><button class="button button-primary" type="submit">筛选</button><button class="button button-secondary" id="clear-filter" type="button">清除</button></div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="selection-mode" id="selection-mode" hidden>
|
||||
<div class="selection-copy"><strong id="selection-count">已选择 0 条</strong><span>只会加入第一趟试选队列,不签发授权、不创建订单、不付款。</span></div>
|
||||
<div class="toolbar-group"><button class="button button-primary" id="start-trial" type="button">开始试选</button><button class="button button-secondary" id="clear-selection" type="button">清除选择</button></div>
|
||||
<div class="selection-copy"><strong id="selection-count">已选择 0 条</strong><span id="selection-cap">最高总额合计 ¥0.00</span><span>点击将授权采购工具逐条创建待付款订单;系统不会付款。</span></div>
|
||||
<div class="toolbar-group"><button class="button button-primary" id="start-purchase" type="button">开始采购(只创建待付款订单)</button><button class="button button-secondary" id="clear-selection" type="button">清除选择</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -268,7 +268,7 @@
|
||||
<section class="drawer-panel"><h3>采购要求</h3><dl class="detail-grid" id="drawer-requirements"></dl></section>
|
||||
<section class="drawer-panel"><h3>采购结果</h3><div id="drawer-result"></div></section>
|
||||
<section class="drawer-panel"><h3>当前状态与下一步</h3><div class="next-step" id="drawer-next-step"></div></section>
|
||||
<section class="drawer-panel"><h3>执行证据</h3><div class="evidence-placeholder"><div><strong>假数据证据占位</strong><br>真实可读字段必须等待 T-103 真机取证;不展示地址、手机号或付款信息。</div></div></section>
|
||||
<section class="drawer-panel"><h3>执行证据</h3><div class="evidence-placeholder"><div><strong>内部截图证据占位</strong><br>真实字段等待真机取证;完整 XML 与支付凭据不会进入采购服务。</div></div></section>
|
||||
</div>
|
||||
<footer class="drawer-actions"><button class="button button-secondary" id="drawer-close-secondary" type="button">关闭</button><a class="button button-primary" id="full-detail-link" href="web-task-detail.html">在完整页面打开</a></footer>
|
||||
</dialog>
|
||||
@@ -278,7 +278,7 @@
|
||||
'use strict';
|
||||
const tasks = [
|
||||
{ id:'T-20260803-023', title:'仓库雨衣补货', productUrl:'https://mobile.yangkeduo.invalid/goods.html?goods_id=900000023', color:'荧光黄', size:'XXL', maxPrice:'96.00', quantity:3, status:'DRAFT', createdAt:'2026-08-03T14:42:00+08:00', result:null },
|
||||
{ id:'T-20260803-022', title:'8 月仓库工作服补货', productUrl:'https://mobile.yangkeduo.invalid/goods.html?goods_id=900000022', color:'白色', size:'XL', maxPrice:'80.00', quantity:2, status:'WAITING_CONFIRMATION', createdAt:'2026-08-03T13:18:00+08:00', result:{ phase:'试选', color:'白色', size:'XL', unitPrice:'32.50', quantity:2 } },
|
||||
{ id:'T-20260803-022', title:'8 月仓库工作服补货', productUrl:'https://mobile.yangkeduo.invalid/goods.html?goods_id=900000022', color:'白色', size:'XL', maxPrice:'80.00', quantity:2, status:'ORDERING', createdAt:'2026-08-03T13:18:00+08:00', result:{ phase:'采购执行', color:'白色', size:'XL', unitPrice:'32.50', quantity:2 } },
|
||||
{ id:'T-20260803-021', title:'办公室收纳盒', productUrl:'https://mobile.yangkeduo.invalid/goods.html?goods_id=900000021', color:'透明', size:'L', maxPrice:'60.00', quantity:2, status:'RECONCILIATION_REQUIRED', createdAt:'2026-08-03T11:36:00+08:00', result:{ phase:'下单待核查', color:'透明', size:'L', unitPrice:'24.00', quantity:2 } },
|
||||
{ id:'T-20260803-020', title:'仓库防滑手套', productUrl:'https://mobile.yangkeduo.invalid/goods.html?goods_id=900000020', color:'黑色', size:'均码', maxPrice:'64.00', quantity:5, status:'PENDING', createdAt:'2026-08-03T10:45:00+08:00', result:null },
|
||||
{ id:'T-20260801-009', title:'标签打印纸补货', productUrl:'https://mobile.yangkeduo.invalid/goods.html?goods_id=900000009', color:'白色', size:'50×30', maxPrice:'50.00', quantity:1, status:'WAITING_PAYMENT', createdAt:'2026-08-01T16:12:00+08:00', result:{ phase:'已下单', color:'白色', size:'50×30', unitPrice:'46.00', quantity:1 } },
|
||||
@@ -286,9 +286,10 @@
|
||||
];
|
||||
|
||||
const statusMeta = {
|
||||
DRAFT:{ label:'待开始', tone:'neutral', next:'勾选后点击“开始试选”,任务才会进入设备领取队列。' },
|
||||
PENDING:{ label:'待领取', tone:'', next:'已进入第一趟试选队列,等待桌面端轮询领取。' },
|
||||
WAITING_CONFIRMATION:{ label:'等待确认', tone:'', next:'查看规格面板证据,确认机器选对后才可签发下单授权。' },
|
||||
DRAFT:{ label:'待开始', tone:'neutral', next:'勾选后点击“开始采购(只创建待付款订单)”,才会签发一次性授权。' },
|
||||
PENDING:{ label:'已授权待领取', tone:'', next:'管理员已授权创建待付款订单,等待采购工具领取;系统不会付款。' },
|
||||
ORDERING:{ label:'采购执行中', tone:'', next:'采购工具正在同一趟校验规格、价格和数量;无需中途确认。' },
|
||||
NEEDS_MANUAL:{ label:'待人工处理', tone:'warning', next:'围栏前执行已停止。核查原因后可重置为待开始或取消。' },
|
||||
RECONCILIATION_REQUIRED:{ label:'结果待核查', tone:'warning', next:'订单可能已创建。只核查同一提交记录,不得重新提交。' },
|
||||
WAITING_PAYMENT:{ label:'待付款', tone:'warning', next:'人在拼多多核对商品、规格、数量和金额后手工付款;系统不会付款。' },
|
||||
SUCCEEDED:{ label:'已完成', tone:'success', next:'已由人工付款并标记完成,可查看执行证据与审计记录。' }
|
||||
@@ -303,6 +304,7 @@
|
||||
const filterForm = document.querySelector('#filter-form');
|
||||
const selectionMode = document.querySelector('#selection-mode');
|
||||
const selectionCount = document.querySelector('#selection-count');
|
||||
const selectionCap = document.querySelector('#selection-cap');
|
||||
const selectAll = document.querySelector('#select-all');
|
||||
const states = { loading:document.querySelector('#loading-state'), empty:document.querySelector('#empty-state'), error:document.querySelector('#error-state') };
|
||||
const selectedIds = new Set();
|
||||
@@ -325,6 +327,12 @@
|
||||
return `${whole}.${fraction}`;
|
||||
}
|
||||
|
||||
function moneyToCents(value) {
|
||||
const normalized = normalizeMoney(value);
|
||||
const [whole, fraction] = normalized.split('.');
|
||||
return Number.parseInt(whole, 10) * 100 + Number.parseInt(fraction, 10);
|
||||
}
|
||||
|
||||
function formatDate(value) {
|
||||
return new Intl.DateTimeFormat('zh-CN',{ year:'numeric', month:'2-digit', day:'2-digit', hour:'2-digit', minute:'2-digit', hour12:false }).format(new Date(value));
|
||||
}
|
||||
@@ -375,6 +383,8 @@
|
||||
filterForm.hidden = count > 0;
|
||||
selectionMode.hidden = count === 0;
|
||||
selectionCount.textContent = `已选择 ${count} 条待开始任务`;
|
||||
const capInCents = tasks.filter((task) => selectedIds.has(task.id)).reduce((sum, task) => sum + moneyToCents(task.maxPrice), 0);
|
||||
selectionCap.textContent = `最高总额合计 ¥${(capInCents / 100).toFixed(2)}`;
|
||||
const selectableVisible = visibleTasks().filter((task) => task.status === 'DRAFT');
|
||||
const selectedVisible = selectableVisible.filter((task) => selectedIds.has(task.id));
|
||||
selectAll.checked = selectableVisible.length > 0 && selectedVisible.length === selectableVisible.length;
|
||||
@@ -438,12 +448,12 @@
|
||||
});
|
||||
|
||||
document.querySelector('#clear-selection').addEventListener('click', () => { selectedIds.clear(); renderRows(); keyword.focus(); });
|
||||
document.querySelector('#start-trial').addEventListener('click', () => {
|
||||
document.querySelector('#start-purchase').addEventListener('click', () => {
|
||||
const ids = [...selectedIds];
|
||||
tasks.forEach((task) => { if (selectedIds.has(task.id) && task.status === 'DRAFT') task.status = 'PENDING'; });
|
||||
selectedIds.clear();
|
||||
renderRows();
|
||||
showToast(`已将 ${ids.length} 条任务加入第一趟试选队列。没有签发下单授权,也不会付款。`);
|
||||
showToast(`已授权 ${ids.length} 条任务创建待付款订单。采购工具将逐条执行,系统不会付款。`);
|
||||
});
|
||||
|
||||
function isInteractive(target) { return Boolean(target.closest('a,button,input,label,select,textarea,summary')); }
|
||||
@@ -548,7 +558,7 @@
|
||||
drawerId.textContent = `${task.id} · ${meta.label}`;
|
||||
drawerTitle.textContent = task.title;
|
||||
drawerRequirements.innerHTML = `<div><dt>颜色</dt><dd>${escapeHtml(task.color)}</dd></div><div><dt>尺码</dt><dd>${escapeHtml(task.size)}</dd></div><div><dt>价格上限</dt><dd>¥${escapeHtml(task.maxPrice)}</dd></div><div><dt>数量</dt><dd>${task.quantity} 件</dd></div><div><dt>状态</dt><dd><span class="status ${meta.tone}">${escapeHtml(meta.label)}</span></dd></div><div><dt>创建时间</dt><dd>${escapeHtml(formatDate(task.createdAt))}</dd></div>`;
|
||||
drawerResult.innerHTML = task.result ? `<p style="margin:0 0 10px">${formatResult(task)}</p><p style="margin:0;color:var(--muted);font-size:.82rem">展示值来自结构化颜色、尺码、单价和数量;“|”只用于页面格式化。</p>` : '<p class="result empty" style="margin:0">尚无采购结果。任务未完成试选或下单,不用其他数据填充。</p>';
|
||||
drawerResult.innerHTML = task.result ? `<p style="margin:0 0 10px">${formatResult(task)}</p><p style="margin:0;color:var(--muted);font-size:.82rem">展示值来自结构化颜色、尺码、单价和数量;“|”只用于页面格式化。</p>` : '<p class="result empty" style="margin:0">尚无采购结果。任务未开始或仍在执行,不用其他数据填充。</p>';
|
||||
drawerNext.textContent = meta.next;
|
||||
fullDetailLink.href = `web-task-detail.html#${encodeURIComponent(task.id)}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user