style(frontend): 全站背景图 webp 化(bg.jpg 4MB→251KB 压缩 + bg.webp)+品牌工具页默认策略调优(品牌检测默认改精确匹配 Simple、Terms 后置)+任务面板/登录页/亚马逊控制台观感微调

This commit is contained in:
2026-09-09 18:32:48 +08:00
parent 9a487ae9a7
commit 83cb3ff84e
29 changed files with 565 additions and 570 deletions
@@ -143,7 +143,7 @@
</div>
<div v-if="queuePushResult" class="queue-debug-card">
<div class="section-title queue-debug-title">推送结果</div>
<div class="section-title queue-debug-title">启动结果</div>
<div class="queue-debug-line">{{ queuePushResult }}</div>
<pre v-if="queuePayloadText" class="queue-debug-payload">{{ queuePayloadText }}</pre>
</div>
@@ -163,7 +163,7 @@
>
<template #cards-extra>
<div class="subsection-title">匹配结果</div>
<div v-if="!matchedItems.length" class="match-empty">完成匹配店铺在此展示紫鸟索引匹配结果供核对后再推送队列</div>
<div v-if="!matchedItems.length" class="match-empty">完成匹配店铺在此展示匹配结果确认无误后再启动任务</div>
<el-table v-else :data="matchedItems" :row-key="rowKeyForMatch" :highlight-current-row="false"
class="result-table match-table">
<el-table-column prop="shopName" label="店铺名" min-width="100" />
@@ -812,16 +812,16 @@ async function runMatch() {
function formatMatchStatus(status: string | undefined) {
const s = (status || '').trim()
const map: Record<string, string> = { MATCHED: '已匹配', PENDING: '待匹配', CONFLICT: '需人工确认', INDEX_STALE: '索引过期' }
const map: Record<string, string> = { MATCHED: '已匹配', PENDING: '待匹配', CONFLICT: '需人工确认', INDEX_STALE: '匹配已过期' }
return map[s] || (s ? s : '—')
}
function formatMatchRemark(row: PriceTrackShopQueueItem) {
const msg = (row.matchMessage || '').trim()
if (msg) return msg
if (row.matched && row.matchStatus === 'MATCHED') return '紫鸟索引已命中,可推送队列'
if (row.matched) return '已关联索引,请结合状态列查看是否可推送'
return '未命中或未就绪,请检查店铺名与索引刷新'
if (row.matched && row.matchStatus === 'MATCHED') return '已匹配成功,可启动任务'
if (row.matched) return '已关联店铺,请查看状态确认'
return '未匹配成功,请检查店铺名'
}
function rowKeyForMatch(row: { shopName?: string; shopId?: number | string | null }) {
@@ -934,7 +934,7 @@ async function removeMatchedRow(row: PriceTrackShopQueueItem) {
await loadHistory()
await loadDashboard()
syncPollingIdsWithHistory()
ElMessage.success('后端记录已不存在,已同步移除本地记录')
ElMessage.success('记录已不存在,已同步移除本地记录')
return
}
matchedItems.value = backup
@@ -1027,7 +1027,7 @@ async function waitForTaskTerminal(taskId: number) {
try {
const batch = await getPriceTrackTaskProgressBatch([taskId])
if (transientErrorCount > 0) {
queuePushResult.value = `任务 ${taskId} 后端已恢复,继续等待执行结果...`
queuePushResult.value = `任务 ${taskId} 服务已恢复,继续等待执行结果...`
}
transientErrorCount = 0
if ((batch.missingTaskIds || []).includes(taskId)) {
@@ -1057,15 +1057,15 @@ async function waitForTaskTerminal(taskId: number) {
}
} catch (error) {
const message = (error instanceof Error ? error.message : String(error || '')).toLowerCase()
const transient = ['无法连接到后端服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
const transient = ['无法连接到服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
if (!transient) throw error
transientErrorCount += 1
if (transientErrorCount >= maxTransientErrors) {
throw new Error(`任务 ${taskId} 等待后端恢复超时,请稍后手动刷新查看状态`)
throw new Error(`任务 ${taskId} 等待服务恢复超时,请稍后手动刷新查看状态`)
}
queuePushResult.value = `任务 ${taskId} 运行中,正在等待后端服务恢复(${transientErrorCount}/${maxTransientErrors}...`
queuePushResult.value = `任务 ${taskId} 运行中,正在等待服务恢复(${transientErrorCount}/${maxTransientErrors}...`
if (transientErrorCount === 1 || transientErrorCount % 5 === 0) {
ElMessage.warning(`任务 ${taskId} 运行中,后端服务暂时不可用,正在自动重试`)
ElMessage.warning(`任务 ${taskId} 运行中,服务暂时不可用,正在自动重试`)
}
await sleep(getPollIntervalMs())
continue
@@ -1130,10 +1130,10 @@ async function processMatchedQueue() {
})
} catch (error) {
const message = (error instanceof Error ? error.message : String(error || '')).toLowerCase()
const transient = ['无法连接到后端服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
const transient = ['无法连接到服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
attempt += 1
if (!transient || attempt >= 10) throw error
queuePushResult.value = '后端服务暂时不可用,正在重试创建任务(' + attempt + '/10...'
queuePushResult.value = '服务暂时不可用,正在重试创建任务(' + attempt + '/10...'
await sleep(getPollIntervalMs())
}
}
@@ -1157,7 +1157,7 @@ async function processMatchedQueue() {
addPollingTask(taskVo.taskId)
scheduleNextPoll(true)
removeMatchedRowsLocally([row])
queuePushResult.value = '任务 ' + taskVo.taskId + ' 已入队,等待执行完成'
queuePushResult.value = '任务 ' + taskVo.taskId + ' 已提交,等待执行完成'
const finalStatus = await waitForTaskTerminal(taskVo.taskId)
if (finalStatus !== 'SUCCESS') {
failedCount += 1
@@ -1177,11 +1177,11 @@ async function processMatchedQueue() {
}
}
if (successCount > 0 || failedCount > 0) {
ElMessage.success('店铺任务推送已完成:成功 ' + successCount + ' 条,失败 ' + failedCount + ' 条')
ElMessage.success('店铺任务启动已完成:成功 ' + successCount + ' 条,失败 ' + failedCount + ' 条')
}
} catch (e) {
if (disposed) return
queuePushResult.value = e instanceof Error ? e.message : '推送异常'
queuePushResult.value = e instanceof Error ? e.message : '启动异常'
ElMessage.error(queuePushResult.value)
} finally {
queueWorkerRunning.value = false
@@ -1214,7 +1214,7 @@ async function syncActiveLoopRun() {
return activeLoopRun.value
} catch (error) {
const message = (error instanceof Error ? error.message : String(error || '')).toLowerCase()
const transient = ['无法连接到后端服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
const transient = ['无法连接到服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
if (transient) return activeLoopRun.value
activeLoopRun.value = null
activeLoopRunId.value = null
@@ -1285,10 +1285,10 @@ async function runLoopExecution(loopId?: number) {
return await completePriceTrackLoopChild(loop.id, activeTaskId)
} catch (error) {
const message = (error instanceof Error ? error.message : String(error || '')).toLowerCase()
const transient = ['无法连接到后端服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
const transient = ['无法连接到服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
completeAttempt += 1
if (!transient || completeAttempt >= 10) throw error
queuePushResult.value = `循环任务 ${loop.id} 等待后端恢复后确认子任务完成(${completeAttempt}/10...`
queuePushResult.value = `循环任务 ${loop.id} 等待服务恢复后确认子任务完成(${completeAttempt}/10...`
await sleep(getPollIntervalMs())
}
}
@@ -1312,10 +1312,10 @@ async function runLoopExecution(loopId?: number) {
return await dispatchNextPriceTrackLoopRun(loop.id)
} catch (error) {
const message = (error instanceof Error ? error.message : String(error || '')).toLowerCase()
const transient = ['无法连接到后端服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
const transient = ['无法连接到服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
dispatchAttempt += 1
if (!transient || dispatchAttempt >= 10) throw error
queuePushResult.value = `循环任务 ${loop.id} 等待后端恢复后继续派发${dispatchAttempt}/10...`
queuePushResult.value = `循环任务 ${loop.id} 等待服务恢复后继续执行${dispatchAttempt}/10...`
await sleep(getPollIntervalMs())
}
}
@@ -1336,10 +1336,10 @@ async function runLoopExecution(loopId?: number) {
return await createPriceTrackTask(childTaskRequest)
} catch (error) {
const message = (error instanceof Error ? error.message : String(error || '')).toLowerCase()
const transient = ['无法连接到后端服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
const transient = ['无法连接到服务', 'bad gateway', 'gateway timeout', 'network error', 'timeout', '502', '503', '504'].some((pattern) => message.includes(pattern.toLowerCase()))
createAttempt += 1
if (!transient || createAttempt >= 10) throw error
queuePushResult.value = `循环任务 ${loop.id} 等待后端恢复后重试创建子任务(${createAttempt}/10...`
queuePushResult.value = `循环任务 ${loop.id} 等待服务恢复后重试创建子任务(${createAttempt}/10...`
await sleep(getPollIntervalMs())
}
}
@@ -1351,7 +1351,7 @@ async function runLoopExecution(loopId?: number) {
addPollingTask(taskVo.taskId)
scheduleNextPoll(true)
removeMatchedRowsLocally([row])
queuePushResult.value = `循环任务 ${loop.id}${dispatch.childTaskRequest.roundIndex || 1} 轮已推送${row.shopName || taskVo.taskId}`
queuePushResult.value = `循环任务 ${loop.id}${dispatch.childTaskRequest.roundIndex || 1} 轮已启动${row.shopName || taskVo.taskId}`
}
} finally {
loopDispatching.value = false
@@ -1637,7 +1637,7 @@ async function deleteTaskRecord(item: PriceTrackHistoryItem) {
removeHistoryItemLocally(item)
await loadDashboard()
syncPollingIdsWithHistory()
ElMessage.success('后端记录已不存在,已同步移除本地记录')
ElMessage.success('记录已不存在,已同步移除本地记录')
return
}
ElMessage.error(e instanceof Error ? e.message : '删除失败')
@@ -1707,7 +1707,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
<style scoped>
.module-page {
min-height: 100vh;
background: #151a25;
background: #242424;
}
.main-content {
@@ -1718,7 +1718,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
.left-panel {
width: 400px;
background: #1c2333;
background: #242424;
padding: 20px;
overflow-y: auto;
border-right: 1px solid #2e3a52;
@@ -1733,7 +1733,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
display: flex;
flex-direction: column;
min-width: 0;
background: #151a25;
background: #242424;
}
.section-title {
@@ -1746,7 +1746,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
border: 1px dashed #3e4a62;
border-radius: 10px;
padding: 16px;
background: #2e3a52;
background: #242424;
margin-bottom: 20px;
}
@@ -1798,7 +1798,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
border: 1px dashed #3e4a62;
border-radius: 10px;
padding: 14px 16px;
background: #2e3a52;
background: #242424;
margin-bottom: 20px;
display: flex;
flex-direction: column;
@@ -1845,7 +1845,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
}
.loop-round-input :deep(.el-input__wrapper) {
background-color: #2e3a52;
background-color: #242424;
box-shadow: 0 0 0 1px #3e4a62 inset;
}
@@ -1893,7 +1893,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
border: 1px dashed #3e4a62;
border-radius: 10px;
padding: 16px;
background: #2e3a52;
background: #242424;
margin-bottom: 20px;
}
@@ -1911,7 +1911,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
padding: 8px 14px;
font-size: 13px;
color: #c8d2e2;
background: #2e3a52;
background: #242424;
border: 1px solid #3e4a62;
border-radius: 6px;
cursor: pointer;
@@ -1965,7 +1965,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
border: 1px solid #333;
border-radius: 8px;
padding: 10px 12px;
background: #2e3a52;
background: #242424;
margin-bottom: 14px;
}
@@ -2028,7 +2028,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
}
.listing-filter-row :deep(.el-select__wrapper) {
background-color: #2e3a52;
background-color: #242424;
box-shadow: 0 0 0 1px #3e4a62 inset;
}
@@ -2062,8 +2062,8 @@ async function batchDeleteHistory(views: TaskItemView[]) {
}
.candidate-table {
--el-table-bg-color: #2e3a52;
--el-table-tr-bg-color: #2e3a52;
--el-table-bg-color: #242424;
--el-table-tr-bg-color: #242424;
--el-table-header-bg-color: #2e3a52;
--el-table-text-color: #c8d2e2;
--el-table-border-color: #333;
@@ -2205,8 +2205,8 @@ async function batchDeleteHistory(views: TaskItemView[]) {
}
.result-table {
--el-table-bg-color: #222;
--el-table-tr-bg-color: #222;
--el-table-bg-color: #242424;
--el-table-tr-bg-color: #242424;
--el-table-header-bg-color: #2e3a52;
--el-table-text-color: #c8d2e2;
--el-table-border-color: #333;