Compare commits

..
2 Commits
Author SHA1 Message Date
QiuSW 27d9560f0a Merge remote-tracking branch 'origin/feat/338-return-match' into feat/339-syb-page-size 2026-09-24 16:27:43 +08:00
QiuSWandClaude Opus 5.5 b72fa105a8 feat(web): flag matched returns past their destroy deadline (#338)
A match whose return has passed its destroy deadline keeps blocking
purchase (user decision: remind, don't auto-cancel). Show 「退货已过销毁截止」
in the SYB products match column and compare dialog, and 「已过销毁截止」
under the occupying SYB product on the yeeke returns page. Uses the live
package deadline, so a resync that moves the deadline updates the flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-24 16:27:38 +08:00
2 changed files with 8 additions and 0 deletions
@@ -63,6 +63,7 @@
</div> </div>
</div> </div>
<el-alert v-if="returnMatchByProductId[row.id].syncStatus === 'missing'" title="退货已不在 yeeke 列表" type="error" :closable="false" show-icon class="return-missing-alert" /> <el-alert v-if="returnMatchByProductId[row.id].syncStatus === 'missing'" title="退货已不在 yeeke 列表" type="error" :closable="false" show-icon class="return-missing-alert" />
<el-alert v-if="isDeadlinePassed(returnMatchByProductId[row.id].destroyDeadline)" title="退货已过销毁截止" type="error" :closable="false" show-icon class="return-missing-alert" />
<div class="quick-link-actions"> <div class="quick-link-actions">
<el-button type="primary" link @click="openMatchDetail(returnMatchByProductId[row.id].id)">查看对比</el-button> <el-button type="primary" link @click="openMatchDetail(returnMatchByProductId[row.id].id)">查看对比</el-button>
<el-button v-if="canPurchase" type="primary" link @click="openMatchDetail(returnMatchByProductId[row.id].id, true)">备注</el-button> <el-button v-if="canPurchase" type="primary" link @click="openMatchDetail(returnMatchByProductId[row.id].id, true)">备注</el-button>
@@ -223,6 +224,7 @@
<div class="split-col"> <div class="split-col">
<h3 class="section-title">yeeke 退货商品</h3> <h3 class="section-title">yeeke 退货商品</h3>
<el-alert v-if="matchDetail.data.yeeke && matchDetail.data.yeeke.syncStatus === 'missing'" title="退货已不在 yeeke 列表" type="error" :closable="false" show-icon class="notice" /> <el-alert v-if="matchDetail.data.yeeke && matchDetail.data.yeeke.syncStatus === 'missing'" title="退货已不在 yeeke 列表" type="error" :closable="false" show-icon class="notice" />
<el-alert v-if="matchDetail.data.yeeke && isDeadlinePassed(matchDetail.data.yeeke.destroyDeadline)" title="退货已过销毁截止,请核实退货是否仍在库" type="error" :closable="false" show-icon class="notice" />
<el-descriptions :column="1" border size="small" v-if="matchDetail.data.yeeke"> <el-descriptions :column="1" border size="small" v-if="matchDetail.data.yeeke">
<el-descriptions-item label="退货订单号">{{ matchDetail.data.yeeke.orderSn }}</el-descriptions-item> <el-descriptions-item label="退货订单号">{{ matchDetail.data.yeeke.orderSn }}</el-descriptions-item>
<el-descriptions-item label="商品ID">{{ matchDetail.data.yeeke.itemId }}</el-descriptions-item> <el-descriptions-item label="商品ID">{{ matchDetail.data.yeeke.itemId }}</el-descriptions-item>
@@ -573,6 +575,8 @@ export default {
// ---------------- 退货匹配 (#338) ---------------- // ---------------- 退货匹配 (#338) ----------------
formatMatchDeadline(value) { return value ? new Date(value).toLocaleString() : '—' }, formatMatchDeadline(value) { return value ? new Date(value).toLocaleString() : '—' },
// #338: an already-matched return whose destroy deadline has passed is only flagged, never auto-cancelled.
isDeadlinePassed(value) { return Boolean(value) && new Date(value).getTime() <= Date.now() },
matchStatusLabel(status) { return { matched: '待确认', confirmed: '已确认', cancelled: '已取消' }[status] || status }, matchStatusLabel(status) { return { matched: '待确认', confirmed: '已确认', cancelled: '已取消' }[status] || status },
async loadReturnMatches(ids, generation = this.loadGeneration) { async loadReturnMatches(ids, generation = this.loadGeneration) {
this.returnMatchByProductId = {} this.returnMatchByProductId = {}
@@ -83,6 +83,7 @@
<template v-if="row.occupyingSybProductId"> <template v-if="row.occupyingSybProductId">
<a class="link" href="javascript:void(0)" @click="openSybProduct(row.occupyingSybProductId)">{{ row.occupyingSybOrderCode }} ↗</a> <a class="link" href="javascript:void(0)" @click="openSybProduct(row.occupyingSybProductId)">{{ row.occupyingSybOrderCode }} ↗</a>
<div class="muted">{{ row.occupyingSybStageLabel }}</div> <div class="muted">{{ row.occupyingSybStageLabel }}</div>
<div v-if="isDeadlinePassed(row.destroyDeadLine)" class="deadline-passed">已过销毁截止</div>
</template> </template>
<span v-else class="muted">—</span> <span v-else class="muted">—</span>
</template> </template>
@@ -193,6 +194,8 @@ export default {
openSybProduct(sybProductId) { this.$router.push({ path: '/syb-products/index', query: { sybProductId } }) }, openSybProduct(sybProductId) { this.$router.push({ path: '/syb-products/index', query: { sybProductId } }) },
statusMeta(status) { return { running: { label: '执行中', type: 'primary' }, succeeded: { label: '成功', type: 'success' }, failed: { label: '失败', type: 'danger' }, interrupted: { label: '已中断', type: 'warning' }}[status] || { label: status || '-', type: 'info' } }, statusMeta(status) { return { running: { label: '执行中', type: 'primary' }, succeeded: { label: '成功', type: 'success' }, failed: { label: '失败', type: 'danger' }, interrupted: { label: '已中断', type: 'warning' }}[status] || { label: status || '-', type: 'info' } },
formatTime(value) { if (!value) return '—'; return new Date(value).toLocaleString('zh-CN', { hour12: false }) }, formatTime(value) { if (!value) return '—'; return new Date(value).toLocaleString('zh-CN', { hour12: false }) },
// #338: a matched return past its destroy deadline is flagged only, never auto-released.
isDeadlinePassed(value) { return Boolean(value) && new Date(value).getTime() <= Date.now() },
async load() { async load() {
this.loading = true; this.loadError = '' this.loading = true; this.loadError = ''
try { try {
@@ -277,4 +280,5 @@ export default {
.item-title{font-size:15px;font-weight:600;color:#1f2937;margin-bottom:4px} .item-title{font-size:15px;font-weight:600;color:#1f2937;margin-bottom:4px}
.muted{font-size:12px;color:#909399} .muted{font-size:12px;color:#909399}
@media(max-width:800px){.page-heading{flex-direction:column}} @media(max-width:800px){.page-heading{flex-direction:column}}
.deadline-passed{color:var(--el-color-danger);font-size:12px}
</style> </style>