342 lines
16 KiB
HTML
342 lines
16 KiB
HTML
{{define "syb/list"}}
|
||
{{template "header" .}}
|
||
|
||
<div class="toolbar">
|
||
<form class="inline sync-range-form" method="post" action="/syb/sync" data-syb-sync-form>
|
||
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
||
<input type="hidden" name="order_no" value="{{.Keyword}}">
|
||
<input type="hidden" name="shop" value="{{.ShopFilter}}">
|
||
<input type="hidden" name="stage" value="{{.StageFilter}}">
|
||
<input type="hidden" name="page" value="{{.CurrentPage}}">
|
||
<label for="sync-date-from">开始日期</label>
|
||
<input id="sync-date-from" type="date" name="date_from"
|
||
value="{{.SyncDateFrom}}" max="{{.Today}}" required>
|
||
<label for="sync-date-to">结束日期</label>
|
||
<input id="sync-date-to" type="date" name="date_to"
|
||
value="{{.SyncDateTo}}" max="{{.Today}}" required>
|
||
<button type="submit" class="primary" data-syb-sync-submit
|
||
{{if .SyncRunning}}disabled aria-busy="true" title="同步正在进行,请耐心等待"{{end}}>
|
||
{{if .SyncRunning}}同步中…{{else}}同步{{end}}
|
||
</button>
|
||
</form>
|
||
|
||
<button type="button" data-modal-open="sync-history-modal">同步记录</button>
|
||
|
||
<form id="syb-collect-form" method="post" action="/syb/collect-pdd-batch" hidden></form>
|
||
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}" form="syb-collect-form">
|
||
<input type="hidden" name="order_no" value="{{.Keyword}}" form="syb-collect-form">
|
||
<input type="hidden" name="shop" value="{{.ShopFilter}}" form="syb-collect-form">
|
||
<input type="hidden" name="stage" value="{{.StageFilter}}" form="syb-collect-form">
|
||
<input type="hidden" name="page" value="{{.CurrentPage}}" form="syb-collect-form">
|
||
<button type="button" data-modal-open="syb-collect-modal" data-collect-open
|
||
data-collect-form="syb-collect-form" data-collect-action="collect"
|
||
data-need-checked="collect">创建 PDD 采集任务</button>
|
||
|
||
<button type="button" data-need-checked="purchase" data-modal-open="purchase-modal"
|
||
data-purchase-open>创建采购任务</button>
|
||
|
||
<form class="inline grow" method="get" action="/syb">
|
||
<label for="stage">处理阶段</label>
|
||
<select id="stage" name="stage">
|
||
{{range .StageOptions}}
|
||
<option value="{{.Value}}" {{if eq .Value $.StageFilter}}selected{{end}}>{{.Text}}</option>
|
||
{{end}}
|
||
</select>
|
||
<label for="shop">店铺</label>
|
||
<input id="shop" type="text" name="shop" value="{{.ShopFilter}}"
|
||
class="search-narrow" placeholder="店铺名">
|
||
<label for="q">订单号</label>
|
||
{{/* search-narrow 把搜索框收到 30%,让「搜索」按钮紧挨着它。
|
||
用类名而不是改 .toolbar input[type="text"]——那条被五个页面共用。 */}}
|
||
<input id="q" type="text" name="order_no" value="{{.Keyword}}"
|
||
class="search-narrow" placeholder="订单号">
|
||
<button type="submit">搜索</button>
|
||
</form>
|
||
|
||
<form class="inline" method="post" action="/syb/delete" data-confirm-delete>
|
||
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
||
<input type="hidden" name="order_no" value="{{.Keyword}}">
|
||
<input type="hidden" name="shop" value="{{.ShopFilter}}">
|
||
<input type="hidden" name="stage" value="{{.StageFilter}}">
|
||
<input type="hidden" name="page" value="{{.CurrentPage}}">
|
||
<button type="submit" class="danger" data-need-checked>删除</button>
|
||
</form>
|
||
</div>
|
||
|
||
{{if .RangeError}}<p class="missing sync-range-message" role="alert">{{.RangeError}}</p>{{end}}
|
||
{{if .RangeWarning}}<p class="hint sync-range-message">{{.RangeWarning}}</p>{{end}}
|
||
<p class="hint sync-range-message">
|
||
按顺运宝货运单创建日期(UTC+8)同步,开始日和结束日都包含,每次最多 31 天。
|
||
</p>
|
||
|
||
<div class="modal-backdrop" id="sync-history-modal" {{if not .NeedSyncHistory}}hidden{{end}}>
|
||
<div class="modal modal-wide" role="dialog" aria-modal="true"
|
||
aria-labelledby="sync-history-modal-title">
|
||
<div class="modal-head">
|
||
<h2 id="sync-history-modal-title">同步记录</h2>
|
||
<button type="button" class="modal-x" data-modal-close aria-label="关闭">×</button>
|
||
</div>
|
||
<div data-syb-history-slot>
|
||
{{template "syb/history_partial" .}}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{if .ConfigProblem}}
|
||
<p class="missing">
|
||
顺运宝配置有问题:{{.ConfigProblem}}
|
||
</p>
|
||
{{end}}
|
||
|
||
{{/* 未登录只给一行提示,**不弹窗**(工单 #48)。
|
||
打开页面纯粹是看数据,不该触发任何对外部系统的动作;
|
||
真正会登录的只有「同步」那一个按钮。
|
||
用 .hint 不用 .missing——这是状态说明不是错误,
|
||
红色留给「PDD 链接未填写」那种要立刻处理的。 */}}
|
||
{{if .LoginHint}}
|
||
<p class="hint">{{.LoginHint}}</p>
|
||
{{end}}
|
||
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th class="col-check"><input type="checkbox" data-check-all></th>
|
||
<th>货运单 ID</th>
|
||
<th>订单号</th>
|
||
<th>店铺</th>
|
||
<th>商品标题</th>
|
||
<th>规格</th>
|
||
<th>蝦皮商品 ID</th>
|
||
<th>数量</th>
|
||
<th>价格</th>
|
||
<th>图片</th>
|
||
<th>处理阶段</th>
|
||
<th>下一步</th>
|
||
<th>更新时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{{range .Rows}}
|
||
<tr {{if .NeedsAttention}}class="row-warn"{{end}}>
|
||
<td class="col-check">
|
||
<span title="{{.SelectionHint}}">
|
||
<input type="checkbox" value="{{.SybID}}" name="ids"
|
||
data-select-action="{{if .CanCollect}}collect{{else if .CanPurchase}}purchase{{end}}"
|
||
{{if .CanCollect}}form="syb-collect-form"{{end}}
|
||
{{if not (or .CanCollect .CanPurchase)}}disabled{{end}}
|
||
aria-label="选择货运单明细 {{.SybID}}:{{.SelectionHint}}">
|
||
</span>
|
||
</td>
|
||
<td>{{.SybID}}</td>
|
||
<td>{{.OrderNo}}</td>
|
||
<td class="truncate" title="{{.ShopName}}">{{if .ShopName}}{{.ShopName}}{{else}}—{{end}}</td>
|
||
<td class="truncate" title="{{.Title}}">
|
||
<button type="button" class="table-title-action" data-detail-open-id="{{.SybID}}"
|
||
title="打开商品处理详情">{{.Title}}</button>
|
||
</td>
|
||
<td class="truncate" title="{{.ProductSpec}}">{{.ProductSpec}}</td>
|
||
<td>{{.ShopeeGoodsID}}</td>
|
||
<td>{{.Quantity}}</td>
|
||
<td>{{.PriceText}}</td>
|
||
<td>
|
||
{{if .ImageURL}}
|
||
<button type="button" class="thumb-action" data-image-preview-url="{{.ImageURL}}"
|
||
data-image-preview-title="{{.Title}}" title="查看商品原图"
|
||
aria-label="查看 {{.Title}} 的原图">
|
||
<img src="{{.ImageURL}}" alt="{{.Title}} 缩略图" class="thumb"
|
||
width="32" height="32" loading="lazy">
|
||
</button>
|
||
{{else}}—{{end}}
|
||
</td>
|
||
<td><span class="status-pill">{{.StageText}}</span><br><small>{{.StageHelp}}</small></td>
|
||
<td>
|
||
{{if eq .Stage "purchase_ready"}}
|
||
<button type="button" data-modal-open="purchase-modal" data-purchase-open
|
||
data-purchase-id="{{.SybID}}">{{.ActionText}}</button>
|
||
{{else if or (eq .Stage "task_created") (eq .Stage "purchase_completed") (eq .Stage "purchase_review")}}
|
||
<a class="button-link" href="/tasks?type=purchase&q={{urlquery .OrderNo}}">{{.ActionText}}</a>
|
||
{{else}}
|
||
<button type="button" data-detail-open-id="{{.SybID}}">{{.ActionText}}</button>
|
||
{{end}}
|
||
</td>
|
||
<td>{{.UpdatedAt}}</td>
|
||
</tr>
|
||
{{else}}
|
||
<tr class="empty">
|
||
<td colspan="13">
|
||
{{if .IsFiltered}}
|
||
当前筛选条件下没有货运单明细。<br>
|
||
<small>换个店铺、订单号或清空筛选条件再试。<a href="/syb">查看全部</a></small>
|
||
{{else}}
|
||
还没有货运单明细。<br>
|
||
<small>点上方「同步」从顺运宝拉取,第一次同步需要先登录(验证码需要手工输入)。</small>
|
||
{{end}}
|
||
</td>
|
||
</tr>
|
||
{{end}}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="modal-backdrop" id="syb-collect-modal" hidden>
|
||
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="syb-collect-modal-title">
|
||
<div class="modal-head">
|
||
<h2 id="syb-collect-modal-title">创建 PDD 采集任务</h2>
|
||
<button type="button" class="modal-x" data-modal-close aria-label="关闭">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p>将处理已选择的 <strong data-collect-count>0</strong> 条可采集货运单明细。</p>
|
||
<div class="field">
|
||
<label for="syb-collect-client">执行客户端</label>
|
||
<select id="syb-collect-client" name="client_id" form="syb-collect-form" autofocus>
|
||
<option value="">不指定(任意客户端可领取)</option>
|
||
{{range .AssignableClients}}
|
||
<option value="{{.ClientID}}">{{.Name}}({{.ClientID}},{{.Status}})</option>
|
||
{{end}}
|
||
</select>
|
||
</div>
|
||
<p class="hint">
|
||
同一 PDD 商品关联多条货运单时只创建一条任务。通常保持不指定即可;
|
||
指定后任务只等待该客户端领取。
|
||
</p>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button type="button" data-modal-close>取消</button>
|
||
<button type="submit" form="syb-collect-form" class="primary"
|
||
data-collect-submit>确认创建</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal-backdrop" id="purchase-modal" hidden>
|
||
<div class="modal modal-wide" role="dialog" aria-modal="true" aria-labelledby="purchase-modal-title">
|
||
<div class="modal-head">
|
||
<h2 id="purchase-modal-title">确认创建采购任务</h2>
|
||
<button type="button" class="modal-x" data-modal-close aria-label="关闭">×</button>
|
||
</div>
|
||
<form method="post" action="/syb/create-task" data-purchase-form>
|
||
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
||
<input type="hidden" name="order_no" value="{{.Keyword}}">
|
||
<input type="hidden" name="shop" value="{{.ShopFilter}}">
|
||
<input type="hidden" name="stage" value="{{.StageFilter}}">
|
||
<input type="hidden" name="page" value="{{.CurrentPage}}">
|
||
<div class="modal-body form-stack">
|
||
<div class="field">
|
||
<label for="purchase-client">执行客户端</label>
|
||
<select id="purchase-client" name="client_id" required autofocus>
|
||
<option value="">请选择执行客户端</option>
|
||
{{range .AssignableClients}}<option value="{{.ClientID}}" {{if eq $.PurchaseClientCount 1}}selected{{end}}>{{.Name}}({{.ClientID}}) — {{.Status}}</option>{{end}}
|
||
</select>
|
||
{{if eq .PurchaseClientCount 0}}<small class="missing">当前账号没有可见的客户端,请先联系管理员绑定客户端。</small>{{else}}<small>所有当前账号可见的客户端均可指派;未就绪或离线客户端会等待其就绪后领取。</small>{{end}}
|
||
</div>
|
||
<p class="hint">价格上限单位是人民币元,默认取已映射 PDD 规格的采集价;不会使用顺运宝的台币售价。</p>
|
||
{{range .Rows}}
|
||
<div class="purchase-confirm-row" data-purchase-row="{{.SybID}}" hidden>
|
||
<input type="hidden" name="ids" value="{{.SybID}}" disabled>
|
||
<input type="hidden" name="mapping_option_key" value="{{.MappingOptionKey}}" disabled>
|
||
<input type="hidden" name="context_version" value="{{.ContextVersion}}" disabled>
|
||
<div><strong>{{.OrderNo}}</strong> · {{.Title}} × {{.Quantity}}</div>
|
||
<div class="hint">顺运宝规格:{{.ProductSpec}}</div>
|
||
<div><strong>已选 PDD 规格:</strong>{{.MappedPddChoice}}</div>
|
||
<label>选定颜色的人民币价格上限(元)
|
||
<input type="number" name="max_price_yuan" min="0.01" step="0.01" value="{{.DefaultMaxPrice}}" required disabled>
|
||
</label>
|
||
</div>
|
||
{{end}}
|
||
<p class="missing" data-purchase-empty hidden>没有选择可采购的明细,请关闭后重新勾选。</p>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button type="button" data-modal-close>取消</button>
|
||
<button type="submit" class="primary" data-purchase-submit {{if eq .PurchaseClientCount 0}}disabled{{end}}>创建真实采购任务</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="hint">
|
||
每行只显示当前最需要完成的下一步;点击商品标题进入处理详情,点击图片查看原图。
|
||
</p>
|
||
|
||
<div class="modal-backdrop" id="image-preview-modal" hidden>
|
||
<div class="modal image-preview-modal" role="dialog" aria-modal="true"
|
||
aria-labelledby="image-preview-title">
|
||
<div class="modal-head">
|
||
<h2 id="image-preview-title" data-image-preview-title>商品原图</h2>
|
||
<button type="button" class="modal-x" data-modal-close aria-label="关闭">×</button>
|
||
</div>
|
||
<div class="modal-body image-preview-body">
|
||
<p class="hint image-preview-status" data-image-preview-status role="status">正在加载原图…</p>
|
||
<img data-image-preview-image alt="" hidden>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<a class="button-link" data-image-preview-link href="#" target="_blank"
|
||
rel="noopener noreferrer">新窗口打开原图</a>
|
||
<button type="button" data-modal-close>关闭</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal-backdrop" id="detail-modal" data-detail-url="{{.DetailURL}}" hidden>
|
||
<div class="modal modal-wide" role="dialog" aria-modal="true" data-detail-slot>
|
||
<div class="modal-body">正在加载…</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{/* ── 登录弹窗 ─────────────────────────────────
|
||
会话未登录/已过期时自动打开(不加 hidden);
|
||
`[必须]` 密码不在界面上显示、也不回显到 HTML,只显示只读账号。 */}}
|
||
<div class="modal-backdrop" id="login-modal" {{if not .NeedLogin}}hidden{{end}}>
|
||
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="login-modal-title">
|
||
<div class="modal-head">
|
||
<h2 id="login-modal-title">登录顺运宝</h2>
|
||
<button type="button" class="modal-x" data-modal-close aria-label="关闭">×</button>
|
||
</div>
|
||
<form method="post" action="/syb/login-and-sync">
|
||
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
||
<input type="hidden" name="order_no" value="{{.Keyword}}">
|
||
<input type="hidden" name="shop" value="{{.ShopFilter}}">
|
||
<input type="hidden" name="stage" value="{{.StageFilter}}">
|
||
<input type="hidden" name="page" value="{{.CurrentPage}}">
|
||
{{/* 登录和 OCR 降级后必须把工具栏里已确认的日期范围原样带回来。 */}}
|
||
<input type="hidden" name="date_from" value="{{.SyncDateFrom}}">
|
||
<input type="hidden" name="date_to" value="{{.SyncDateTo}}">
|
||
<div class="modal-body">
|
||
{{/* `[必须]` 工单 #47:自动识别验证码失败/降级时要说明原因
|
||
(已尝试 N 次 / 服务不可用),操作员才知道是配置错了还是
|
||
服务挂了。ocr_url 留空时压根没尝试过自动识别,不显示这条。 */}}
|
||
{{if .NeedLoginReason}}
|
||
<p class="hint login-degrade-reason">{{.NeedLoginReason}}</p>
|
||
{{end}}
|
||
<div class="field">
|
||
<label for="login-username">账号</label>
|
||
<input id="login-username" type="text" value="{{.Username}}" readonly>
|
||
</div>
|
||
<div class="field">
|
||
<label for="login-captcha-img">验证码</label>
|
||
{{/* 图片地址带一个时间戳查询参数,确保每次打开弹窗/点"换一张"
|
||
都拿到新图,不被浏览器缓存吃掉旧的 */}}
|
||
<img id="login-captcha-img" src="/syb/captcha" alt="验证码"
|
||
class="captcha-img" data-captcha-refresh="/syb/captcha">
|
||
<button type="button" id="login-captcha-refresh">换一张</button>
|
||
</div>
|
||
<div class="field">
|
||
<label for="login-code">验证码文字</label>
|
||
<input id="login-code" type="text" name="code" required autocomplete="off"
|
||
maxlength="8" placeholder="图片里的 4 位字母数字">
|
||
</div>
|
||
<p class="hint">
|
||
账号密码取自 <code>admin/config.yaml</code>,界面上不显示密码。
|
||
</p>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button type="button" data-modal-close>取消</button>
|
||
<button type="submit" class="primary">登录并同步</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
{{template "footer" .}}
|
||
{{end}}
|