feat: 支持顺运宝批量创建采集任务 (#153)
This commit is contained in:
@@ -611,6 +611,27 @@ func (h *Handler) SybCollectPdd(c *gin.Context) {
|
||||
h.sybRedirect(c, service.FormatCollectTaskMessage(result))
|
||||
}
|
||||
|
||||
// SybCollectPddBatch 为勾选的顺运宝明细批量创建 PDD 采集任务。
|
||||
// Service 会重新校验阶段、按 PDD 商品去重并核对指定客户端的可见范围。
|
||||
func (h *Handler) SybCollectPddBatch(c *gin.Context) {
|
||||
ids := c.PostFormArray("ids")
|
||||
if len(ids) == 0 {
|
||||
h.sybRedirect(c, "没有选择可采集的顺运宝明细,没有创建任务。")
|
||||
return
|
||||
}
|
||||
result, err := service.CreateSybPddCollectTasksForUser(
|
||||
h.db, currentUser(c), ids, c.PostForm("client_id"))
|
||||
if err != nil {
|
||||
message := "批量采集任务未创建:系统没有改动这一批数据,请稍后重试。"
|
||||
if service.IsValidationError(err) {
|
||||
message = "批量采集任务未创建:" + err.Error()
|
||||
}
|
||||
h.sybRedirect(c, message)
|
||||
return
|
||||
}
|
||||
h.sybRedirect(c, service.FormatCollectTaskMessage(result))
|
||||
}
|
||||
|
||||
// SybMatch 保存规格匹配结果。
|
||||
//
|
||||
// 关键:保存的是**可复用的商品规格映射**(写 spec_mappings),
|
||||
|
||||
@@ -166,3 +166,26 @@ func TestPurchaseTaskOptionsFromForm_忽略篡改模式并固定Live(t *testing.
|
||||
t.Fatalf("采购客户端读取错误: %+v", options)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSybCollectPddBatch_未选择时保留列表筛选(t *testing.T) {
|
||||
context, recorder := sybPostContext(t, url.Values{
|
||||
"order_no": {"ORDER-1"}, "shop": {"qwg8fkb044"},
|
||||
"stage": {"pdd_pending"}, "page": {"2"},
|
||||
})
|
||||
(&Handler{}).SybCollectPddBatch(context)
|
||||
location, err := url.Parse(recorder.Header().Get("Location"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
query := location.Query()
|
||||
for name, want := range map[string]string{
|
||||
"order_no": "ORDER-1", "shop": "qwg8fkb044", "stage": "pdd_pending", "page": "2",
|
||||
} {
|
||||
if got := query.Get(name); got != want {
|
||||
t.Errorf("批量采集返回丢失 %s:got=%q want=%q Location=%s", name, got, want, location.String())
|
||||
}
|
||||
}
|
||||
if !strings.Contains(query.Get("msg"), "没有选择") {
|
||||
t.Fatalf("未选择应返回可读提示:%s", location.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ func Register(r *gin.Engine, db *sql.DB, onlineThreshold time.Duration) {
|
||||
pages.POST("/syb/login-and-sync", h.SybLoginAndSync)
|
||||
pages.POST("/syb/associate-pdd", h.SybAssociatePdd)
|
||||
pages.POST("/syb/collect-pdd", h.SybCollectPdd)
|
||||
pages.POST("/syb/collect-pdd-batch", h.SybCollectPddBatch)
|
||||
pages.POST("/syb/match", h.SybMatch)
|
||||
pages.POST("/syb/create-task", h.SybCreateTask)
|
||||
pages.POST("/syb/delete", h.SybDelete)
|
||||
|
||||
@@ -65,6 +65,37 @@ func TestPurchaseModal_未选规格行保持隐藏且不提交(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSybBulkActions_按服务端动作能力隔离勾选项(t *testing.T) {
|
||||
files := map[string][]string{
|
||||
"templates/syb/list.html": {
|
||||
`data-need-checked="collect"`, `data-need-checked="purchase"`,
|
||||
`data-select-action="{{if .CanCollect}}collect{{else if .CanPurchase}}purchase{{end}}"`,
|
||||
`{{if .CanCollect}}form="syb-collect-form"{{end}}`,
|
||||
`action="/syb/collect-pdd-batch"`, `id="syb-collect-modal"`,
|
||||
},
|
||||
"static/js/app.js": {
|
||||
`supportsSelectAction(box, action)`,
|
||||
`checkedCount(action)`,
|
||||
`supportsSelectAction(box, "purchase")`,
|
||||
`getAttribute("data-collect-action")`,
|
||||
},
|
||||
"handler/web/web.go": {
|
||||
`pages.POST("/syb/collect-pdd-batch", h.SybCollectPddBatch)`,
|
||||
},
|
||||
}
|
||||
for path, wants := range files {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range wants {
|
||||
if !strings.Contains(string(content), want) {
|
||||
t.Errorf("%s 缺少动作隔离约束 %q", path, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPddDetailRecollect_使用独立入口并可选客户端(t *testing.T) {
|
||||
content, err := os.ReadFile("templates/pdd/edit_modal.html")
|
||||
if err != nil {
|
||||
|
||||
@@ -157,3 +157,31 @@ func CreateSybPddCollectTaskForUser(db *sql.DB, actor *model.User, sybID string)
|
||||
}
|
||||
return CreateShopeePddCollectTaskForUser(db, actor, context.Order.ShopeeGoodsID)
|
||||
}
|
||||
|
||||
// CreateSybPddCollectTasksForUser 把勾选的顺运宝明细转换成 PDD 商品后批量建采集任务。
|
||||
// 同一 PDD 商品可能对应多条货运单明细,因此先按 goods_id 去重;任何明细不存在或
|
||||
// 已不处于可采集阶段时整批拒绝,避免浏览器过期或参数篡改造成部分创建。
|
||||
func CreateSybPddCollectTasksForUser(db *sql.DB, actor *model.User, sybIDs []string, clientID string) (CollectTaskResult, error) {
|
||||
sybIDs = dedupe(sybIDs)
|
||||
if len(sybIDs) == 0 {
|
||||
return CollectTaskResult{}, invalidInput("没有选择可采集的顺运宝明细")
|
||||
}
|
||||
|
||||
goodsIDs := make([]string, 0, len(sybIDs))
|
||||
for _, sybID := range sybIDs {
|
||||
context, err := repository.GetSybOrderContext(db, sybID)
|
||||
if err != nil {
|
||||
return CollectTaskResult{}, err
|
||||
}
|
||||
if context == nil {
|
||||
return CollectTaskResult{}, invalidInput("顺运宝明细 %s 不存在,请刷新页面后重新选择", sybID)
|
||||
}
|
||||
stage, _, _, _ := sybStageFor(*context)
|
||||
if stage != SybStagePddPending && stage != SybStagePddFailed {
|
||||
return CollectTaskResult{}, invalidInput("顺运宝明细 %s 已不处于 PDD 待采集或采集失败状态,请刷新页面后重新选择", sybID)
|
||||
}
|
||||
goodsIDs = append(goodsIDs, context.PddGoodsID)
|
||||
}
|
||||
|
||||
return CreatePddCollectTasksForUser(db, actor, dedupe(goodsIDs), clientID)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cmautobuy/admin/model"
|
||||
"cmautobuy/admin/repository"
|
||||
)
|
||||
|
||||
@@ -116,3 +119,92 @@ func TestCreateShopeePddCollectTaskForUser_记录当前用户(t *testing.T) {
|
||||
t.Fatalf("蝦皮入口任务创建人=%q err=%v", creator, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSybPddCollectTasksForUser_同一PDD商品去重(t *testing.T) {
|
||||
db := newTestDB(t)
|
||||
admin, _, _ := insertTaskUsers(t, db)
|
||||
seedShopeeProduct(t, db, "1001", "商品一")
|
||||
seedShopeeProduct(t, db, "1002", "商品二")
|
||||
if _, err := AssociateShopeePdd(db, "1001", pddURLA, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := AssociateShopeePdd(db, "1002", pddURLA, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
seedSybCollectOrder(t, db, "SYB-1", "1001")
|
||||
seedSybCollectOrder(t, db, "SYB-2", "1002")
|
||||
|
||||
result, err := CreateSybPddCollectTasksForUser(
|
||||
db, admin, []string{"SYB-1", "SYB-2", "SYB-1"}, "")
|
||||
if err != nil || result.Created != 1 {
|
||||
t.Fatalf("批量创建结果=%+v err=%v", result, err)
|
||||
}
|
||||
var taskCount int
|
||||
if err := db.QueryRow(`SELECT COUNT(*) FROM tasks WHERE task_type='collect'`).Scan(&taskCount); err != nil || taskCount != 1 {
|
||||
t.Fatalf("同一 PDD 商品应只建一条任务,count=%d err=%v", taskCount, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSybPddCollectTasksForUser_不可采集明细整批拒绝(t *testing.T) {
|
||||
db := newTestDB(t)
|
||||
admin, _, _ := insertTaskUsers(t, db)
|
||||
seedShopeeProduct(t, db, "1001", "商品一")
|
||||
seedShopeeProduct(t, db, "1002", "商品二")
|
||||
AssociateShopeePdd(db, "1001", pddURLA, false)
|
||||
AssociateShopeePdd(db, "1002", pddURLB, false)
|
||||
seedSybCollectOrder(t, db, "SYB-PENDING", "1001")
|
||||
seedSybCollectOrder(t, db, "SYB-COLLECTED", "1002")
|
||||
if _, err := db.Exec(`UPDATE pdd_products SET collect_status='collected' WHERE goods_id='937122477375'`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err := CreateSybPddCollectTasksForUser(
|
||||
db, admin, []string{"SYB-PENDING", "SYB-COLLECTED"}, "")
|
||||
if !IsValidationError(err) {
|
||||
t.Fatalf("不可采集明细应返回校验错误,实际 %v", err)
|
||||
}
|
||||
var taskCount int
|
||||
db.QueryRow(`SELECT COUNT(*) FROM tasks WHERE task_type='collect'`).Scan(&taskCount)
|
||||
if taskCount != 0 {
|
||||
t.Fatalf("整批拒绝后不应产生部分任务,实际 %d", taskCount)
|
||||
}
|
||||
pending, _ := repository.GetPddProductByGoodsID(db, "737116531267")
|
||||
if pending.CollectStatus != model.CollectPending {
|
||||
t.Fatalf("整批拒绝后待采集商品状态被改变为 %s", pending.CollectStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSybPddCollectTasksForUser_拒绝不可见客户端(t *testing.T) {
|
||||
db := newTestDB(t)
|
||||
admin, buyerA, buyerB := prepareClientAssignmentUsers(t, db)
|
||||
if err := RegisterClient(db, model.Client{ClientID: "client-b", Name: "B 的机器"}, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, err := AssignClient(db, admin, "client-b", buyerB.UserID,
|
||||
time.Date(2026, 8, 11, 1, 0, 0, 0, time.UTC)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
seedShopeeProduct(t, db, "1001", "商品一")
|
||||
AssociateShopeePdd(db, "1001", pddURLA, false)
|
||||
seedSybCollectOrder(t, db, "SYB-1", "1001")
|
||||
|
||||
_, err := CreateSybPddCollectTasksForUser(db, buyerA, []string{"SYB-1"}, "client-b")
|
||||
if !IsValidationError(err) {
|
||||
t.Fatalf("不可见客户端应返回校验错误,实际 %v", err)
|
||||
}
|
||||
var taskCount int
|
||||
db.QueryRow(`SELECT COUNT(*) FROM tasks WHERE task_type='collect'`).Scan(&taskCount)
|
||||
if taskCount != 0 {
|
||||
t.Fatalf("客户端权限校验失败后不应建任务,实际 %d", taskCount)
|
||||
}
|
||||
}
|
||||
|
||||
func seedSybCollectOrder(t *testing.T, db *sql.DB, sybID, shopeeGoodsID string) {
|
||||
t.Helper()
|
||||
if _, err := repository.UpsertSybOrder(db, model.SybOrder{
|
||||
SybID: sybID, OrderNo: "ORDER-" + sybID, Title: "测试商品", ProductSpec: "黑色,M",
|
||||
ShopeeGoodsID: shopeeGoodsID, Quantity: 1, SybData: `{}`,
|
||||
}); err != nil {
|
||||
t.Fatalf("插入顺运宝测试明细失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -939,7 +939,9 @@ type SybOrderView struct {
|
||||
StageHelp string
|
||||
ActionText string
|
||||
NeedsAttention bool
|
||||
CanCollect bool
|
||||
CanPurchase bool
|
||||
SelectionHint string
|
||||
DefaultMaxPrice string
|
||||
MappedPddChoice string
|
||||
MappingOptionKey string
|
||||
@@ -1129,7 +1131,16 @@ func sybOrderViewFor(context repository.SybOrderContext) SybOrderView {
|
||||
Quantity: o.Quantity, ImageURL: o.ImageURL,
|
||||
UpdatedAt: formatLocalTime(o.UpdatedAt)}
|
||||
v.Stage, v.StageText, v.StageHelp, v.ActionText = sybStageFor(context)
|
||||
v.CanCollect = v.Stage == SybStagePddPending || v.Stage == SybStagePddFailed
|
||||
v.CanPurchase = v.Stage == SybStagePurchaseReady
|
||||
switch {
|
||||
case v.CanCollect:
|
||||
v.SelectionHint = "可批量创建 PDD 采集任务"
|
||||
case v.CanPurchase:
|
||||
v.SelectionHint = "可批量创建采购任务"
|
||||
default:
|
||||
v.SelectionHint = "当前阶段不能批量操作:" + v.StageHelp
|
||||
}
|
||||
if v.CanPurchase {
|
||||
v.MappingOptionKey = context.MappingOptionKey
|
||||
v.ContextVersion = mappingContextVersion(context)
|
||||
|
||||
@@ -130,3 +130,44 @@ func TestSybStageFor_全部筛选阶段与逐行推导一致(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSybOrderViewFor_批量动作能力按阶段隔离(t *testing.T) {
|
||||
base := repository.SybOrderContext{
|
||||
Order: model.SybOrder{SybID: "SYB-1", SpecKey: "黑色,M", Quantity: 1},
|
||||
PddGoodsID: "737116531267",
|
||||
}
|
||||
cases := []struct {
|
||||
status model.CollectStatus
|
||||
mappingKey string
|
||||
wantCollect, wantPurchase bool
|
||||
}{
|
||||
{status: model.CollectPending, wantCollect: true},
|
||||
{status: model.CollectFailed, wantCollect: true},
|
||||
{status: model.CollectCollecting},
|
||||
{status: model.CollectCollected},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
context := base
|
||||
context.PddCollectStatus = string(tc.status)
|
||||
context.MappingOptionKey = tc.mappingKey
|
||||
view := sybOrderViewFor(context)
|
||||
if view.CanCollect != tc.wantCollect || view.CanPurchase != tc.wantPurchase {
|
||||
t.Errorf("status=%s: CanCollect=%v CanPurchase=%v", tc.status, view.CanCollect, view.CanPurchase)
|
||||
}
|
||||
if view.SelectionHint == "" {
|
||||
t.Errorf("status=%s: 应给出选择能力说明", tc.status)
|
||||
}
|
||||
}
|
||||
key, err := OptionKey(map[string]string{"color": "黑色", "size": "M码", "style": "常规"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ready := base
|
||||
ready.PddCollectStatus = string(model.CollectCollected)
|
||||
ready.PddSkusJSON = collectedThreeDimensions
|
||||
ready.MappingOptionKey = key
|
||||
view := sybOrderViewFor(ready)
|
||||
if view.CanCollect || !view.CanPurchase {
|
||||
t.Fatalf("采购就绪行应只允许采购:CanCollect=%v CanPurchase=%v", view.CanCollect, view.CanPurchase)
|
||||
}
|
||||
}
|
||||
|
||||
+16
-8
@@ -40,18 +40,25 @@
|
||||
}
|
||||
|
||||
/* ── 选中数量 ────────────────────────────── */
|
||||
function checkedCount() {
|
||||
return document.querySelectorAll(
|
||||
"tbody input[type=checkbox]:checked:not(:disabled)"
|
||||
function supportsSelectAction(box, action) {
|
||||
if (!action) return true;
|
||||
var actions = (box.getAttribute("data-select-action") || "").split(/\s+/);
|
||||
return actions.indexOf(action) !== -1;
|
||||
}
|
||||
|
||||
function checkedCount(action) {
|
||||
return Array.prototype.filter.call(
|
||||
document.querySelectorAll("tbody input[type=checkbox]:checked:not(:disabled)"),
|
||||
function (box) { return supportsSelectAction(box, action); }
|
||||
).length;
|
||||
}
|
||||
|
||||
/* ── 需要勾选才能点的按钮 ──────────────────
|
||||
标了 data-need-checked 的按钮,没勾选任何行时禁用。 */
|
||||
function syncButtons() {
|
||||
var n = checkedCount();
|
||||
document.querySelectorAll("[data-need-checked]").forEach(function (btn) {
|
||||
btn.disabled = n === 0;
|
||||
var action = btn.getAttribute("data-need-checked") || "";
|
||||
btn.disabled = checkedCount(action) === 0;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -148,8 +155,8 @@
|
||||
if (singleID) {
|
||||
selected[singleID] = true;
|
||||
} else {
|
||||
document.querySelectorAll("tbody input[name=ids]:checked").forEach(function (box) {
|
||||
selected[box.value] = true;
|
||||
document.querySelectorAll("tbody input[name=ids]:checked:not(:disabled)").forEach(function (box) {
|
||||
if (supportsSelectAction(box, "purchase")) selected[box.value] = true;
|
||||
});
|
||||
}
|
||||
var shown = 0;
|
||||
@@ -183,11 +190,12 @@
|
||||
if (!form || !modal) return;
|
||||
var count = modal.querySelector("[data-collect-count]");
|
||||
var submitButton = modal.querySelector("[data-collect-submit]");
|
||||
var action = openButton.getAttribute("data-collect-action") || "";
|
||||
if (!count || !submitButton) return;
|
||||
|
||||
openButton.addEventListener("click", function () {
|
||||
var fixedCount = openButton.getAttribute("data-collect-count-value");
|
||||
count.textContent = fixedCount === null ? checkedCount() : fixedCount;
|
||||
count.textContent = fixedCount === null ? checkedCount(action) : fixedCount;
|
||||
});
|
||||
form.addEventListener("submit", function (event) {
|
||||
if (event.submitter !== submitButton) return;
|
||||
|
||||
@@ -30,6 +30,13 @@ func TestSybListTemplate_区分标题图片与阶段动作(t *testing.T) {
|
||||
SybID: "SYB-PDD", OrderNo: "ORDER-PDD", Title: "待关联商品",
|
||||
Stage: service.SybStagePddMissing, StageText: "未关联 PDD",
|
||||
StageHelp: "下一步关联采购商品", ActionText: "关联 PDD",
|
||||
SelectionHint: "当前阶段不能批量操作:下一步关联采购商品",
|
||||
},
|
||||
{
|
||||
SybID: "SYB-COLLECT", OrderNo: "ORDER-COLLECT", Title: "待采集商品",
|
||||
Stage: service.SybStagePddPending, StageText: "PDD 待采集",
|
||||
StageHelp: "尚未创建采集任务", ActionText: "创建采集任务",
|
||||
CanCollect: true, SelectionHint: "可批量创建 PDD 采集任务",
|
||||
},
|
||||
}
|
||||
var output bytes.Buffer
|
||||
@@ -50,6 +57,15 @@ func TestSybListTemplate_区分标题图片与阶段动作(t *testing.T) {
|
||||
`href="/tasks?type=purchase`,
|
||||
`q=ORDER-TASK"`,
|
||||
`data-detail-open-id="SYB-PDD"`,
|
||||
`action="/syb/collect-pdd-batch"`,
|
||||
`data-need-checked="collect"`,
|
||||
`data-need-checked="purchase"`,
|
||||
`data-collect-action="collect"`,
|
||||
`data-select-action="collect"`,
|
||||
`form="syb-collect-form"`,
|
||||
`id="syb-collect-modal"`,
|
||||
`同一 PDD 商品关联多条货运单时只创建一条任务`,
|
||||
`title="当前阶段不能批量操作:下一步关联采购商品"`,
|
||||
} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Errorf("顺运宝列表缺少 %q", want)
|
||||
|
||||
@@ -22,7 +22,18 @@
|
||||
|
||||
<button type="button" data-modal-open="sync-history-modal">同步记录</button>
|
||||
|
||||
<button type="button" data-need-checked data-modal-open="purchase-modal" data-purchase-open>创建采购任务</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>
|
||||
@@ -109,8 +120,13 @@
|
||||
{{range .Rows}}
|
||||
<tr {{if .NeedsAttention}}class="row-warn"{{end}}>
|
||||
<td class="col-check">
|
||||
<input type="checkbox" value="{{.SybID}}" name="ids" {{if not .CanPurchase}}disabled{{end}}
|
||||
aria-label="选择货运单明细 {{.SybID}}">
|
||||
<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>
|
||||
@@ -163,6 +179,36 @@
|
||||
</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">
|
||||
|
||||
Reference in New Issue
Block a user