fix(web): decouple SYB sync feedback (#77)

This commit is contained in:
QiuSW
2026-08-24 16:49:23 +08:00
parent 791f90c6c5
commit 3261561940
7 changed files with 43 additions and 96 deletions
+1 -1
View File
@@ -154,7 +154,7 @@ export const constantRoutes = [
path: 'index',
component: () => import('@/views/goauto/syb-sync-runs/index'),
name: 'GoAutoSybSyncRuns',
meta: { title: '同步记录', icon: 'time' }
meta: { title: 'SYB 同步记录', icon: 'time' }
}
]
},
+2 -45
View File
@@ -13,15 +13,6 @@
<el-form-item label="解析状态"><el-select v-model="query.parseStatus" clearable placeholder="全部" style="width: 110px"><el-option label="成功" value="success" /><el-option label="存疑" value="uncertain" /><el-option label="失败" value="failed" /></el-select></el-form-item>
<el-form-item class="toolbar-actions"><el-button type="primary" :icon="Search" @click="search">查询</el-button><el-button :icon="RefreshLeft" @click="reset">重置</el-button><el-button v-if="canPurchase" type="primary" :disabled="selectedProducts.length === 0" @click="openPurchaseBatch">创建采购</el-button></el-form-item>
</el-form>
<div v-if="activeRun" class="sync-banner" :class="`is-${activeRun.status}`" aria-live="polite">
<div>
<strong>{{ syncStatus(activeRun.status).label }}</strong>
<span>{{ activeRun.dateFrom }} 至 {{ activeRun.dateTo }}</span>
<span v-if="activeRun.status === 'running'">已处理 {{ activeRun.daysProcessed }}/{{ activeRun.daysTotal }} 天,{{ activeRun.detailCount }} 条商品明细</span>
<span v-else-if="activeRun.errorMessage">{{ activeRun.errorMessage }}</span>
</div>
<el-button type="primary" link @click="$router.push({ path: '/syb-sync-runs/index', query: { runId: activeRun.id }})">查看详情</el-button>
</div>
<el-alert v-if="canPurchase" title="表头全选只选择当前页中可以创建采购任务的商品;不可创建的原因显示在“采购准备”列。" type="info" :closable="false" show-icon class="notice compact-notice" />
<el-table ref="productTable" v-loading="loading" :data="products" row-key="id" border stripe empty-text="暂无 SYB 商品明细" @selection-change="handleSelectionChange">
<el-table-column v-if="canPurchase" type="selection" width="48" :selectable="rowSelectable" />
@@ -127,7 +118,7 @@
<script>
import { ElMessage } from 'element-plus'
import { Search, RefreshLeft } from '@element-plus/icons-vue'
import { listSybProducts, getSybProduct, reparseSybProduct, correctSybProduct, listSybSyncRuns, getSybSyncRun } from '@/api/goauto/syb-products'
import { listSybProducts, getSybProduct, reparseSybProduct, correctSybProduct } from '@/api/goauto/syb-products'
import { listSybShops } from '@/api/goauto/syb-shops'
import { listDevices } from '@/api/goauto/devices'
import { createPurchaseTasksBatch, previewPurchaseTasks } from '@/api/goauto/purchase-tasks'
@@ -144,7 +135,6 @@ export default {
purchaseDialog: { open: false, loading: false, saving: false, ids: [], deviceId: null, items: [], eligibleCount: 0, skippedCount: 0 },
purchaseResult: { open: false, items: [], createdCount: 0, failedCount: 0 },
query: { page: 1, pageSize: 20, shopName: '', orderCodesText: '', parseStatus: '' },
activeRun: null, syncPollTimer: null,
detail: { open: false, loading: false, item: null },
correct: { open: false, saving: false, productId: null, targetColor: '', targetSize: '' }
}
@@ -157,8 +147,7 @@ export default {
try { return JSON.stringify(JSON.parse(this.detail.item.rawJson), null, 2) } catch { return this.detail.item.rawJson }
}
},
created() { this.load(); this.loadLatestSyncRun() },
beforeUnmount() { this.stopSyncPolling() },
created() { this.load() },
methods: {
async load(allowNetworkRetry = true) {
const orderCodes = this.normalizedOrderCodes()
@@ -234,7 +223,6 @@ export default {
},
priceText(row) { if (row.unitPriceCent === null || row.unitPriceCent === undefined) return '—'; return `¥${(row.unitPriceCent / 100).toFixed(2)}` },
parseMeta(status) { return { success: { label: '成功', type: 'success' }, uncertain: { label: '存疑', type: 'warning' }, failed: { label: '失败', type: 'danger' }}[status] || { label: status || '-', type: 'info' } },
syncStatus(status) { return { running: { label: '正在同步' }, succeeded: { label: '同步完成' }, failed: { label: '同步失败' }, interrupted: { label: '同步已中断' }}[status] || { label: status || '未知状态' } },
openShopeeDetail(shopeeProductId, targetColor = '') {
if (!shopeeProductId) { ElMessage.warning('该明细尚未关联虾皮商品'); return }
const query = { productId: shopeeProductId }
@@ -286,36 +274,6 @@ export default {
} finally { this.purchaseDialog.saving = false }
},
// ---------------- SYB 自动同步状态 ----------------
async loadLatestSyncRun() {
try {
const response = await listSybSyncRuns({ page: 1, pageSize: 1 }, { suppressNetworkError: true, suppressErrorMessage: true })
const latest = Array.isArray(response.data?.items) ? response.data.items[0] : null
if (!latest) return
this.activeRun = latest
if (latest.status === 'running') this.startSyncPolling(latest.id)
} catch {
// 同步状态是辅助信息,失败不能阻断商品列表。
}
},
startSyncPolling(runId) {
this.stopSyncPolling()
const poll = async() => {
try {
const r = await getSybSyncRun(runId)
this.activeRun = r.data.item
if (this.activeRun.status !== 'running') {
this.stopSyncPolling()
await this.load()
ElMessage[this.activeRun.status === 'succeeded' ? 'success' : 'warning'](this.syncStatus(this.activeRun.status).label)
}
} catch { this.stopSyncPolling() }
}
poll()
this.syncPollTimer = window.setInterval(poll, 3000)
},
stopSyncPolling() { if (this.syncPollTimer) { window.clearInterval(this.syncPollTimer); this.syncPollTimer = null } },
// ---------------- 详情 ----------------
async openDetail(id) {
this.detail = { open: true, loading: true, item: null }
@@ -369,7 +327,6 @@ export default {
.toolbar-actions :deep(.el-button + .el-button){margin-left:0}
.selection-count{display:inline-flex;align-items:center;min-height:32px;white-space:nowrap;color:#606266}
.selection-count strong{margin:0 4px;font-variant-numeric:tabular-nums;color:#1e40af}
.sync-banner{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:12px 16px;margin-bottom:12px;border:1px solid #bfdbfe;border-radius:8px;background:#eff6ff}.sync-banner>div{display:flex;flex-wrap:wrap;gap:8px 16px}.sync-banner.is-failed,.sync-banner.is-interrupted{border-color:#fecaca;background:#fef2f2}.sync-banner.is-succeeded{border-color:#bbf7d0;background:#f0fdf4}
.primary{font-weight:600;color:#1f2937}.muted{font-size:12px;color:#909399}
.missing{color:#b91c1c}
.thumb{width:44px;height:44px;border-radius:4px;object-fit:cover}.thumb.placeholder{display:flex;align-items:center;justify-content:center;background:#f1f5f9;color:#909399;font-size:11px}
+1 -2
View File
@@ -3,8 +3,7 @@
<template #wrapper>
<el-card class="page-card" shadow="never">
<div class="page-heading">
<div><h1>同步记录</h1><p>查看每次 SYB 后台导入的进度、结果和按店铺统计。记录只读,失败后可回到 SYB 商品页重新导入。</p></div>
<el-button type="primary" @click="$router.push('/syb-products/index')">返回 SYB 商品</el-button>
<div><h1>SYB 同步记录</h1><p>查看每次 SYB 同步的进度、结果和按店铺统计。自动同步失败后,系统会在下个调度周期再次尝试。</p></div>
</div>
<el-form :model="query" :inline="true" class="search-form" @submit.prevent="search">
<el-form-item label="状态"><el-select v-model="query.status" clearable placeholder="全部状态" style="width:150px"><el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item>
+8 -1
View File
@@ -107,7 +107,7 @@
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="290">
<template #default="scope">
<el-button v-permisaction="['job:sysJob:edit']" type="primary" link size="small" :icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
<template v-if="scope.row.entry_id!==0 && scope.row.status!=1">
@@ -118,6 +118,10 @@
<el-divider direction="vertical" />
<el-button v-permisaction="['job:sysJob:start']" type="primary" link size="small" @click="handleStart(scope.row)">启动</el-button>
</template>
<template v-if="scope.row.invokeTarget === 'GoAutoSYBHourlySync'">
<el-divider direction="vertical" />
<el-button type="primary" link size="small" @click="handleSybSyncRuns">执行记录</el-button>
</template>
<el-divider direction="vertical" />
<el-button v-permisaction="['job:sysJob:remove']" type="danger" link size="small" :icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
</template>
@@ -497,6 +501,9 @@ export default {
},
handleLog() {
this.$router.push({ name: 'job_log', params: { }})
},
handleSybSyncRuns() {
this.$router.push('/syb-sync-runs/index')
}
}
}