2026-08-03 12:35:54 +08:00
|
|
|
|
<!doctype html>
|
|
|
|
|
|
<html lang="zh-CN">
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<meta charset="utf-8">
|
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
2026-08-03 12:44:59 +08:00
|
|
|
|
<link rel="icon" href="data:,">
|
2026-08-03 12:35:54 +08:00
|
|
|
|
<title>任务详情 · cmbuyer 原型</title>
|
|
|
|
|
|
<style>
|
|
|
|
|
|
:root {
|
|
|
|
|
|
--bg:#f4f7fb; --surface:#fff; --subtle:#f8fafc; --text:#172033; --muted:#526079; --border:#cfd8e6;
|
|
|
|
|
|
--primary:#155eef; --primary-hover:#0b4ed1; --primary-soft:#eaf1ff; --danger:#b42318; --danger-hover:#8f1d14;
|
|
|
|
|
|
--danger-soft:#fef3f2; --success:#067647; --success-soft:#ecfdf3; --warning:#8a4b0f; --warning-soft:#fff7e8;
|
|
|
|
|
|
--focus:#ffbf47; --shadow:0 6px 20px rgba(23,32,51,.08);
|
|
|
|
|
|
font-family:"Segoe UI","Microsoft YaHei UI",system-ui,sans-serif;
|
|
|
|
|
|
}
|
|
|
|
|
|
* { box-sizing:border-box; }
|
|
|
|
|
|
html { min-width:320px; background:var(--bg); }
|
|
|
|
|
|
body { margin:0; min-height:100dvh; color:var(--text); background:var(--bg); font-size:16px; line-height:1.5; }
|
|
|
|
|
|
a { color:var(--primary); text-underline-offset:3px; }
|
|
|
|
|
|
button,input,select,textarea { font:inherit; }
|
|
|
|
|
|
button,.button,input,select { min-height:44px; }
|
|
|
|
|
|
button { cursor:pointer; }
|
|
|
|
|
|
button:disabled { cursor:not-allowed; opacity:.56; }
|
|
|
|
|
|
:focus-visible { outline:3px solid var(--focus); outline-offset:3px; }
|
|
|
|
|
|
.skip-link { position:fixed; z-index:1000; top:8px; left:8px; padding:10px 14px; color:#fff; background:var(--text); transform:translateY(-160%); }
|
|
|
|
|
|
.skip-link:focus { transform:translateY(0); }
|
|
|
|
|
|
.prototype-banner { position:sticky; z-index:50; top:0; padding:7px 16px; color:#fff; background:#29364d; text-align:center; font-size:.78rem; font-weight:700; letter-spacing:.04em; }
|
|
|
|
|
|
.app-header { position:sticky; z-index:40; top:33px; min-height:64px; display:flex; align-items:center; gap:24px; padding:0 max(16px,calc((100vw - 1280px)/2)); border-bottom:1px solid var(--border); background:rgba(255,255,255,.98); }
|
|
|
|
|
|
.brand { display:flex; align-items:center; gap:10px; color:var(--text); text-decoration:none; font-weight:750; white-space:nowrap; }
|
|
|
|
|
|
.brand-mark { display:grid; place-items:center; width:32px; height:32px; border-radius:8px; color:#fff; background:var(--primary); font-size:.8rem; }
|
|
|
|
|
|
nav.primary { display:flex; align-items:stretch; align-self:stretch; gap:4px; }
|
|
|
|
|
|
nav.primary a { display:flex; align-items:center; padding:0 12px; color:#3d4b63; text-decoration:none; font-weight:650; border-bottom:3px solid transparent; }
|
|
|
|
|
|
nav.primary a[aria-current="page"] { color:var(--primary); border-bottom-color:var(--primary); }
|
|
|
|
|
|
.header-account { margin-left:auto; color:var(--muted); font-size:.88rem; white-space:nowrap; }
|
|
|
|
|
|
main { width:min(100% - 32px,1200px); margin:0 auto; padding:24px 0 64px; }
|
|
|
|
|
|
.breadcrumb { display:flex; flex-wrap:wrap; gap:8px; margin-bottom:11px; color:var(--muted); font-size:.86rem; }
|
|
|
|
|
|
.page-head { display:flex; align-items:start; justify-content:space-between; gap:20px; margin-bottom:16px; }
|
|
|
|
|
|
h1 { margin:0; font-size:clamp(1.5rem,3vw,1.95rem); line-height:1.25; }
|
|
|
|
|
|
.task-id { margin:5px 0 0; color:var(--muted); font-size:.87rem; font-variant-numeric:tabular-nums; }
|
|
|
|
|
|
.state-chip { display:inline-flex; align-items:center; gap:7px; padding:7px 10px; border:1px solid #b9cffc; border-radius:999px; color:#163c8c; background:var(--primary-soft); font-size:.82rem; font-weight:750; }
|
|
|
|
|
|
.state-chip::before { content:""; width:8px; height:8px; border-radius:50%; background:currentColor; }
|
|
|
|
|
|
.safety-bar { display:grid; grid-template-columns:auto 1fr; gap:10px; align-items:start; margin-bottom:16px; padding:13px 15px; border:1px solid #b9cffc; border-radius:10px; background:var(--primary-soft); color:#29466f; }
|
|
|
|
|
|
.safety-bar strong { color:#163c8c; }
|
|
|
|
|
|
.layout { display:grid; grid-template-columns:minmax(0,1fr) 300px; gap:18px; align-items:start; }
|
|
|
|
|
|
.panel { border:1px solid var(--border); border-radius:12px; background:var(--surface); box-shadow:var(--shadow); }
|
|
|
|
|
|
.main-panel { min-width:0; }
|
|
|
|
|
|
.panel-head { display:flex; align-items:start; justify-content:space-between; gap:12px; padding:18px 20px 14px; border-bottom:1px solid var(--border); }
|
|
|
|
|
|
.panel-head h2 { margin:0; font-size:1.18rem; }
|
|
|
|
|
|
.panel-head p { margin:5px 0 0; color:var(--muted); font-size:.88rem; }
|
|
|
|
|
|
.panel-body { padding:20px; }
|
|
|
|
|
|
.state-panel[hidden] { display:none; }
|
|
|
|
|
|
.notice { margin:0 0 16px; padding:12px 14px; border-left:4px solid var(--primary); border-radius:6px; color:#29466f; background:var(--primary-soft); }
|
|
|
|
|
|
.notice.warning { border-left-color:#b7751b; color:var(--warning); background:var(--warning-soft); }
|
|
|
|
|
|
.notice.danger { border-left-color:var(--danger); color:var(--danger); background:var(--danger-soft); }
|
|
|
|
|
|
.notice.success { border-left-color:var(--success); color:var(--success); background:var(--success-soft); }
|
|
|
|
|
|
.notice strong { display:block; margin-bottom:2px; }
|
|
|
|
|
|
.comparison { width:100%; border-collapse:collapse; margin-bottom:16px; table-layout:fixed; }
|
|
|
|
|
|
.comparison th,.comparison td { padding:10px 9px; border-bottom:1px solid #e4e9f0; text-align:left; vertical-align:top; overflow-wrap:anywhere; }
|
|
|
|
|
|
.comparison th { color:var(--muted); background:var(--subtle); font-size:.8rem; }
|
|
|
|
|
|
.comparison td { font-size:.9rem; }
|
|
|
|
|
|
.result-text { font-weight:750; }
|
|
|
|
|
|
.result-text.good { color:var(--success); }
|
|
|
|
|
|
.result-text.bad { color:var(--danger); }
|
|
|
|
|
|
.evidence { margin:16px 0; border:1px solid var(--border); border-radius:10px; overflow:hidden; background:#eef2f7; }
|
|
|
|
|
|
.evidence svg { display:block; width:100%; height:auto; min-height:180px; }
|
|
|
|
|
|
.evidence figcaption { padding:9px 12px; color:var(--muted); background:var(--surface); font-size:.8rem; }
|
|
|
|
|
|
.summary-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; margin:0 0 18px; }
|
|
|
|
|
|
.summary-item { min-width:0; padding:11px 12px; border:1px solid #e0e6ee; border-radius:8px; background:var(--subtle); }
|
|
|
|
|
|
.summary-item dt { color:var(--muted); font-size:.76rem; }
|
|
|
|
|
|
.summary-item dd { margin:2px 0 0; overflow-wrap:anywhere; font-weight:650; font-variant-numeric:tabular-nums; }
|
|
|
|
|
|
.actions { display:flex; flex-wrap:wrap; justify-content:flex-end; gap:10px; margin-top:18px; padding-top:16px; border-top:1px solid var(--border); }
|
|
|
|
|
|
.actions.between { justify-content:space-between; }
|
|
|
|
|
|
.button { display:inline-flex; align-items:center; justify-content:center; gap:8px; padding:9px 16px; border:1px solid transparent; border-radius:8px; text-decoration:none; font-weight:700; }
|
|
|
|
|
|
.button-primary { color:#fff; background:var(--primary); }
|
|
|
|
|
|
.button-primary:hover:not(:disabled) { background:var(--primary-hover); }
|
|
|
|
|
|
.button-secondary { color:var(--text); border-color:var(--border); background:var(--surface); }
|
|
|
|
|
|
.button-secondary:hover:not(:disabled) { background:var(--subtle); }
|
|
|
|
|
|
.button-danger { color:#fff; background:var(--danger); }
|
|
|
|
|
|
.button-danger:hover:not(:disabled) { background:var(--danger-hover); }
|
|
|
|
|
|
.button-danger-outline { color:var(--danger); border-color:#e0a29c; background:var(--surface); }
|
|
|
|
|
|
.button-danger-outline:hover:not(:disabled) { background:var(--danger-soft); }
|
|
|
|
|
|
.spinner { width:16px; height:16px; border:2px solid rgba(255,255,255,.45); border-top-color:#fff; border-radius:50%; animation:spin .8s linear infinite; }
|
|
|
|
|
|
.substate-control { display:flex; align-items:center; gap:10px; margin-bottom:14px; padding:10px 12px; border:1px dashed #aebacc; border-radius:8px; background:var(--subtle); }
|
|
|
|
|
|
.substate-control label { flex:0 0 auto; font-size:.82rem; font-weight:700; }
|
|
|
|
|
|
.substate-control select { width:100%; padding:8px 10px; border:1px solid #91a0b4; border-radius:7px; background:#fff; }
|
|
|
|
|
|
.side-panel { position:sticky; top:116px; }
|
|
|
|
|
|
.side-panel section { padding:15px; border-bottom:1px solid var(--border); }
|
|
|
|
|
|
.side-panel section:last-child { border-bottom:0; }
|
|
|
|
|
|
.side-panel h2 { margin:0 0 8px; font-size:.98rem; }
|
|
|
|
|
|
.side-panel p { margin:0; color:var(--muted); font-size:.84rem; }
|
|
|
|
|
|
.state-select { width:100%; margin-top:8px; padding:8px 10px; border:1px solid #91a0b4; border-radius:7px; background:#fff; }
|
|
|
|
|
|
.timeline { list-style:none; margin:10px 0 0; padding:0; }
|
|
|
|
|
|
.timeline li { position:relative; padding:0 0 13px 18px; color:#33415b; font-size:.82rem; }
|
|
|
|
|
|
.timeline li::before { content:""; position:absolute; left:0; top:.4em; width:8px; height:8px; border-radius:50%; background:#8ba0bd; }
|
|
|
|
|
|
.timeline li::after { content:""; position:absolute; left:3px; top:1em; bottom:1px; width:2px; background:#d8e0eb; }
|
|
|
|
|
|
.timeline li:last-child::after { display:none; }
|
|
|
|
|
|
.timeline strong { display:block; color:var(--text); }
|
|
|
|
|
|
.fence-lock { padding:14px; border:2px solid #e0bd75; border-radius:10px; background:var(--warning-soft); }
|
|
|
|
|
|
.fence-lock h3 { margin:0 0 5px; color:var(--warning); font-size:1rem; }
|
|
|
|
|
|
.fence-lock p { margin:4px 0; color:#674018; }
|
|
|
|
|
|
.mono { font-family:ui-monospace,"Cascadia Mono",Consolas,monospace; font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
|
|
|
|
|
|
.checklist { display:grid; gap:9px; margin:15px 0; }
|
|
|
|
|
|
.checklist label,.radio-list label { display:flex; gap:10px; align-items:flex-start; min-height:44px; padding:10px 12px; border:1px solid var(--border); border-radius:8px; background:var(--surface); }
|
|
|
|
|
|
.checklist input,.radio-list input { width:18px; height:18px; flex:0 0 auto; margin-top:2px; accent-color:var(--primary); }
|
|
|
|
|
|
.radio-list { display:grid; gap:8px; margin:12px 0; }
|
|
|
|
|
|
textarea { width:100%; min-height:84px; padding:10px; border:1px solid #91a0b4; border-radius:8px; resize:vertical; }
|
|
|
|
|
|
.field-label { display:block; margin:14px 0 6px; font-weight:700; }
|
|
|
|
|
|
.terminal-mark { display:grid; place-items:center; width:54px; height:54px; margin:0 auto 12px; border-radius:50%; color:var(--success); border:2px solid currentColor; font-weight:800; }
|
|
|
|
|
|
.terminal { text-align:center; }
|
|
|
|
|
|
.terminal p { max-width:600px; margin:0 auto 10px; color:var(--muted); }
|
|
|
|
|
|
.live-region { margin-bottom:14px; }
|
|
|
|
|
|
.live-region:empty { display:none; }
|
|
|
|
|
|
dialog { width:min(100% - 32px,500px); padding:0; border:0; border-radius:12px; color:var(--text); box-shadow:0 24px 80px rgba(0,0,0,.3); }
|
|
|
|
|
|
dialog::backdrop { background:rgba(15,23,42,.58); }
|
|
|
|
|
|
.dialog-head { padding:18px 20px 12px; border-bottom:1px solid var(--border); }
|
|
|
|
|
|
.dialog-head h2 { margin:0; font-size:1.15rem; }
|
|
|
|
|
|
.dialog-body { padding:18px 20px; }
|
|
|
|
|
|
.dialog-body p { margin:0 0 10px; }
|
|
|
|
|
|
.dialog-actions { display:flex; justify-content:flex-end; flex-wrap:wrap; gap:9px; padding:14px 20px 18px; }
|
|
|
|
|
|
@keyframes spin { to { transform:rotate(360deg); } }
|
|
|
|
|
|
@media (max-width:900px) { .layout{grid-template-columns:1fr}.side-panel{position:static;display:grid;grid-template-columns:1fr 1fr}.side-panel section{border-bottom:0}.side-panel section+section{border-left:1px solid var(--border)} }
|
|
|
|
|
|
@media (max-width:767px) {
|
|
|
|
|
|
.app-header{top:33px;padding-inline:16px;gap:10px}.header-account{display:none}nav.primary a{padding-inline:8px}main{width:min(100% - 24px,1200px);padding-top:18px}.page-head{flex-direction:column;gap:10px}.panel-head,.panel-body{padding-inline:15px}.summary-grid{grid-template-columns:1fr}.actions,.actions.between{flex-direction:column-reverse}.actions .button{width:100%}.substate-control{align-items:stretch;flex-direction:column}.side-panel{grid-template-columns:1fr}.side-panel section+section{border-left:0;border-top:1px solid var(--border)}
|
|
|
|
|
|
.comparison,.comparison tbody,.comparison tr,.comparison td{display:block;width:100%}.comparison thead{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.comparison tr{padding:8px 0;border-bottom:1px solid #e4e9f0}.comparison td{display:grid;grid-template-columns:110px 1fr;gap:8px;padding:5px 0;border:0}.comparison td::before{content:attr(data-label);color:var(--muted);font-size:.78rem;font-weight:700}
|
|
|
|
|
|
}
|
|
|
|
|
|
@media (max-width:430px) { .prototype-banner{position:static;padding-inline:8px}.app-header{top:0}.brand span:last-child,nav.primary a:last-child{display:none}.safety-bar{grid-template-columns:1fr}.comparison td{grid-template-columns:92px 1fr} }
|
|
|
|
|
|
@media (prefers-reduced-motion:reduce) { *,*::before,*::after{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important} }
|
|
|
|
|
|
</style>
|
|
|
|
|
|
</head>
|
|
|
|
|
|
<body>
|
|
|
|
|
|
<a class="skip-link" href="#main">跳到主要内容</a>
|
|
|
|
|
|
<div class="prototype-banner">PROTOTYPE - 仅供枚举交互,非实现依据</div>
|
|
|
|
|
|
<header class="app-header">
|
|
|
|
|
|
<a class="brand" href="web-task-workbench.html"><span class="brand-mark" aria-hidden="true">CM</span><span>cmbuyer</span></a>
|
|
|
|
|
|
<nav class="primary" aria-label="主导航"><a href="web-task-workbench.html" aria-current="page">任务工作台</a><a href="web-task-create.html">新建任务</a></nav>
|
|
|
|
|
|
<span class="header-account">采购管理员 · caigou_admin</span>
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<main id="main">
|
|
|
|
|
|
<nav class="breadcrumb" aria-label="面包屑"><a href="web-task-workbench.html">任务工作台</a><span aria-hidden="true">/</span><span>任务详情</span></nav>
|
|
|
|
|
|
<header class="page-head">
|
|
|
|
|
|
<div><h1>8 月仓库工作服补货</h1><p class="task-id">任务 T-20260803-018 · 假数据</p></div>
|
|
|
|
|
|
<span class="state-chip" id="state-chip">等待确认</span>
|
|
|
|
|
|
</header>
|
|
|
|
|
|
<div class="safety-bar"><strong>只下单,不付款</strong><span>系统最多创建待付款订单。付款始终由人在拼多多核对后完成;待付款状态不是成功。</span></div>
|
|
|
|
|
|
<div class="layout">
|
|
|
|
|
|
<section class="panel main-panel" aria-label="当前任务状态">
|
|
|
|
|
|
<div class="live-region" id="live-region" aria-live="polite"></div>
|
|
|
|
|
|
|
|
|
|
|
|
<article class="state-panel" data-state-panel="waiting">
|
|
|
|
|
|
<header class="panel-head"><div><h2>机器选对了吗?</h2><p>确认前必须查看实际勾选结果、价格和规格面板证据。</p></div></header>
|
|
|
|
|
|
<div class="panel-body">
|
|
|
|
|
|
<div class="substate-control"><label for="trial-variant">原型子状态</label><select id="trial-variant"><option value="match">规格一致、金额未超</option><option value="mismatch">规格不一致</option><option value="over">金额超上限</option><option value="missing">截图缺失</option></select></div>
|
|
|
|
|
|
<div class="notice" id="trial-notice"><strong>可以授权</strong>机器所选规格与要求一致,合计未超过资金上限。</div>
|
|
|
|
|
|
<table class="comparison">
|
|
|
|
|
|
<thead><tr><th scope="col">核对项</th><th scope="col">你要的</th><th scope="col">机器选到</th><th scope="col">结论</th></tr></thead>
|
|
|
|
|
|
<tbody>
|
|
|
|
|
|
<tr><td data-label="核对项">颜色分类</td><td data-label="你要的">白色</td><td data-label="机器选到" id="actual-color">白色</td><td data-label="结论"><span class="result-text good" id="color-result">符合</span></td></tr>
|
|
|
|
|
|
<tr><td data-label="核对项">尺码</td><td data-label="你要的">XL</td><td data-label="机器选到">XL</td><td data-label="结论"><span class="result-text good">符合</span></td></tr>
|
|
|
|
|
|
<tr><td data-label="核对项">金额</td><td data-label="你要的">总额不超过 ¥80.00</td><td data-label="机器选到" id="actual-price">¥32.50 × 2 = ¥65.00</td><td data-label="结论"><span class="result-text good" id="price-result">未超上限</span></td></tr>
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
<figure class="evidence" id="trial-evidence">
|
|
|
|
|
|
<svg viewBox="0 0 760 300" role="img" aria-labelledby="trial-svg-title trial-svg-desc">
|
|
|
|
|
|
<title id="trial-svg-title">规格面板截图假数据占位</title><desc id="trial-svg-desc">用于确认信息架构,不代表真实拼多多页面结构或字段位置。</desc>
|
|
|
|
|
|
<rect width="760" height="300" fill="#e7edf5"/><rect x="30" y="26" width="700" height="248" rx="12" fill="#fff" stroke="#b8c4d5"/>
|
|
|
|
|
|
<text x="55" y="62" fill="#526079" font-size="18">假数据证据占位 · 不代表真实 App 页面</text><line x1="55" y1="78" x2="705" y2="78" stroke="#d6dde8"/>
|
|
|
|
|
|
<rect x="55" y="102" width="190" height="42" rx="7" fill="#eaf1ff" stroke="#8aacf2"/><text x="72" y="129" fill="#163c8c" font-size="16">颜色分类:白色(假)</text>
|
|
|
|
|
|
<rect x="265" y="102" width="150" height="42" rx="7" fill="#eaf1ff" stroke="#8aacf2"/><text x="282" y="129" fill="#163c8c" font-size="16">尺码:XL(假)</text>
|
|
|
|
|
|
<rect x="55" y="166" width="360" height="58" rx="7" fill="#f8fafc" stroke="#cfd8e6"/><text x="72" y="201" fill="#172033" font-size="22">规格面板读价:¥32.50(假)</text>
|
|
|
|
|
|
<text x="55" y="252" fill="#526079" font-size="15">真实可读字段需等待 T-103 真机取证后复核</text>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
<figcaption>规格面板证据 · 假数据 · 未包含地址、手机号或付款信息</figcaption>
|
|
|
|
|
|
</figure>
|
|
|
|
|
|
<label class="field-label" for="auth-note">授权备注(可选)</label><textarea id="auth-note" maxlength="500" placeholder="只记录本次决定所需的非敏感信息"></textarea>
|
|
|
|
|
|
<div class="actions between"><button class="button button-danger-outline" id="reject-button" type="button">退回,不买</button><button class="button button-primary" id="authorize-button" type="button">确认下单(不付款)</button></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</article>
|
|
|
|
|
|
|
|
|
|
|
|
<article class="state-panel" data-state-panel="authorized" hidden>
|
|
|
|
|
|
<header class="panel-head"><div><h2>授权已签发,等待第二趟</h2><p>尚未建立提交围栏;旧授权仍可安全放弃,但必须重新试选取价。</p></div></header>
|
|
|
|
|
|
<div class="panel-body">
|
|
|
|
|
|
<div class="notice"><strong>系统正在等待桌面端领取</strong>当前没有创建订单,也没有发生付款。</div>
|
|
|
|
|
|
<dl class="summary-grid"><div class="summary-item"><dt>锁定单价</dt><dd>¥32.50</dd></div><div class="summary-item"><dt>授权总额上限</dt><dd>¥80.00</dd></div><div class="summary-item"><dt>授权编号</dt><dd class="mono">AUTH-018-V5</dd></div><div class="summary-item"><dt>围栏状态</dt><dd>尚未建立</dd></div></dl>
|
|
|
|
|
|
<div class="notice warning"><strong>放弃不会复用旧价</strong>任务将转为待重新试选,完成新试选后才能再次确认。</div>
|
|
|
|
|
|
<div class="actions"><button class="button button-danger" type="button" data-open-abandon>放弃授权并重新试选</button></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</article>
|
|
|
|
|
|
|
|
|
|
|
|
<article class="state-panel" data-state-panel="ordering-pre" hidden>
|
|
|
|
|
|
<header class="panel-head"><div><h2>第二趟执行中 · 围栏前</h2><p>桌面端正在重新选同一规格并复核价格,尚未取得提交许可。</p></div></header>
|
|
|
|
|
|
<div class="panel-body">
|
|
|
|
|
|
<ol class="timeline"><li><strong>已重新打开商品</strong>页面细节不在本原型定义</li><li><strong>正在重新选择规格</strong>必须按维度精确匹配</li><li><strong>待价格闸门复核</strong>现价必须等于锁定价 ¥32.50</li><li><strong>尚未申请提交围栏</strong>不会点击提交订单</li></ol>
|
|
|
|
|
|
<div class="notice warning"><strong>围栏前仍可安全放弃</strong>放弃后不会沿用旧试选价格,任务回到待重新试选。</div>
|
|
|
|
|
|
<div class="actions"><button class="button button-danger" type="button" data-open-abandon>放弃授权并重新试选</button></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</article>
|
|
|
|
|
|
|
|
|
|
|
|
<article class="state-panel" data-state-panel="retrial" hidden>
|
|
|
|
|
|
<header class="panel-head"><div><h2>等待重新试选</h2><p>旧授权已作废,旧价格不再有效。</p></div></header>
|
|
|
|
|
|
<div class="panel-body terminal"><div class="terminal-mark" aria-hidden="true">新</div><div class="notice"><strong>下一步安全可执行</strong>桌面端会重新跑第一趟,读取新价格并回传新证据;在此之前页面不会显示确认入口。</div><p>当前没有有效授权,没有建立提交围栏,也不会创建订单。</p><div class="actions"><a class="button button-primary" href="web-task-workbench.html">返回任务工作台</a></div></div>
|
|
|
|
|
|
</article>
|
|
|
|
|
|
|
|
|
|
|
|
<article class="state-panel" data-state-panel="fenced" hidden>
|
|
|
|
|
|
<header class="panel-head"><div><h2>提交围栏已建立</h2><p>只允许恢复和核查这一笔提交记录。</p></div></header>
|
|
|
|
|
|
<div class="panel-body">
|
|
|
|
|
|
<div class="fence-lock"><h3>禁止再次提交或放弃</h3><p>唯一提交记录:<strong class="mono">SUB-018-5A7C</strong></p><p>围栏建立后即使连接中断,也不能释放授权、重新领取或再点一次。</p></div>
|
|
|
|
|
|
<dl class="summary-grid" style="margin-top:16px"><div class="summary-item"><dt>提交记录</dt><dd class="mono">SUB-018-5A7C</dd></div><div class="summary-item"><dt>授权状态</dt><dd>已围栏</dd></div><div class="summary-item"><dt>确认页金额</dt><dd>¥65.00</dd></div><div class="summary-item"><dt>付款状态</dt><dd>未付款</dd></div></dl>
|
|
|
|
|
|
<div class="notice warning"><strong>这里没有重试或放弃入口</strong>如果结果不明,只能核查同一提交编号,避免产生第二笔订单。</div>
|
|
|
|
|
|
<div class="actions"><button class="button button-primary" type="button" data-go-reconcile>进入人工核查</button></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</article>
|
|
|
|
|
|
|
|
|
|
|
|
<article class="state-panel" data-state-panel="reconcile" hidden>
|
|
|
|
|
|
<header class="panel-head"><div><h2>订单结果需要人工核查</h2><p>订单可能已创建,请勿再次提交;授权额度继续预留。</p></div></header>
|
|
|
|
|
|
<div class="panel-body">
|
|
|
|
|
|
<div class="notice warning"><strong>结果不明,不代表失败</strong>提交记录 <span class="mono">SUB-018-5A7C</span> 已被永久围栏。当前只记录核查结果,不发起新的下单。</div>
|
|
|
|
|
|
<dl class="summary-grid"><div class="summary-item"><dt>观察结果</dt><dd>页面超时,无法确认</dd></div><div class="summary-item"><dt>付款状态</dt><dd>未知;系统未付款</dd></div><div class="summary-item"><dt>证据</dt><dd>1 张脱敏截图(假)</dd></div><div class="summary-item"><dt>可用动作</dt><dd>仅人工核查</dd></div></dl>
|
|
|
|
|
|
<fieldset style="border:0;padding:0;margin:0"><legend style="font-weight:750">人工核查结论</legend><div class="radio-list"><label><input type="radio" name="review" value="created">已确认创建了待付款订单</label><label><input type="radio" name="review" value="not-created">已确认没有创建订单</label><label><input type="radio" name="review" value="unknown" checked>仍不明确,继续保留围栏</label></div></fieldset>
|
|
|
|
|
|
<label class="field-label" for="review-note">核查说明(必填,勿写敏感信息)</label><textarea id="review-note" placeholder="例如:已由人工查看拼多多待付款列表;本原型不访问真实页面">仍需人工查看待付款列表(假数据)</textarea>
|
|
|
|
|
|
<div class="actions"><button class="button button-primary" id="save-review" type="button">保存核查记录</button></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</article>
|
|
|
|
|
|
|
|
|
|
|
|
<article class="state-panel" data-state-panel="payment" hidden>
|
|
|
|
|
|
<header class="panel-head"><div><h2>待付款核对</h2><p>订单已创建,但任务尚未成功。请先在拼多多逐项核对。</p></div></header>
|
|
|
|
|
|
<div class="panel-body">
|
|
|
|
|
|
<div class="notice warning"><strong>对不上就别付</strong>不一致时回到这里标记异常,不要在拼多多改单。系统不会代付。</div>
|
|
|
|
|
|
<figure class="evidence"><svg viewBox="0 0 760 260" role="img" aria-labelledby="order-svg-title order-svg-desc"><title id="order-svg-title">订单截图假数据占位</title><desc id="order-svg-desc">仅用于原型核对流程,不表示真实拼多多订单页布局。</desc><rect width="760" height="260" fill="#e7edf5"/><rect x="30" y="25" width="700" height="210" rx="12" fill="#fff" stroke="#b8c4d5"/><text x="54" y="60" fill="#526079" font-size="18">待付款订单证据 · 假数据</text><line x1="54" y1="76" x2="706" y2="76" stroke="#d6dde8"/><text x="54" y="112" fill="#172033" font-size="18">商品:2026 夏季纯棉圆领短袖(假)</text><text x="54" y="150" fill="#172033" font-size="18">规格:白色 · XL 数量:2</text><text x="54" y="194" fill="#172033" font-size="22">授权金额:¥65.00 状态:待付款</text></svg><figcaption>订单证据 · 假数据 · 真实字段需 T-103 后复核</figcaption></figure>
|
|
|
|
|
|
<div class="checklist"><label><input type="checkbox" data-payment-check>拼多多中商品与这里展示的一致</label><label><input type="checkbox" data-payment-check>规格、数量与这里展示的一致</label><label><input type="checkbox" data-payment-check>付款金额不超过授权金额 ¥80.00</label><label><input type="checkbox" data-payment-check>我已由人工在拼多多完成付款</label></div>
|
|
|
|
|
|
<div class="actions between"><button class="button button-danger-outline" id="mark-abnormal" type="button">标记异常,先不付款</button><button class="button button-primary" id="mark-paid" type="button" disabled aria-describedby="paid-disabled-reason">确认已付款并完成</button></div>
|
|
|
|
|
|
<p id="paid-disabled-reason" style="margin:8px 0 0;color:var(--muted);font-size:.82rem">完成全部四项人工核对后才能标记。</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</article>
|
|
|
|
|
|
|
|
|
|
|
|
<article class="state-panel" data-state-panel="manual" hidden>
|
|
|
|
|
|
<header class="panel-head"><div><h2>需要人工处理</h2><p>这是围栏前异常;系统已经停止,不会猜测或继续执行。</p></div></header>
|
|
|
|
|
|
<div class="panel-body"><div class="notice danger"><strong>价格与授权不一致</strong>授权单价 ¥32.50,第二趟读到 ¥35.00。系统没有申请提交围栏,也没有点击提交订单。</div><dl class="summary-grid"><div class="summary-item"><dt>失败阶段</dt><dd>第二趟 · 价格闸门二</dd></div><div class="summary-item"><dt>安全处置</dt><dd>停止并保留证据</dd></div><div class="summary-item"><dt>可做什么</dt><dd>核查原因,决定重新试选</dd></div><div class="summary-item"><dt>不可做什么</dt><dd>按新价继续下单</dd></div></dl><label class="field-label" for="manual-note">处理记录</label><textarea id="manual-note" placeholder="记录下一步,不填写敏感信息"></textarea><div class="actions"><button class="button button-primary" id="record-manual" type="button">记录人工处理结果</button></div></div>
|
|
|
|
|
|
</article>
|
|
|
|
|
|
|
|
|
|
|
|
<article class="state-panel" data-state-panel="succeeded" hidden><header class="panel-head"><div><h2>任务已完成</h2><p>人工付款并核对后才进入此状态。</p></div></header><div class="panel-body terminal"><div class="terminal-mark" aria-hidden="true">完成</div><p><strong>已由采购管理员于 2026-08-03 11:20 标记完成。</strong></p><p>付款由人在拼多多完成,系统未读取或保存任何付款凭据。</p><div class="actions"><a class="button button-primary" href="web-task-workbench.html">返回任务工作台</a></div></div></article>
|
2026-08-03 12:44:59 +08:00
|
|
|
|
<article class="state-panel" data-state-panel="failed" hidden><header class="panel-head"><div><h2>任务失败</h2><p>失败原因可区分,并保留当时证据。</p></div></header><div class="panel-body"><div class="notice danger"><strong>设备连接中断</strong>发生在第一趟打开商品前,没有进行规格选择或下单动作。</div><dl class="summary-grid"><div class="summary-item"><dt>原因码</dt><dd class="mono">DEVICE_DISCONNECTED</dd></div><div class="summary-item"><dt>下一步</dt><dd>检查设备连接后,由人工决定重新试选或结束任务</dd></div></dl><div class="actions"><a class="button button-primary" href="web-task-workbench.html">返回工作台</a></div></div></article>
|
2026-08-03 12:35:54 +08:00
|
|
|
|
<article class="state-panel" data-state-panel="canceled" hidden><header class="panel-head"><div><h2>任务已取消</h2><p>管理员选择“退回,不买”,不会再自动执行。</p></div></header><div class="panel-body terminal"><div class="terminal-mark" style="color:var(--danger)" aria-hidden="true">停</div><p>没有签发下单授权,没有创建订单,也没有发生付款。</p><div class="actions"><a class="button button-primary" href="web-task-workbench.html">返回任务工作台</a></div></div></article>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<aside class="panel side-panel" aria-label="原型状态与任务摘要">
|
|
|
|
|
|
<section><h2>原型状态切换</h2><p>演示任务在不同业务状态下的唯一主区块,不发送请求。</p><label for="state-select" style="display:block;margin-top:10px;font-weight:700;font-size:.82rem">业务状态</label><select class="state-select" id="state-select"><option value="waiting">WAITING_CONFIRMATION</option><option value="authorized">AUTHORIZED</option><option value="ordering-pre">ORDERING · 围栏前</option><option value="retrial">PENDING_RETRIAL</option><option value="fenced">ORDERING · 已围栏</option><option value="reconcile">RECONCILIATION_REQUIRED</option><option value="payment">WAITING_PAYMENT</option><option value="manual">NEEDS_MANUAL</option><option value="succeeded">SUCCEEDED</option><option value="failed">FAILED</option><option value="canceled">CANCELED</option></select></section>
|
|
|
|
|
|
<section><h2>任务要求</h2><dl class="summary-grid" style="grid-template-columns:1fr;margin:0"><div class="summary-item"><dt>颜色分类 / 尺码</dt><dd>白色 / XL</dd></div><div class="summary-item"><dt>数量</dt><dd>2 件</dd></div><div class="summary-item"><dt>价格上限</dt><dd>¥80.00</dd></div></dl></section>
|
|
|
|
|
|
<section><h2>固定安全边界</h2><p>金额均为十进制字符串假数据。原型不定义拼多多页面判据,不调用接口,不读取地址、手机号或付款凭据。</p></section>
|
|
|
|
|
|
</aside>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
|
|
|
<dialog id="reject-dialog" aria-labelledby="reject-title"><div class="dialog-head"><h2 id="reject-title">确认退回,不买?</h2></div><div class="dialog-body"><p>任务将结束为已取消,不会签发下单授权,也不会再自动执行。</p></div><div class="dialog-actions"><button class="button button-secondary" type="button" data-close-dialog>继续检查</button><button class="button button-danger" type="button" id="confirm-reject">确认退回</button></div></dialog>
|
|
|
|
|
|
<dialog id="abandon-dialog" aria-labelledby="abandon-title"><div class="dialog-head"><h2 id="abandon-title">放弃当前授权?</h2></div><div class="dialog-body"><p>当前确认尚未建立提交围栏,可以安全放弃。</p><p><strong>放弃后必须重新跑第一趟取新价,不能复用旧授权或旧截图。</strong></p></div><div class="dialog-actions"><button class="button button-secondary" type="button" data-close-dialog>保留授权</button><button class="button button-danger" type="button" id="confirm-abandon">放弃并重新试选</button></div></dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
(() => {
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
const select = document.querySelector('#state-select');
|
|
|
|
|
|
const chip = document.querySelector('#state-chip');
|
|
|
|
|
|
const live = document.querySelector('#live-region');
|
|
|
|
|
|
const labels = { waiting:'等待确认', authorized:'已授权 · 围栏前', 'ordering-pre':'第二趟 · 围栏前', retrial:'等待重新试选', fenced:'提交围栏已建立', reconcile:'结果需要调和', payment:'待付款', manual:'需要人工', succeeded:'已完成', failed:'失败', canceled:'已取消' };
|
|
|
|
|
|
const panels = [...document.querySelectorAll('[data-state-panel]')];
|
|
|
|
|
|
const rejectDialog = document.querySelector('#reject-dialog');
|
|
|
|
|
|
const abandonDialog = document.querySelector('#abandon-dialog');
|
|
|
|
|
|
|
|
|
|
|
|
function showState(state, message = '', shouldScroll = true) {
|
|
|
|
|
|
panels.forEach((panel) => { panel.hidden = panel.dataset.statePanel !== state; });
|
|
|
|
|
|
select.value = state;
|
|
|
|
|
|
chip.textContent = labels[state];
|
|
|
|
|
|
live.className = message ? 'notice success live-region' : 'live-region';
|
|
|
|
|
|
live.textContent = message;
|
|
|
|
|
|
if (shouldScroll) {
|
|
|
|
|
|
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
|
|
|
document.querySelector('.main-panel').scrollIntoView({ block:'start', behavior:reduceMotion ? 'auto' : 'smooth' });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
select.addEventListener('change', () => showState(select.value));
|
|
|
|
|
|
|
|
|
|
|
|
const variant = document.querySelector('#trial-variant');
|
|
|
|
|
|
const actualColor = document.querySelector('#actual-color');
|
|
|
|
|
|
const colorResult = document.querySelector('#color-result');
|
|
|
|
|
|
const actualPrice = document.querySelector('#actual-price');
|
|
|
|
|
|
const priceResult = document.querySelector('#price-result');
|
|
|
|
|
|
const trialNotice = document.querySelector('#trial-notice');
|
|
|
|
|
|
const evidence = document.querySelector('#trial-evidence');
|
|
|
|
|
|
const authorizeButton = document.querySelector('#authorize-button');
|
|
|
|
|
|
|
|
|
|
|
|
function setTrialVariant(value) {
|
|
|
|
|
|
actualColor.textContent = value === 'mismatch' ? '粉白色' : '白色';
|
|
|
|
|
|
colorResult.textContent = value === 'mismatch' ? '不符合:颜色不同' : '符合';
|
|
|
|
|
|
colorResult.className = `result-text ${value === 'mismatch' ? 'bad' : 'good'}`;
|
|
|
|
|
|
actualPrice.textContent = value === 'over' ? '¥43.50 × 2 = ¥87.00' : '¥32.50 × 2 = ¥65.00';
|
|
|
|
|
|
priceResult.textContent = value === 'over' ? '超出 ¥7.00' : '未超上限';
|
|
|
|
|
|
priceResult.className = `result-text ${value === 'over' ? 'bad' : 'good'}`;
|
|
|
|
|
|
evidence.hidden = value === 'missing';
|
|
|
|
|
|
authorizeButton.disabled = value !== 'match';
|
|
|
|
|
|
if (value === 'match') { trialNotice.className='notice'; trialNotice.innerHTML='<strong>可以授权</strong>机器所选规格与要求一致,合计未超过资金上限。'; }
|
|
|
|
|
|
else if (value === 'mismatch') { trialNotice.className='notice danger'; trialNotice.innerHTML='<strong>不能授权:规格不一致</strong>颜色分类不相同,只能退回或转人工;不提供“仍然确认”。'; }
|
|
|
|
|
|
else if (value === 'over') { trialNotice.className='notice danger'; trialNotice.innerHTML='<strong>不能授权:金额超上限</strong>合计超出 ¥7.00,不提供绕过资金边界的入口。'; }
|
|
|
|
|
|
else { trialNotice.className='notice danger'; trialNotice.innerHTML='<strong>不能授权:证据缺失</strong>未加载出规格面板截图,无证据不能签发授权。'; }
|
|
|
|
|
|
}
|
|
|
|
|
|
variant.addEventListener('change', () => setTrialVariant(variant.value));
|
|
|
|
|
|
|
|
|
|
|
|
authorizeButton.addEventListener('click', () => {
|
|
|
|
|
|
authorizeButton.disabled = true;
|
|
|
|
|
|
authorizeButton.innerHTML = '<span class="spinner" aria-hidden="true"></span>正在签发授权…';
|
|
|
|
|
|
window.setTimeout(() => { authorizeButton.innerHTML='确认下单(不付款)'; authorizeButton.disabled=false; showState('authorized','授权已签发并锁定单价 ¥32.50。这里只允许创建待付款订单,系统不会付款。'); }, 500);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
document.querySelector('#reject-button').addEventListener('click', () => rejectDialog.showModal());
|
|
|
|
|
|
document.querySelectorAll('[data-open-abandon]').forEach((button) => button.addEventListener('click', () => abandonDialog.showModal()));
|
|
|
|
|
|
document.querySelectorAll('[data-close-dialog]').forEach((button) => button.addEventListener('click', () => button.closest('dialog').close()));
|
|
|
|
|
|
document.querySelector('#confirm-reject').addEventListener('click', () => { rejectDialog.close(); showState('canceled','任务已退回并取消,不会再自动执行。'); });
|
|
|
|
|
|
document.querySelector('#confirm-abandon').addEventListener('click', () => { abandonDialog.close(); showState('retrial','旧授权已作废。任务必须先重新试选取新价,不能复用旧价格。'); });
|
|
|
|
|
|
document.querySelectorAll('[data-go-reconcile]').forEach((button) => button.addEventListener('click', () => showState('reconcile')));
|
|
|
|
|
|
document.querySelector('#save-review').addEventListener('click', () => { live.className='notice success live-region'; live.textContent='人工核查记录已保存为假数据。无论选择何种结论,本原型都不会发起新的下单。'; live.scrollIntoView({block:'nearest'}); });
|
|
|
|
|
|
|
|
|
|
|
|
const paymentChecks = [...document.querySelectorAll('[data-payment-check]')];
|
|
|
|
|
|
const markPaid = document.querySelector('#mark-paid');
|
|
|
|
|
|
function updatePaidButton() { markPaid.disabled = !paymentChecks.every((check) => check.checked); }
|
|
|
|
|
|
paymentChecks.forEach((check) => check.addEventListener('change', updatePaidButton));
|
|
|
|
|
|
markPaid.addEventListener('click', () => showState('succeeded','已记录人工付款与核对结果。系统没有执行付款。'));
|
|
|
|
|
|
document.querySelector('#mark-abnormal').addEventListener('click', () => showState('manual','已标记核对异常,任务不会被直接标记完成。'));
|
|
|
|
|
|
document.querySelector('#record-manual').addEventListener('click', () => { live.className='notice success live-region'; live.textContent='人工处理记录已保存为假数据。后续如需重来,仍必须重新试选取价。'; live.scrollIntoView({block:'nearest'}); });
|
|
|
|
|
|
|
|
|
|
|
|
[rejectDialog, abandonDialog].forEach((dialog) => dialog.addEventListener('click', (event) => { if (event.target === dialog) dialog.close(); }));
|
|
|
|
|
|
setTrialVariant('match');
|
|
|
|
|
|
showState('waiting', '', false);
|
|
|
|
|
|
})();
|
|
|
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
</html>
|