feat: 蝦皮数据页改为商品级列表与规格弹窗 (#41)

原来是 SKU 级一行,问题不在行数(6092→5195 只少 15%),在语义错了:
pdd_goods_url / pdd_goods_id 在 shopee_products 上,是商品级字段。
商品 24420648774 有 62 个 SKU,列表里 62 行显示同一个 PDD 链接,
将来双击任意一行编辑的也是同一个字段,操作员会以为改的是这一行。

加了两列,都是防止聚合后丢信息:

「SKU 数」—— 只显示颜色数和尺码数会严重误导。实测 602 个商品(11.6%)
的颜色数×尺码数大于实际 SKU 数,最悬殊的 26886533818 是 15×5=75 但
实际只有 28 个。蝦皮报表只含有销售成绩的 SKU,数据本来就不全。

「待补」—— 原来解析失败的行整行标黄,聚合成数量后这个信号会丢。
23 条失败分布在 6 个商品里,其中 4 个是「部分失败」:数字看着正常,
坏数据被吃掉,永远没人去补。现在待补>0 整行标黄。

颜色数/尺码数只统计 parse_ok=1,否则空值会被算进 DISTINCT 多出
一个「空颜色」。

弹窗只读,待补的行显示 spec_raw 原文——不显示的话人工不知道该填什么,
保留原文这个设计就白做了。复用 #18 的弹窗机制,app.js 未改动。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
chengma
2026-08-08 11:12:25 +08:00
co-authored by Claude Opus 5
parent d199867101
commit 64d5e74d0d
8 changed files with 658 additions and 81 deletions
+70
View File
@@ -0,0 +1,70 @@
{{define "shopee/detail_modal"}}
{{/* 双击一行时弹出的内容。
这是一个**片段**,不是整页——外面的弹窗壳子在 shopee/list.html 里。
`[必须]` 这个弹窗只读,没有保存表单——ShopeeSave 仍是 501,
不要放一个点了没反应的保存按钮,见工单 #41。 */}}
{{with .D}}
<div class="modal-head">
<h2>商品 {{.GoodsID}}</h2>
<button type="button" class="modal-x" data-modal-close aria-label="关闭">×</button>
</div>
<div class="modal-body">
<dl class="detail">
<dt>商品名称</dt><dd>{{.Title}}</dd>
<dt>蝦皮状态</dt><dd>{{.ShopeeStatus}}</dd>
<dt>主商品货号</dt><dd>{{.MainSKUCode}}</dd>
{{/* PDD 链接本工单只读展示,不做保存,见 #41「不做」清单 */}}
<dt>PDD 链接</dt>
<dd{{if .PddMissing}} class="missing"{{end}}>{{.PddURL}}</dd>
<dt>采集状态</dt><dd>{{.StatusText}}</dd>
</dl>
<h3>
规格({{len .SKUs}} 个{{if gt .PendingCount 0}},{{.PendingCount}} 个待人工补{{end}})
</h3>
{{if not .SKUs}}
<p class="missing">这个商品在报表里没有任何 SKU。</p>
{{else}}
<div class="table-wrap">
<table>
<thead>
<tr>
<th>规格ID</th>
<th>颜色</th>
<th>尺码</th>
<th>建議</th>
{{/* 状态列要有文字(✓ / 待补),不能只靠颜色区分,见 #41 */}}
<th>状态</th>
</tr>
</thead>
<tbody>
{{range .SKUs}}
<tr{{if .Pending}} class="row-warn"{{end}}>
<td>{{.SKUID}}</td>
<td>{{.Color}}</td>
<td>{{.Size}}</td>
<td>{{.Advice}}</td>
<td>{{.StatusText}}</td>
</tr>
{{if .Pending}}
{{/* `[必须]` 待补的行要显示 spec_raw 原文——不显示的话人工不知道
该填什么,这正是保留原文这个设计的全部意义,见 #41。 */}}
<tr class="row-warn">
<td colspan="5"><small>原文:{{.SpecRaw}}</small></td>
</tr>
{{end}}
{{end}}
</tbody>
</table>
</div>
{{end}}
</div>
<div class="modal-foot">
<button type="button" data-modal-close>关闭</button>
</div>
{{end}}
{{end}}