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
@@ -6,7 +6,7 @@
<aside class="left-panel left-panel--with-template">
<div class="section-title">选择文件</div>
<div class="upload-zone">
<div class="hint">上传删除品牌 Excel无论是直接上传文件还是上传文件夹批量导入都会按每个 Excel 文件名作为店铺名</div>
<div class="hint">上传删除品牌 Excel直接上传文件上传文件夹批量导入都可以每个 Excel 文件名作为店铺名</div>
<div class="btns">
<button type="button" class="opt-btn" @click="selectFiles">选择 Excel 文件</button>
<button type="button" class="opt-btn" @click="selectFolder">选择文件夹</button>
@@ -299,7 +299,7 @@ const historySectionItems = computed(() =>
),
);
const queueRunnableItems = computed(() =>
currentSectionItems.value.filter((item) => canPushToQueue(item) && getQueueStatus(item) === '未入队'),
currentSectionItems.value.filter((item) => canPushToQueue(item) && getQueueStatus(item) === '未启动'),
)
const deleteBrandCards = computed<TaskStatCard[]>(() => {
@@ -477,7 +477,7 @@ function shouldShowProgress(item: DeleteBrandResultItem) {
const isProgressTarget = Boolean(progress?.has_progress && progress?.info?.file_name === item.sourceFilename)
// 多文件任务下,展示规则更宽松:
// 1) 当前活跃文件;2) 后端进度指向的文件;3) 已推送文件;4) 已完成文件(任务未终态时保留)。
// 1) 当前活跃文件;2) 后端进度指向的文件;3) 已启动文件;4) 已完成文件(任务未终态时保留)。
if (isActiveItem || isProgressTarget || sessionItem?._pushed) {
return true
}
@@ -497,7 +497,7 @@ function shouldShowProgress(item: DeleteBrandResultItem) {
return true
}
return qs === '已入队' || qs === '处理中'
return qs === '已提交' || qs === '处理中'
}
@@ -550,7 +550,7 @@ function formatDateTime(value?: string) {
function formatMatchResult(item: DeleteBrandResultItem) {
if (item.matchStatus === 'INDEX_STALE' && isUsableMatchedItem(item)) {
return `已匹配 ${item.shopId || ''}索引过期,可继续推送`.trim()
return `已匹配 ${item.shopId || ''}匹配信息已过期,可继续启动`.trim()
}
if (item.matchStatus === 'MATCHED' || item.matched) {
return `已匹配 ${item.shopId || ''}`.trim()
@@ -562,7 +562,7 @@ function formatMatchResult(item: DeleteBrandResultItem) {
return '存在多个同名店铺,请人工确认'
}
if (item.matchStatus === 'PENDING') {
return '店铺索引暂未就绪'
return '店铺尚未匹配完成'
}
if (item.matchStatus) {
return item.matchStatus
@@ -774,9 +774,9 @@ function getQueueStatus(item: DeleteBrandResultItem) {
return '处理中'
}
// 多文件:未推送且未完成,才视为未入队
// 多文件:未推送且未完成,才视为未启动
if (multiFileTask && !sessionItem?._pushed && !sessionItem?._completed) {
return '未入队'
return '未启动'
}
if (multiFileTask && status === 'SUCCESS') {
@@ -794,10 +794,10 @@ function getQueueStatus(item: DeleteBrandResultItem) {
}
if (sessionItem?._pushed) {
return '已入队'
return '已提交'
}
return '未入队'
return '未启动'
}
function canDownloadTaskResult(item: DeleteBrandResultItem) {
@@ -902,7 +902,7 @@ async function refreshTaskDetails(taskIds?: number[]) {
try {
const batch = await getDeleteBrandTaskProgress(ids)
if (waitingForBackendRecovery.value && chainStarted.value) {
queuePushResult.value = '后端已恢复,继续执行当前任务并自动衔接后续任务...'
queuePushResult.value = '服务已恢复,继续执行当前任务并自动衔接后续任务...'
}
waitingForBackendRecovery.value = false
let changed = false
@@ -923,10 +923,10 @@ async function refreshTaskDetails(taskIds?: 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 && chainStarted.value) {
waitingForBackendRecovery.value = true
queuePushResult.value = '当前任务运行中,正在等待后端服务恢复...'
queuePushResult.value = '当前任务运行中,正在等待服务恢复...'
}
}
}
@@ -1123,8 +1123,8 @@ async function submitRun() {
const hasBlockedItems = normalizedItems.some((item) => !isUsableMatchedItem(item))
queuePushResult.value = hasRunnableItems
? '解析完成,可在左侧点击“启动任务”开始串行处理'
: '解析完成,当前没有可推送的文件'
? '解析完成,可在左侧点击“启动任务”开始依次处理'
: '解析完成,当前没有可启动的文件'
queuePayloadText.value = ''
if (normalizedItems.length && normalizedItems[0].taskId) {
@@ -1133,13 +1133,13 @@ async function submitRun() {
syncResultState()
if (hasBlockedItems) {
ElMessage.warning('部分文件尚未命中可用店铺索引,已保留状态信息,请等待后台刷新后重试。')
ElMessage.warning('部分文件尚未匹配到店铺,已保留状态信息,请后重试。')
} else if (hasStaleMatchedItems) {
ElMessage.warning('部分文件命中的是过期索引,仍可启动任务;后台刷新后会自动重新匹配。')
ElMessage.warning('部分文件匹配到的店铺信息已过期,仍可启动任务;后台会自动重新匹配。')
} else if (hasRunnableItems) {
ElMessage.success('删除品牌解析完成,请在左侧启动任务')
} else {
ElMessage.warning('当前没有可推送的文件,请先等待店铺索引刷新。')
ElMessage.warning('当前没有可启动的文件,请先等待店铺匹配完成。')
}
// 更新历史记录,以展示那些未进入队列的失败项
@@ -1183,7 +1183,7 @@ async function loadHistory() {
ensurePolling(true)
} catch (err) {
const message = (err instanceof Error ? err.message : String(err || '')).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) {
console.error('loadHistory error:', err)
}
@@ -1263,7 +1263,7 @@ function isPythonQueueBusy() {
for (const task of sessionTasks.value) {
for (const item of task.items) {
const qs = getQueueStatus(item)
if (qs === '已入队' || qs === '处理中') {
if (qs === '已提交' || qs === '处理中') {
return true
}
}
@@ -1302,7 +1302,7 @@ function findNextAutoRunnableItem() {
if (getItemKey(item) === activeItemKey.value) return false
const sessionItem = findSessionItem(item.taskId, item)
if (sessionItem?._completed) return false
return getQueueStatus(item) === '未入队'
return getQueueStatus(item) === '未启动'
})
}
@@ -1325,7 +1325,7 @@ async function runItem(item: DeleteBrandResultItem, options?: { auto?: boolean }
if (!sessionTask) return { success: false, retryable: false }
if (!api?.enqueue_json) {
ElMessage.error('当前环境未启用 pywebview enqueue_json(浏览器环境不会推送')
ElMessage.error('当前环境未启用任务队列能力(浏览器预览环境不会启动')
return { success: false, retryable: false }
}
@@ -1346,14 +1346,14 @@ async function runItem(item: DeleteBrandResultItem, options?: { auto?: boolean }
nonEmptyArrayKeys: ['items'],
})
if (!(await passGuard(guard))) {
queuePushResult.value = `文件 ${item.sourceFilename || taskId} 数据校验未通过,已阻止推送`
queuePushResult.value = `文件 ${item.sourceFilename || taskId} 数据校验未通过,已阻止启动`
saveSessionTask(taskId, sessionTask.items, queuePushResult.value, queuePayloadText.value)
return { success: false, retryable: false }
}
const pushResult = await api.enqueue_json(payload)
let qpr = ''
if (pushResult?.success) {
qpr = `推送文件 ${item.sourceFilename}当前队列长度${pushResult.queue_size ?? '-'}`
qpr = `启动文件 ${item.sourceFilename}排队中的任务数${pushResult.queue_size ?? '-'}`
const sessionItem = findSessionItem(taskId, item)
if (sessionItem) {
@@ -1381,7 +1381,7 @@ async function runItem(item: DeleteBrandResultItem, options?: { auto?: boolean }
async function pushToPythonQueue() {
const nextItem = findNextAutoRunnableItem()
if (!nextItem) {
ElMessage.warning('当前没有可推送的文件')
ElMessage.warning('当前没有可启动的文件')
return
}
@@ -1426,7 +1426,7 @@ async function maybeAutoAdvance() {
})
}
const status = getQueueStatus(currentItem)
if (status === '已入队' || status === '处理中') {
if (status === '已提交' || status === '处理中') {
debugAutoAdvance('skip: current item still running', {
currentKey,
status,
@@ -1581,7 +1581,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
.module-page {
min-height: 100vh;
background: #151a25;
background: #242424;
}
.main-content {
@@ -1592,7 +1592,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
.left-panel {
width: 380px;
background: #1c2333;
background: #242424;
padding: 20px;
overflow-y: auto;
border-right: 1px solid #2e3a52;
@@ -1603,7 +1603,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
display: flex;
flex-direction: column;
min-width: 0;
background: #151a25;
background: #242424;
}
.section-title {
@@ -1617,14 +1617,14 @@ async function batchDeleteHistory(views: TaskItemView[]) {
border-radius: 10px;
padding: 24px;
text-align: center;
background: #2e3a52;
background: #242424;
margin-bottom: 20px;
transition: all 0.2s ease;
}
.upload-zone:hover {
border-color: #3498db;
background: #2e3a52;
background: #242424;
}
.hint {
@@ -1654,7 +1654,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
padding: 8px 16px;
font-size: 13px;
color: #c8d2e2;
background: #2e3a52;
background: #242424;
border: 1px solid #3e4a62;
border-radius: 6px;
}
@@ -1701,7 +1701,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
cursor: pointer;
padding: 10px 12px;
border-radius: 8px;
background: #2e3a52;
background: #242424;
border: 1px solid #2e3a52;
margin-bottom: 8px;
transition: all 0.2s ease;
@@ -1709,7 +1709,7 @@ async function batchDeleteHistory(views: TaskItemView[]) {
.radio-item:hover,
.radio-item.active {
background: #2e3a52;
background: #242424;
border-color: #3e4a62;
}