fix: 完善顺运宝列表紧凑布局 (#216)

This commit is contained in:
chengma
2026-08-13 14:35:34 +08:00
parent 47cdb2ed03
commit f834f6224c
9 changed files with 82 additions and 33 deletions
+17 -3
View File
@@ -336,7 +336,7 @@ func TestSybPage_紧凑筛选和可见横向滚动(t *testing.T) {
`class="table-wrap syb-table-wrap"`,
`class="syb-col-shop"`,
`class="syb-col-spec"`,
`colspan="12"`,
`colspan="11"`,
} {
if !strings.Contains(pageText, want) {
t.Errorf("顺运宝紧凑列表缺少 %q", want)
@@ -345,6 +345,8 @@ func TestSybPage_紧凑筛选和可见横向滚动(t *testing.T) {
for _, removed := range []string{
`href="/shops">店铺管理(同步`,
`<th>货运单 ID</th>`,
`<th>处理阶段</th>`,
`按顺运宝货运单创建日期(UTC+8)同步`,
`每行只显示当前最需要完成的下一步`,
} {
if strings.Contains(pageText, removed) {
@@ -359,10 +361,15 @@ func TestSybPage_紧凑筛选和可见横向滚动(t *testing.T) {
`.syb-col-spec`,
`max-width: 140px`,
`.syb-table-wrap`,
`max-height: calc(100vh - 270px)`,
`flex: 1 1 auto`,
`min-height: 0`,
`max-height: none`,
`overflow: auto`,
`.syb-table-wrap thead th`,
`position: sticky`,
`body.syb-page-body`,
`height: 100dvh`,
`body.syb-page-body > .page`,
} {
if !strings.Contains(string(css), want) {
t.Errorf("顺运宝紧凑列表样式缺少 %q", want)
@@ -371,6 +378,9 @@ func TestSybPage_紧凑筛选和可见横向滚动(t *testing.T) {
if !strings.Contains(string(header), `href="/shops"`) {
t.Error("移除顺运宝工具条入口后,管理员主导航仍应保留店铺管理模块")
}
if !strings.Contains(string(header), `class="syb-page-body"`) {
t.Error("顺运宝页面缺少专用视口布局类")
}
}
func TestShops_单一名称和状态且保护删除(t *testing.T) {
@@ -610,7 +620,6 @@ func TestMainPagesReturnOK(t *testing.T) {
`data-history-fetch-url=`,
`name="date_from"`, `name="date_to"`,
`name="stage"`, "处理阶段", `name="shop"`, "店铺", "下一步", `data-detail-url=`,
"按顺运宝货运单创建日期(UTC+8)同步",
"创建真实采购任务", "所有当前账号可见的客户端均可指派",
} {
if !strings.Contains(sybResponse.Body.String(), want) {
@@ -630,6 +639,11 @@ func TestMainPagesReturnOK(t *testing.T) {
if strings.Contains(sybResponse.Body.String(), "指定日期同步") {
t.Error("顺运宝页面不应再显示旧的指定日期同步入口")
}
for _, removed := range []string{"按顺运宝货运单创建日期(UTC+8)同步", `<th>处理阶段</th>`} {
if strings.Contains(sybResponse.Body.String(), removed) {
t.Errorf("顺运宝页面仍包含已移除内容 %q", removed)
}
}
for _, removed := range []string{`name="execution_mode"`, "采购演练", "仅演练", "支持真实下单",
"真实下单安全确认", `name="live_acknowledged"`, `name="live_confirmation"`, `data-confirm-submit=`} {
if strings.Contains(sybResponse.Body.String(), removed) {
+3 -3
View File
@@ -1133,11 +1133,11 @@ func sybStageFor(c repository.SybOrderContext) (stage, text, help, action string
case c.HasSucceededPurchaseTask:
return SybStagePurchaseCompleted, "采购完成", "采购任务已成功完成,请到采集采购页面查看 PDD 订单编号和下单时间。", "查看采购结果"
case c.HasManualReviewPurchaseTask:
return SybStagePurchaseReview, "采购待人工核对", "任务可能已经下单,必须先核对订单,禁止重新创建采购任务。", "查看采购任务"
return SybStagePurchaseReview, "采购待人工核对", "任务可能已经下单,必须先核对订单,禁止重新创建采购任务。", "核对采购订单"
case c.HasActiveTask:
return SybStageTaskCreated, "已创建采购任务", "已有未结束的采购任务,请到采集采购页面查看。", "查看采购任务"
case c.Order.SpecKey == "":
return SybStageSpecMissing, "采购数据异常:顺运宝未提供规格", "缺少规格原文,不能匹配或创建采购任务。", "核对数据"
return SybStageSpecMissing, "采购数据异常:顺运宝未提供规格", "缺少规格原文,不能匹配或创建采购任务。", "核对缺失规格"
case c.PddGoodsID == "" || c.PddCollectStatus == "":
return SybStagePddMissing, "未关联 PDD", "下一步关联采购商品。", "关联 PDD"
case c.PddCollectStatus == string(model.CollectPending):
@@ -1155,7 +1155,7 @@ func sybStageFor(c repository.SybOrderContext) (stage, text, help, action string
case !mappingIsValid(c):
return SybStageMappingPending, "规格待匹配", "PDD 数据已采集,下一步匹配采购规格。", "匹配规格"
case c.Order.Quantity <= 0:
return SybStagePurchaseBlocked, "采购数据异常", "顺运宝采购数量不是正数,不能创建采购任务。", "核对数据"
return SybStagePurchaseBlocked, "采购数据异常", "顺运宝采购数量不是正数,不能创建采购任务。", "核对采购数量"
default:
return SybStagePurchaseReady, "可创建采购任务", "规格映射有效,可以创建采购任务。", "创建采购任务"
}
+3 -3
View File
@@ -110,7 +110,7 @@ func TestSybStageFor_全部筛选阶段与逐行推导一致(t *testing.T) {
action string
context repository.SybOrderContext
}{
{SybStageSpecMissing, "核对数据", copyWith(func(c *repository.SybOrderContext) { c.Order.SpecKey = "" })},
{SybStageSpecMissing, "核对缺失规格", copyWith(func(c *repository.SybOrderContext) { c.Order.SpecKey = "" })},
{SybStagePddMissing, "关联 PDD", copyWith(func(c *repository.SybOrderContext) { c.PddGoodsID, c.PddCollectStatus = "", "" })},
{SybStagePddPending, "创建采集任务", copyWith(func(c *repository.SybOrderContext) { c.PddCollectStatus = string(model.CollectPending) })},
{SybStagePddCollecting, "查看采集状态", copyWith(func(c *repository.SybOrderContext) {
@@ -129,11 +129,11 @@ func TestSybStageFor_全部筛选阶段与逐行推导一致(t *testing.T) {
c.HasSucceededPurchaseTask, c.HasManualReviewPurchaseTask = true, true
c.Order.SpecKey = ""
})},
{SybStagePurchaseReview, "查看采购任务", copyWith(func(c *repository.SybOrderContext) {
{SybStagePurchaseReview, "核对采购订单", copyWith(func(c *repository.SybOrderContext) {
c.HasManualReviewPurchaseTask = true
c.MappingOptionKey = ""
})},
{SybStagePurchaseBlocked, "核对数据", copyWith(func(c *repository.SybOrderContext) { c.Order.Quantity = 0 })},
{SybStagePurchaseBlocked, "核对采购数量", copyWith(func(c *repository.SybOrderContext) { c.Order.Quantity = 0 })},
{SybStagePurchaseReady, "创建采购任务", ready},
}
if got := len(SybStageOptions()) - 1; got != len(cases) {
+27 -1
View File
@@ -79,6 +79,23 @@ body {
/* ── 页面主体 ─────────────────────────── */
.page { padding: 16px; }
/* 顺运宝是高密度操作表:桌面端让主区域占满导航和固定状态栏之间的空间,
工具条或条件提示高度变化时,表格自动取得剩余高度,不依赖固定像素扣减。 */
body.syb-page-body {
height: 100vh;
height: 100dvh;
display: flex;
flex-direction: column;
overflow: hidden;
}
body.syb-page-body > .nav { flex: 0 0 auto; }
body.syb-page-body > .page {
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
}
/* ── 第一段:顶部工具条 ───────────────── */
.toolbar {
display: flex;
@@ -256,7 +273,9 @@ tr.empty small { color: #aaa; }
max-width: 140px;
}
.syb-table-wrap {
max-height: calc(100vh - 270px);
flex: 1 1 auto;
min-height: 0;
max-height: none;
overflow: auto;
scrollbar-gutter: stable;
}
@@ -266,6 +285,13 @@ tr.empty small { color: #aaa; }
z-index: 1;
}
@media (max-width: 760px) {
body.syb-page-body {
height: auto;
min-height: 100dvh;
display: block;
overflow: auto;
}
body.syb-page-body > .page { display: block; }
.syb-table-wrap { max-height: none; }
}
+6 -1
View File
@@ -35,7 +35,7 @@ func TestSybListTemplate_区分标题图片与阶段动作(t *testing.T) {
{
SybID: "SYB-REVIEW", OrderNo: "ORDER-REVIEW", Title: "待核对商品",
Stage: service.SybStagePurchaseReview, StageText: "采购待人工核对",
StageHelp: "禁止重新采购", ActionText: "查看采购任务",
StageHelp: "禁止重新采购", ActionText: "核对采购订单",
},
{
SybID: "SYB-PDD", OrderNo: "ORDER-PDD", Title: "待关联商品",
@@ -90,10 +90,13 @@ func TestSybListTemplate_区分标题图片与阶段动作(t *testing.T) {
`href="/syb?date_from=2026-08-12`,
`date_to=2026-08-13">清除</a>`,
`class="table-wrap syb-table-wrap"`,
`class="syb-page-body"`,
`<th class="syb-col-shop">店铺</th>`,
`<th class="syb-col-spec">规格</th>`,
`value="SYB-READY" name="ids"`,
`同一 PDD 商品关联多条货运单时只创建一条任务`,
`>核对采购订单</a>`,
`title="禁止重新采购"`,
`title="当前阶段不能批量操作:下一步关联采购商品"`,
} {
if !strings.Contains(body, want) {
@@ -105,10 +108,12 @@ func TestSybListTemplate_区分标题图片与阶段动作(t *testing.T) {
}
for _, removed := range []string{
`<th>货运单 ID</th>`,
`<th>处理阶段</th>`,
`<td>SYB-READY</td>`,
`>创建 PDD 采集任务</button>`,
`<h2 id="syb-collect-modal-title">创建 PDD 采集任务</h2>`,
`每行只显示当前最需要完成的下一步`,
`按顺运宝货运单创建日期(UTC+8)同步`,
} {
if strings.Contains(body, removed) {
t.Errorf("顺运宝列表仍包含已移除内容 %q", removed)
+1 -1
View File
@@ -6,7 +6,7 @@
<title>{{.Title}} · 采集采购管理端</title>
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body>
<body{{if eq .Active "syb"}} class="syb-page-body"{{end}}>
<nav class="nav" data-module-navigation data-ui-state-user="{{if .CurrentUser}}{{.CurrentUser.UserID}}{{end}}">
<span class="nav-brand">采集采购管理端</span>
+6 -9
View File
@@ -64,9 +64,6 @@
{{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 天;当前在店铺管理中启用 {{.EnabledSyncShopCount}} 个 SYB 同步店铺。
</p>
<div class="modal-backdrop" id="sync-history-modal" {{if not .NeedSyncHistory}}hidden{{end}}>
<div class="modal modal-wide" role="dialog" aria-modal="true"
@@ -109,7 +106,6 @@
<th>数量</th>
<th>价格</th>
<th>图片</th>
<th>处理阶段</th>
<th>下一步</th>
<th>更新时间</th>
</tr>
@@ -146,22 +142,23 @@
</button>
{{else}}—{{end}}
</td>
<td><span class="status-pill">{{.StageText}}</span><br><small>{{.StageHelp}}</small>{{if .PddAssociationText}}<br><small class="hint">{{.PddAssociationText}}</small>{{end}}</td>
<td>
{{if eq .Stage "purchase_ready"}}
<button type="button" data-modal-open="purchase-modal" data-purchase-open
data-purchase-id="{{.SybID}}">{{.ActionText}}</button>
data-purchase-id="{{.SybID}}" title="{{.StageHelp}}">{{.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>
<a class="button-link" href="/tasks?type=purchase&q={{urlquery .OrderNo}}"
title="{{.StageHelp}}">{{.ActionText}}</a>
{{else}}
<button type="button" data-detail-open-id="{{.SybID}}">{{.ActionText}}</button>
<button type="button" data-detail-open-id="{{.SybID}}"
title="{{.StageHelp}}">{{.ActionText}}</button>
{{end}}
</td>
<td>{{.UpdatedAt}}</td>
</tr>
{{else}}
<tr class="empty">
<td colspan="12">
<td colspan="11">
{{if .IsFiltered}}
当前筛选条件下没有货运单明细。<br>
<small>换个店铺、订单号或清空筛选条件再试。<a href="/syb">查看全部</a></small>