feat(syb): confirm one-click replacement from latest collection #258

This commit is contained in:
QiuSW
2026-09-10 15:47:27 +08:00
parent 305062dba0
commit 2b01974fe5
12 changed files with 445 additions and 34 deletions
+3 -3
View File
@@ -35,9 +35,9 @@
<div class="quick-link-actions">
<el-button v-if="purchaseActionLabel({ nextAction: purchaseReady(row).processNextAction })" type="primary" link @click="runPurchaseNextAction(row, { ...purchaseReady(row), nextAction: purchaseReady(row).processNextAction })">{{ purchaseActionLabel({ nextAction: purchaseReady(row).processNextAction }) }}</el-button>
<el-button-group v-if="quickCanLink(row)">
<el-button size="small" type="primary" plain :loading="quickBusyId === row.id" :disabled="!!quickBusyId" @click="quickStart(row)">一键关联</el-button>
<el-button size="small" type="primary" plain :loading="quickBusyId === row.id" :disabled="!!quickBusyId" @click="quickStart(row)">{{ quickActionLabel(row) }}</el-button>
<el-dropdown trigger="click" @command="quickOpenPicker()">
<el-button size="small" type="primary" plain aria-label="一键关联使用的手机" :icon="ArrowDown" />
<el-button size="small" type="primary" plain :aria-label="`${quickActionLabel(row)}使用的手机`" :icon="ArrowDown" />
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item disabled>当前手机:{{ quickDeviceLabel() }}</el-dropdown-item>
@@ -57,7 +57,7 @@
</el-card>
<el-dialog v-model="quickPicker.open" :title="quickPicker.row ? '选择采集手机' : '切换采集手机'" width="460px" :close-on-click-modal="false" @close="quickCancelPicker">
<p>{{ quickPicker.row ? '选择后继续关联刚才的商品。' : '只修改各行共用的手机,不启动关联,也不影响正在进行的操作。' }}</p>
<p>{{ quickPicker.row ? '选择后继续处理刚才的商品;更换为其他商品前会再次确认。' : '只修改各行共用的手机,不启动关联或替换,也不影响正在进行的操作。' }}</p>
<el-form label-width="80px">
<el-form-item label="采集手机">
<el-select v-model="quickPicker.deviceId" :loading="quickDeviceLoading" placeholder="请选择手机" style="width:100%">
+50 -10
View File
@@ -1,4 +1,4 @@
import { ElMessage } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { listDevices } from '@/api/goauto/devices'
import { getShopeeProduct, linkShopeeProductPdd, autoMatchShopeeSpecMappings } from '@/api/goauto/shopee-products'
import { previewPurchaseTasks } from '@/api/goauto/purchase-tasks'
@@ -20,6 +20,7 @@ export default {
deactivated() { this.quickInvalidate() },
methods: {
quickInvalidate() {
if (this.quickPhase === '等待替换确认') ElMessageBox.close()
this.quickGeneration++
this.quickPickerGeneration++
if (this.quickBusyId) this.quickMessages[this.quickBusyId] = '操作结果待确认,请刷新核对;离开页面不代表服务端已取消'
@@ -29,7 +30,16 @@ export default {
},
quickCanLink(row) {
const ready = this.purchaseReady(row)
return this.canPurchase && !!row.shopeeProductId && ready.processNextAction === 'open_pdd_link' && !ready.pddProductId
return this.canPurchase && !!row.shopeeProductId && ((ready.processNextAction === 'open_pdd_link' && !ready.pddProductId) || this.quickCanReplace(row))
},
quickCanReplace(row) {
const ready = this.purchaseReady(row)
return !!ready.pddProductId && ready.processNextAction === 'open_pdd' && (
['pdd_pending', 'pdd_collection_failed'].includes(ready.processStage) ||
(ready.processStage === 'manual_action' && ['PDD_NOT_READY', 'PDD_PRICE_MISSING'].includes(ready.reasonCode)))
},
quickActionLabel(row) {
return this.quickCanReplace(row) ? '一键替换' : '一键关联'
},
quickDeviceLabel() {
const device = this.quickDevices.find(d => d.id === this.quickDeviceId)
@@ -61,34 +71,60 @@ export default {
async quickStart(row) {
if (!this.quickCanLink(row) || this.quickBusyId) return
if (!this.quickDeviceId) return this.quickOpenPicker(row)
const frozen = { sybProductId: row.id, productId: row.shopeeProductId, deviceId: this.quickDeviceId }
const replacing = this.quickCanReplace(row)
const frozen = { sybProductId: row.id, productId: row.shopeeProductId, deviceId: this.quickDeviceId, oldPddId: this.purchaseReady(row).pddProductId }
const generation = ++this.quickGeneration
const current = () => generation === this.quickGeneration
this.quickBusyId = row.id
this.quickMessages[row.id] = ''
this.quickPhase = '正在关联'
let wrote = false
let targetId = null
try {
const detail = (await getShopeeProduct(frozen.productId, readOptions)).data
if (!current()) return
if (detail.product.pddProductId) throw new Error('该商品已经关联,请查看现有关联')
if (!replacing && detail.product.pddProductId) throw new Error('该商品已经关联,请查看现有关联')
if (replacing && detail.product.pddProductId !== frozen.oldPddId) throw new Error('关联已变化,请刷新列表')
const latestCollection = { sybProductId: frozen.sybProductId, deviceId: frozen.deviceId, specContextVersion: detail.product.specContextVersion }
if (replacing) {
this.quickPhase = '正在读取最近采集'
const preview = (await linkShopeeProductPdd(frozen.productId, {
requestId: createRequestId(), latestCollection: { ...latestCollection, replacement: { preview: true, expectedPddProductId: frozen.oldPddId }}
}, readOptions)).data
if (!current()) return
const source = preview.collectionSource
targetId = source.pddProductId
latestCollection.replacement = { preview: false, expectedPddProductId: frozen.oldPddId, taskId: source.taskId, targetPddProductId: targetId, targetVersion: source.targetVersion }
if (targetId !== frozen.oldPddId) {
this.quickPhase = '等待替换确认'
try {
await ElMessageBox.confirm(`将「${source.previousTitle || '原商品'}(${source.previousGoodsId || frozen.oldPddId})」替换为「${source.title || '最近采集商品'}(${source.goodsId || targetId})」。来源:${source.deviceName || '手机'} #${frozen.deviceId}。共用此蝦皮商品的 SYB 明细也会使用新关联;已有采购任务不变。`, '确认替换 PDD 商品', { confirmButtonText: '确认替换并匹配', cancelButtonText: '取消', closeOnClickModal: false })
} catch {
if (current()) this.quickMessages[row.id] = '已取消,未修改关联'
return
}
if (!current()) return
}
this.quickPhase = targetId === frozen.oldPddId ? '正在核对现有关联' : '正在替换'
}
const linked = (await linkShopeeProductPdd(frozen.productId, {
requestId: createRequestId(),
latestCollection: { sybProductId: frozen.sybProductId, deviceId: frozen.deviceId, specContextVersion: detail.product.specContextVersion }
latestCollection
}, readOptions)).data
wrote = true
if (!current()) return
const ready = (await previewPurchaseTasks({ sybProductIds: [frozen.sybProductId] }, readOptions)).data.items[0]
if (!current()) return
const outcome = replacing && targetId !== frozen.oldPddId ? '已替换' : '已关联'
if (linked.collectionSource.requiresSpecConfirmation || !ready?.aiMatchEligible) {
this.quickMessages[row.id] = ready?.eligible ? '已关联,当前明细可创建采购' : '已关联;请按处理阶段提示继续人工处理'
this.quickMessages[row.id] = ready?.eligible ? `${outcome},当前明细可创建采购` : `${outcome};请按处理阶段提示继续人工处理`
} else {
this.quickPhase = '正在匹配并保存'
const result = (await autoMatchShopeeSpecMappings(frozen.productId, {
requestId: createRequestId(), specContextVersion: linked.product.specContextVersion
}, detail.autoMatchTimeoutSeconds)).data
if (!current()) return
this.quickMessages[row.id] = result.unmatchedCount ? '已关联并保存有效匹配;剩余规格请去匹配处理' : '已关联并保存匹配;以最新处理阶段为准'
this.quickMessages[row.id] = result.unmatchedCount ? `${outcome}并保存有效匹配;剩余规格请去匹配处理` : `${outcome}并保存匹配;以最新处理阶段为准`
}
} catch (error) {
if (!current()) return
@@ -102,9 +138,13 @@ export default {
try {
const saved = (await getShopeeProduct(frozen.productId, readOptions)).data.product
if (!current()) return
this.quickMessages[row.id] = saved.pddProductId
? `当前已有关联,匹配结果请去匹配查看。${reason}`
: `当前未查到关联,操作结果未确认,请刷新核对后再操作。${reason}`
this.quickMessages[row.id] = replacing
? (targetId && saved.pddProductId === targetId
? `当前关联为本次目标商品,匹配结果请去匹配查看。${reason}`
: `当前关联${saved.pddProductId === frozen.oldPddId ? '仍为原商品' : '已变化'};本次结果未确认,请刷新核对,不自动重试。${reason}`)
: saved.pddProductId
? `当前已有关联,匹配结果请去匹配查看。${reason}`
: `当前未查到关联,操作结果未确认,请刷新核对后再操作。${reason}`
} catch {
if (current()) this.quickMessages[row.id] = `${wrote ? '关联已保存;' : ''}结果未确认,请刷新核对,不要连续重复点击。${reason}`
}