feat(web): 前端 SPA 化 + 工具页任务面板统一与历史批量删除
- SPA 化:22 个 MPA html 入口与 *-main.ts 合并为 index.html + vue-router(URL 无 .html 后缀), 页面跳转全部 router-link,/new_web_source/xxx.html 旧路径归一为 /xxx - 任务面板统一:共享 TaskCenterPanel/TaskItemCard/TaskStatCards/HistoryTaskLayer, 16 个工具页右侧统一为统计卡 + 当前任务 + 历史任务弹层(任务ID/开始/结束/状态必展示) - 历史记录支持单条删除 + 批量勾选删除(确认框/全选/失败提示) - Java 7 模块(dedupe/convert/split/productrisk/shopmatch/pricetrack/deletebrand) history 接口补齐任务时间字段(VO+Service,复用 biz_file_task 列) - 图片工作台/API 层(brand/permission/user)既有未提交改动一并提交
This commit is contained in:
@@ -58,7 +58,7 @@
|
||||
</button>
|
||||
<button type="button" class="btn-run btn-queue" :disabled="pushing || !queueRunnableItems.length"
|
||||
@click="pushToPythonQueue">
|
||||
{{ pushing ? '推送中...' : '推送到 Python 队列' }}
|
||||
{{ pushing ? '启动中...' : '启动任务' }}
|
||||
</button>
|
||||
<span class="loading-msg">
|
||||
{{ running ? '正在读取删除品牌数据,请稍候…' : '解析后会在右侧展示按国家分组的去重结果' }}
|
||||
@@ -66,7 +66,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="queuePushResult || queuePayloadText" class="queue-debug-card">
|
||||
<div class="section-title queue-debug-title">Python 队列推送结果</div>
|
||||
<div class="section-title queue-debug-title">任务启动结果</div>
|
||||
<div v-if="queuePushResult" class="queue-debug-line">
|
||||
{{ queuePushResult }}
|
||||
</div>
|
||||
@@ -77,135 +77,29 @@
|
||||
</aside>
|
||||
|
||||
<section class="right-panel">
|
||||
<div class="panel-header">删除品牌结果</div>
|
||||
<div class="task-list-wrap">
|
||||
<div class="clean-result-summary">
|
||||
<div class="summary-card">
|
||||
<span class="summary-label">已处理文件</span>
|
||||
<strong>{{ summary.total }}</strong>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<span class="summary-label">成功结果</span>
|
||||
<strong>{{ summary.successCount }}</strong>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<span class="summary-label">失败文件</span>
|
||||
<strong>{{ summary.failedCount }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-list-wrap">
|
||||
<div class="result-list-header">
|
||||
<span>删除品牌结果列表</span>
|
||||
</div>
|
||||
|
||||
<div v-if="!hasVisibleItems" class="empty-tasks">
|
||||
暂无删除品牌结果,完成解析后会在这里展示按国家分组的去重结果
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<div v-if="currentSectionItems.length" class="result-subsection">
|
||||
<div class="result-subsection-title">当前任务</div>
|
||||
<ul class="task-list clean-result-list">
|
||||
<li v-for="item in currentSectionItems"
|
||||
:key="`current-${item.taskId || item.resultId || item.sourceFilename}`"
|
||||
class="task-item split-result-item">
|
||||
<div class="left split-result-main">
|
||||
<span class="id" :title="item.sourceFilename">{{ item.sourceFilename || '-' }}</span>
|
||||
<div class="files">店铺名:{{ item.shopName || '-' }}</div>
|
||||
<div class="files">匹配结果:{{ formatMatchResult(item) }}</div>
|
||||
<div v-if="item.platform" class="files">平台:{{ item.platform }}</div>
|
||||
<div v-if="getQueueStatus(item)" class="files">队列状态:{{ getQueueStatus(item) }}</div>
|
||||
<div v-if="item.countryCount !== undefined && item.matched" class="files">国家数:{{ item.countryCount
|
||||
}}</div>
|
||||
<div v-if="item.totalRows !== undefined && item.matched" class="time">去重后 {{ item.totalRows }} 条
|
||||
</div>
|
||||
<div v-if="shouldShowProgress(item)" class="delete-brand-progress-block">
|
||||
<div class="delete-brand-progress-header">
|
||||
<span>任务进度</span>
|
||||
<span>{{ formatProgressPercent(item) }}%</span>
|
||||
</div>
|
||||
<div class="delete-brand-progress-bar">
|
||||
<div class="delete-brand-progress-bar-fill"
|
||||
:style="{ width: `${formatProgressPercent(item)}%` }"></div>
|
||||
</div>
|
||||
<div class="files delete-brand-progress">{{ formatProgress(item) }}</div>
|
||||
</div>
|
||||
<div v-if="item.previewRows?.length" class="files split-entry-list delete-brand-preview">
|
||||
{{ formatPreview(item.previewRows) }}
|
||||
</div>
|
||||
<div v-if="getDisplayError(item)" class="files">错误信息:{{ getDisplayError(item) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="task-right split-result-actions">
|
||||
<span class="status" :class="getTaskStatusInfo(item).className">
|
||||
{{ getTaskStatusInfo(item).text }}
|
||||
</span>
|
||||
<button v-if="canDownloadTaskResult(item)" type="button" class="download"
|
||||
@click="downloadTaskResult(item)">
|
||||
下载
|
||||
</button>
|
||||
<button v-if="item.resultId" type="button" class="btn-delete"
|
||||
@click="deleteHistoryRecord(item.resultId)">
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="historySectionItems.length" class="result-subsection">
|
||||
<div class="result-subsection-title">历史记录</div>
|
||||
<ul class="task-list clean-result-list">
|
||||
<li v-for="item in historySectionItems"
|
||||
:key="`history-${item.taskId || item.resultId || item.sourceFilename}`"
|
||||
class="task-item split-result-item">
|
||||
<div class="left split-result-main">
|
||||
<span class="id" :title="item.sourceFilename">{{ item.sourceFilename || '-' }}</span>
|
||||
<div class="files">店铺名:{{ item.shopName || '-' }}</div>
|
||||
<div class="files">匹配结果:{{ formatMatchResult(item) }}</div>
|
||||
<div v-if="item.platform" class="files">平台:{{ item.platform }}</div>
|
||||
<div v-if="item.countryCount !== undefined && item.matched" class="files">国家数:{{ item.countryCount
|
||||
}}</div>
|
||||
<div v-if="item.totalRows !== undefined && item.matched" class="time">去重后 {{ item.totalRows }} 条
|
||||
</div>
|
||||
<div v-if="shouldShowProgress(item)" class="delete-brand-progress-block">
|
||||
<div class="delete-brand-progress-header">
|
||||
<span>任务进度</span>
|
||||
<span>{{ formatProgressPercent(item) }}%</span>
|
||||
</div>
|
||||
<div class="delete-brand-progress-bar">
|
||||
<div class="delete-brand-progress-bar-fill"
|
||||
:style="{ width: `${formatProgressPercent(item)}%` }"></div>
|
||||
</div>
|
||||
<div class="files delete-brand-progress">{{ formatProgress(item) }}</div>
|
||||
</div>
|
||||
<div v-if="item.previewRows?.length" class="files split-entry-list delete-brand-preview">
|
||||
{{ formatPreview(item.previewRows) }}
|
||||
</div>
|
||||
<div v-if="getDisplayError(item)" class="files">错误信息:{{ getDisplayError(item) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="task-right split-result-actions">
|
||||
<span class="status" :class="getTaskStatusInfo(item).className">
|
||||
{{ getTaskStatusInfo(item).text }}
|
||||
</span>
|
||||
<!-- 历史记录区不再显示打开紫鸟按钮 -->
|
||||
<button v-if="canDownloadTaskResult(item)" type="button" class="download"
|
||||
@click="downloadTaskResult(item)">
|
||||
下载
|
||||
</button>
|
||||
<button v-if="item.resultId" type="button" class="btn-delete"
|
||||
@click="deleteHistoryRecord(item.resultId)">
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<TaskCenterPanel
|
||||
:on-batch-delete="batchDeleteHistory"
|
||||
title="删除品牌结果"
|
||||
:cards="deleteBrandCards"
|
||||
:current-items="currentTaskViews"
|
||||
:history-items="historyTaskViews"
|
||||
current-title="当前任务"
|
||||
current-empty-text="暂无删除品牌结果,完成解析后会在这里展示按国家分组的去重结果"
|
||||
history-empty-text="暂无历史记录"
|
||||
>
|
||||
<template #item-actions="{ item }">
|
||||
<button v-if="canDownloadTaskResult(itemSource(item))" type="button" class="download"
|
||||
@click="downloadTaskResult(itemSource(item))">下载</button>
|
||||
<button v-if="itemSource(item).resultId" type="button" class="btn-delete"
|
||||
@click="deleteHistoryRecord(itemSource(item).resultId!)">删除</button>
|
||||
</template>
|
||||
<template #history-item-actions="{ item }">
|
||||
<button v-if="canDownloadTaskResult(itemSource(item))" type="button" class="download"
|
||||
@click="downloadTaskResult(itemSource(item))">下载</button>
|
||||
<button v-if="itemSource(item).resultId" type="button" class="btn-delete"
|
||||
@click="deleteHistoryRecord(itemSource(item).resultId!)">删除</button>
|
||||
</template>
|
||||
</TaskCenterPanel>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
@@ -217,6 +111,8 @@ import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import ModuleTemplateDownload from '@/shared/components/ModuleTemplateDownload.vue'
|
||||
import TaskCenterPanel from '@/shared/components/tasks/TaskCenterPanel.vue'
|
||||
import type { TaskItemView, TaskStatCard } from '@/shared/components/tasks/types'
|
||||
import ZiniaoVersionSetting from '@/shared/components/ZiniaoVersionSetting.vue'
|
||||
import { expandBrandFolderRecursive, type BrandExpandFolderItem } from '@/shared/api/brand'
|
||||
import {
|
||||
@@ -403,11 +299,78 @@ const historySectionItems = computed(() =>
|
||||
(item) => !currentSectionItems.value.some(c => isSameDeleteBrandItem(c, item))
|
||||
),
|
||||
);
|
||||
const hasVisibleItems = computed(() => currentSectionItems.value.length > 0 || historySectionItems.value.length > 0)
|
||||
const queueRunnableItems = computed(() =>
|
||||
currentSectionItems.value.filter((item) => canPushToQueue(item) && getQueueStatus(item) === '未入队'),
|
||||
)
|
||||
|
||||
const deleteBrandCards = computed<TaskStatCard[]>(() => {
|
||||
const historyRows = historySectionItems.value
|
||||
let successCount = 0
|
||||
let failedCount = 0
|
||||
for (const item of historyRows) {
|
||||
const status = (item.taskStatus || '').toUpperCase()
|
||||
if (status === 'SUCCESS') {
|
||||
successCount++
|
||||
} else if (status === 'FAILED') {
|
||||
failedCount++
|
||||
} else if (item.success === true) {
|
||||
successCount++
|
||||
} else {
|
||||
failedCount++
|
||||
}
|
||||
}
|
||||
return [
|
||||
{ label: '运行中任务', value: currentSectionItems.value.length },
|
||||
{ label: '已结束任务', value: historyRows.length },
|
||||
{ label: '成功任务', value: successCount },
|
||||
{ label: '失败任务', value: failedCount },
|
||||
]
|
||||
})
|
||||
|
||||
const currentTaskViews = computed<TaskItemView[]>(() =>
|
||||
currentSectionItems.value.map(toDeleteBrandTaskView),
|
||||
)
|
||||
|
||||
const historyTaskViews = computed<TaskItemView[]>(() =>
|
||||
historySectionItems.value.map(toDeleteBrandTaskView),
|
||||
)
|
||||
|
||||
function itemSource(item: TaskItemView): DeleteBrandResultItem {
|
||||
return item.source as DeleteBrandResultItem
|
||||
}
|
||||
|
||||
function toDeleteBrandTaskView(item: DeleteBrandResultItem): TaskItemView {
|
||||
const statusInfo = getTaskStatusInfo(item)
|
||||
const showProgress = Boolean(shouldShowProgress(item))
|
||||
const queueStatus = getQueueStatus(item)
|
||||
const taskStatus = (item.taskStatus || '').toUpperCase()
|
||||
const detailStatus = (getTaskStatus(item.taskId) || '').toUpperCase()
|
||||
const isTerminal = isTerminalStatus(taskStatus) || isTerminalStatus(detailStatus)
|
||||
const extraLines: string[] = [
|
||||
`店铺名:${item.shopName || '-'}`,
|
||||
`匹配结果:${formatMatchResult(item)}`,
|
||||
...(item.platform ? [`平台:${item.platform}`] : []),
|
||||
...(!isTerminal && queueStatus ? [`队列状态:${queueStatus}`] : []),
|
||||
...(item.countryCount !== undefined && item.matched ? [`国家数:${item.countryCount}`] : []),
|
||||
...(item.totalRows !== undefined && item.matched ? [`去重后 ${item.totalRows} 条`] : []),
|
||||
...(showProgress && formatProgress(item) ? [formatProgress(item)] : []),
|
||||
...(item.previewRows?.length ? [formatPreview(item.previewRows)] : []),
|
||||
...(getDisplayError(item) ? [`错误信息:${getDisplayError(item)}`] : []),
|
||||
]
|
||||
return {
|
||||
key: `delete-brand-${item.taskId ?? ''}-${item.resultId ?? item.fileKey ?? item.sourceFilename}`,
|
||||
title: item.sourceFilename || '-',
|
||||
taskId: item.taskId ?? '-',
|
||||
startedAt: formatDateTime(taskCreatedAt(item)),
|
||||
finishedAt: taskFinishedAt(item) ? formatDateTime(taskFinishedAt(item)) : '进行中',
|
||||
statusText: statusInfo.text,
|
||||
statusClass: statusInfo.className as TaskItemView['statusClass'],
|
||||
extraLines,
|
||||
progress: showProgress ? { percent: formatProgressPercent(item), stage: '任务进度' } : null,
|
||||
source: item,
|
||||
}
|
||||
}
|
||||
|
||||
function persistSessionTasks() {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (sessionTasks.value.length === 0) window.localStorage.removeItem(getStorageKey())
|
||||
@@ -560,6 +523,32 @@ function normalizeDeleteBrandItems(items: DeleteBrandResultItem[]) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 任务卡开始时间(任务详情快照的创建时间) */
|
||||
function taskCreatedAt(item: DeleteBrandResultItem) {
|
||||
if (!item.taskId) return ''
|
||||
return taskDetails.value[item.taskId]?.task?.createdAt ?? ''
|
||||
}
|
||||
|
||||
/** 任务卡结束时间(任务详情快照的完成时间) */
|
||||
function taskFinishedAt(item: DeleteBrandResultItem) {
|
||||
if (!item.taskId) return ''
|
||||
return taskDetails.value[item.taskId]?.task?.finishedAt ?? ''
|
||||
}
|
||||
|
||||
function formatDateTime(value?: string) {
|
||||
if (!value) return '-'
|
||||
const date = new Date(value)
|
||||
if (Number.isNaN(date.getTime())) return value
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
|
||||
}
|
||||
|
||||
|
||||
function formatMatchResult(item: DeleteBrandResultItem) {
|
||||
if (item.matchStatus === 'INDEX_STALE' && isUsableMatchedItem(item)) {
|
||||
return `已匹配 ${item.shopId || ''}(索引过期,可继续推送)`.trim()
|
||||
@@ -1135,7 +1124,7 @@ async function submitRun() {
|
||||
const hasBlockedItems = normalizedItems.some((item) => !isUsableMatchedItem(item))
|
||||
|
||||
queuePushResult.value = hasRunnableItems
|
||||
? '解析完成,可在左侧点击“推送到 Python 队列”开始串行处理'
|
||||
? '解析完成,可在左侧点击“启动任务”开始串行处理'
|
||||
: '解析完成,当前没有可推送的文件'
|
||||
queuePayloadText.value = ''
|
||||
|
||||
@@ -1147,9 +1136,9 @@ async function submitRun() {
|
||||
if (hasBlockedItems) {
|
||||
ElMessage.warning('部分文件尚未命中可用店铺索引,已保留状态信息,请等待后台刷新后重试。')
|
||||
} else if (hasStaleMatchedItems) {
|
||||
ElMessage.warning('部分文件命中的是过期索引,仍可推送到 Python 队列;后台刷新后会自动重新匹配。')
|
||||
ElMessage.warning('部分文件命中的是过期索引,仍可启动任务;后台刷新后会自动重新匹配。')
|
||||
} else if (hasRunnableItems) {
|
||||
ElMessage.success('删除品牌解析完成,请在左侧推送到 Python 队列')
|
||||
ElMessage.success('删除品牌解析完成,请在左侧启动任务')
|
||||
} else {
|
||||
ElMessage.warning('当前没有可推送的文件,请先等待店铺索引刷新。')
|
||||
}
|
||||
@@ -1379,7 +1368,7 @@ async function runItem(item: DeleteBrandResultItem, options?: { auto?: boolean }
|
||||
return { success: true, retryable: false }
|
||||
} else {
|
||||
const message = pushResult?.error || '未知错误'
|
||||
qpr = `推送到 Python 队列失败:${message}`
|
||||
qpr = `启动任务失败:${message}`
|
||||
queuePushResult.value = qpr
|
||||
saveSessionTask(taskId, sessionTask.items, qpr, queuePayloadText.value)
|
||||
if (options?.auto && isQueueBusyError(message)) {
|
||||
@@ -1407,7 +1396,7 @@ async function pushToPythonQueue() {
|
||||
activeItemKey.value = ''
|
||||
return
|
||||
}
|
||||
ElMessage.success('已开始按顺序推送到 Python 队列')
|
||||
ElMessage.success('已开始按顺序启动任务')
|
||||
}
|
||||
|
||||
async function maybeAutoAdvance() {
|
||||
@@ -1535,6 +1524,26 @@ onUnmounted(() => {
|
||||
timers.clearScope()
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* 历史记录批量删除:复用单条删除逻辑(删除后原函数各自刷新列表)
|
||||
*/
|
||||
async function batchDeleteHistory(views: TaskItemView[]) {
|
||||
if (!views.length) return
|
||||
let failed = 0
|
||||
for (const view of views) {
|
||||
try {
|
||||
await deleteHistoryRecord(itemSource(view).resultId!)
|
||||
} catch {
|
||||
failed += 1
|
||||
}
|
||||
}
|
||||
if (failed > 0) {
|
||||
ElMessage.warning(`删除完成,${failed} 条失败`)
|
||||
} else {
|
||||
ElMessage.success(`已删除 ${views.length} 条历史记录`)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -1754,154 +1763,12 @@ onUnmounted(() => {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.task-list-wrap {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.task-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.task-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.split-result-item {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.split-result-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.id {
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.files {
|
||||
font-size: 12px;
|
||||
color: #5e6878;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 12px;
|
||||
color: #5e6878;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.split-entry-list {
|
||||
line-height: 1.6;
|
||||
word-break: break-all;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.delete-brand-preview {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.delete-brand-progress-block {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.delete-brand-progress-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 6px;
|
||||
font-size: 12px;
|
||||
color: #8fbfff;
|
||||
}
|
||||
|
||||
.delete-brand-progress-bar {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.delete-brand-progress-bar-fill {
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(90deg, #2d8cf0 0%, #4db3ff 100%);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.delete-brand-progress {
|
||||
color: #69b6ff;
|
||||
line-height: 1.6;
|
||||
white-space: normal;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.task-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.split-result-actions {
|
||||
flex-shrink: 0;
|
||||
min-width: 180px;
|
||||
justify-content: flex-end;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status.success {
|
||||
background: rgba(46, 204, 113, 0.18);
|
||||
color: #2ecc71;
|
||||
}
|
||||
|
||||
.status.failed {
|
||||
background: rgba(231, 76, 60, 0.18);
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
.status.running {
|
||||
background: rgba(52, 152, 219, 0.18);
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.download {
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
@@ -1926,60 +1793,10 @@ onUnmounted(() => {
|
||||
background: rgba(231, 76, 60, 0.22);
|
||||
}
|
||||
|
||||
.empty-tasks {
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
padding: 24px 8px;
|
||||
}
|
||||
|
||||
.clean-placeholder {
|
||||
max-height: 112px;
|
||||
}
|
||||
|
||||
.clean-result-summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 220px));
|
||||
gap: 16px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.summary-card {
|
||||
padding: 16px 18px;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.summary-card strong {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
font-size: 24px;
|
||||
color: #f5f8fc;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: #5e6878;
|
||||
}
|
||||
|
||||
.result-list-wrap {
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1c2333;
|
||||
min-height: 260px;
|
||||
}
|
||||
|
||||
.result-list-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 14px;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.main-content {
|
||||
flex-direction: column;
|
||||
@@ -1995,25 +1812,6 @@ onUnmounted(() => {
|
||||
.right-panel {
|
||||
min-height: 420px;
|
||||
}
|
||||
|
||||
.task-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.task-right {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.split-result-actions {
|
||||
min-width: 0;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.clean-result-summary {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user