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:
@@ -7,7 +7,7 @@
|
||||
<div class="section-title">店铺输入</div>
|
||||
<div class="input-zone">
|
||||
<div class="hint">
|
||||
左侧负责录入店铺并加入备选区,确认命中后推送到 Python 队列。每次推送会把本次选中的所有店铺合并为一条取款任务,任务之间串行执行。
|
||||
左侧负责录入店铺并加入备选区,确认命中后启动任务。每次推送会把本次选中的所有店铺合并为一条取款任务,任务之间串行执行。
|
||||
</div>
|
||||
<div class="input-row">
|
||||
<el-input
|
||||
@@ -88,7 +88,7 @@
|
||||
:disabled="isQueueBusy || !matchedRunnableItems.length"
|
||||
@click="pushToPythonQueue"
|
||||
>
|
||||
{{ isQueueBusy ? "串行执行中..." : "推送到 Python 队列" }}
|
||||
{{ isQueueBusy ? "串行执行中..." : "启动任务" }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -106,223 +106,114 @@
|
||||
</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>{{ dashboard.candidateCount }}</strong>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<span class="summary-label">已处理任务</span>
|
||||
<strong>{{ dashboard.processedTaskCount }}</strong>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<span class="summary-label">成功任务</span>
|
||||
<strong>{{ dashboard.successTaskCount }}</strong>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<span class="summary-label">失败任务</span>
|
||||
<strong>{{ dashboard.failedTaskCount }}</strong>
|
||||
</div>
|
||||
<div class="match-zone">
|
||||
<div class="match-zone-header">
|
||||
<span>匹配结果</span>
|
||||
</div>
|
||||
|
||||
<div class="subsection-title">匹配结果</div>
|
||||
<div v-if="!matchedItems.length" class="empty-tasks narrow">
|
||||
<div v-if="!matchedItems.length" class="match-zone-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="120" />
|
||||
<el-table-column label="匹配" width="72" align="center">
|
||||
<template #default="{ row }">
|
||||
<span :class="row.matched ? 'ok' : 'fail'">
|
||||
{{ row.matched ? "是" : "否" }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="shopId"
|
||||
label="店铺 ID"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="platform"
|
||||
label="平台"
|
||||
width="88"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="companyName"
|
||||
label="公司"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="状态" width="110" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ formatMatchStatus(row.matchStatus) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="说明" min-width="180" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ formatMatchRemark(row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="72" align="center">
|
||||
<template #default="{ row }">
|
||||
<button
|
||||
type="button"
|
||||
class="link-danger"
|
||||
@click="removeMatchedRow(row)"
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="result-list-wrap">
|
||||
<div class="result-list-header">
|
||||
<span>任务记录</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!currentSectionItems.length && !historySectionItems.length"
|
||||
class="empty-tasks"
|
||||
<div v-else class="match-zone-scroll">
|
||||
<el-table
|
||||
:data="matchedItems"
|
||||
:row-key="rowKeyForMatch"
|
||||
:highlight-current-row="false"
|
||||
class="result-table match-table"
|
||||
>
|
||||
暂无任务记录。推送到 Python 队列后,这里会展示当前任务和历史任务。
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<div v-if="currentSectionItems.length" class="result-subsection">
|
||||
<div class="result-subsection-title">当前任务</div>
|
||||
<ul class="task-list">
|
||||
<li
|
||||
v-for="item in currentSectionItems"
|
||||
:key="`current-${item.taskId || item.resultId}`"
|
||||
class="task-item"
|
||||
<el-table-column prop="shopName" label="店铺名" min-width="120" />
|
||||
<el-table-column label="匹配" width="72" align="center">
|
||||
<template #default="{ row }">
|
||||
<span :class="row.matched ? 'ok' : 'fail'">
|
||||
{{ row.matched ? "是" : "否" }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="shopId"
|
||||
label="店铺 ID"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="platform"
|
||||
label="平台"
|
||||
width="88"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="companyName"
|
||||
label="公司"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="状态" width="110" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ formatMatchStatus(row.matchStatus) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="说明" min-width="180" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ formatMatchRemark(row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="72" align="center">
|
||||
<template #default="{ row }">
|
||||
<button
|
||||
type="button"
|
||||
class="link-danger"
|
||||
@click="removeMatchedRow(row)"
|
||||
>
|
||||
<div class="left split-result-main">
|
||||
<span class="id" :title="item.shopName || ''">
|
||||
取款任务 {{ item.taskId ?? "-" }}
|
||||
</span>
|
||||
<div class="files">任务 ID: {{ item.taskId ?? "-" }}</div>
|
||||
<div class="files">包含店铺: {{ item.shopName || "-" }}</div>
|
||||
<div class="files">
|
||||
开始时间: {{ formatDateTime(taskStartTime(item.taskId)) }}
|
||||
</div>
|
||||
<div class="files">
|
||||
创建时间: {{ formatDateTime(item.createdAt) }}
|
||||
</div>
|
||||
<div class="files">
|
||||
取款结果: {{ formatTemplateSummary(item) }}
|
||||
</div>
|
||||
<div v-if="item.shops.length" class="shop-detail-list">
|
||||
<div
|
||||
v-for="shop in item.shops"
|
||||
:key="`cur-shop-${shop.resultId || shop.shopId || shop.shopName}`"
|
||||
class="files shop-detail-line"
|
||||
>
|
||||
{{ shop.shopName || "-" }} · 结果 ID: {{ shop.resultId ?? "-" }} · 店铺 ID: {{ shop.shopId || "-" }} · 平台: {{ shop.platform || "-" }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.error" class="files">
|
||||
错误: {{ item.error }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-right">
|
||||
<span
|
||||
class="status"
|
||||
:class="statusClass(item.taskStatus)"
|
||||
>
|
||||
{{ statusText(item.taskStatus) }}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-delete"
|
||||
@click="deleteTaskRecord(item)"
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="historySectionItems.length" class="result-subsection">
|
||||
<div class="result-subsection-title">历史任务</div>
|
||||
<ul class="task-list">
|
||||
<li
|
||||
v-for="item in historySectionItems"
|
||||
:key="`history-${item.taskId || item.resultId}`"
|
||||
class="task-item"
|
||||
>
|
||||
<div class="left split-result-main">
|
||||
<span class="id" :title="item.shopName || ''">
|
||||
取款任务 {{ item.taskId ?? "-" }}
|
||||
</span>
|
||||
<div class="files">任务 ID: {{ item.taskId ?? "-" }}</div>
|
||||
<div class="files">包含店铺: {{ item.shopName || "-" }}</div>
|
||||
<div class="files">
|
||||
开始时间: {{ formatDateTime(taskStartTime(item.taskId)) }}
|
||||
</div>
|
||||
<div class="files">
|
||||
创建时间: {{ formatDateTime(item.createdAt) }}
|
||||
</div>
|
||||
<div v-if="item.finishedAt" class="files">
|
||||
完成时间: {{ formatDateTime(item.finishedAt) }}
|
||||
</div>
|
||||
<div class="files">
|
||||
取款结果: {{ formatTemplateSummary(item) }}
|
||||
</div>
|
||||
<div v-if="item.shops.length" class="shop-detail-list">
|
||||
<div
|
||||
v-for="shop in item.shops"
|
||||
:key="`his-shop-${shop.resultId || shop.shopId || shop.shopName}`"
|
||||
class="files shop-detail-line"
|
||||
>
|
||||
{{ shop.shopName || "-" }} · 结果 ID: {{ shop.resultId ?? "-" }} · 店铺 ID: {{ shop.shopId || "-" }} · 平台: {{ shop.platform || "-" }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.error" class="files">
|
||||
错误: {{ item.error }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-right">
|
||||
<span
|
||||
class="status"
|
||||
:class="statusClass(item.taskStatus)"
|
||||
>
|
||||
{{ statusText(item.taskStatus) }}
|
||||
</span>
|
||||
<button
|
||||
v-if="canDownload(item)"
|
||||
type="button"
|
||||
class="archive"
|
||||
@click="downloadResult(item)"
|
||||
>
|
||||
下载结果
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-delete"
|
||||
@click="deleteTaskRecord(item)"
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
删除
|
||||
</button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TaskCenterPanel
|
||||
:on-batch-delete="batchDeleteHistory"
|
||||
title="匹配与任务"
|
||||
:cards="withdrawCards"
|
||||
:current-items="currentTaskViews"
|
||||
:history-items="historyTaskViews"
|
||||
current-title="当前任务"
|
||||
current-empty-text="暂无当前任务,启动任务后运行中的任务会显示在这里"
|
||||
history-empty-text="暂无历史任务"
|
||||
>
|
||||
<template #item-extra="{ item }">
|
||||
<div class="files">取款结果:{{ formatTemplateSummary(itemSource(item)) }}</div>
|
||||
<div v-if="itemSource(item).shops.length" class="shop-detail-list">
|
||||
<div
|
||||
v-for="shop in itemSource(item).shops"
|
||||
:key="`cur-shop-${shop.resultId || shop.shopId || shop.shopName}`"
|
||||
class="files shop-detail-line"
|
||||
>
|
||||
{{ shop.shopName || "-" }} · 结果 ID: {{ shop.resultId ?? "-" }} · 店铺 ID: {{ shop.shopId || "-" }} · 平台: {{ shop.platform || "-" }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #history-item-extra="{ item }">
|
||||
<div class="files">取款结果:{{ formatTemplateSummary(itemSource(item)) }}</div>
|
||||
<div v-if="itemSource(item).shops.length" class="shop-detail-list">
|
||||
<div
|
||||
v-for="shop in itemSource(item).shops"
|
||||
:key="`his-shop-${shop.resultId || shop.shopId || shop.shopName}`"
|
||||
class="files shop-detail-line"
|
||||
>
|
||||
{{ shop.shopName || "-" }} · 结果 ID: {{ shop.resultId ?? "-" }} · 店铺 ID: {{ shop.shopId || "-" }} · 平台: {{ shop.platform || "-" }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #item-actions="{ item }">
|
||||
<button type="button" class="btn-delete" @click="deleteTaskRecord(itemSource(item))">删除</button>
|
||||
</template>
|
||||
<template #history-item-actions="{ item }">
|
||||
<button v-if="canDownload(itemSource(item))" type="button" class="download"
|
||||
@click="downloadResult(itemSource(item))">下载结果</button>
|
||||
<button type="button" class="btn-delete" @click="deleteTaskRecord(itemSource(item))">删除</button>
|
||||
</template>
|
||||
</TaskCenterPanel>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
@@ -333,6 +224,8 @@
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import TaskCenterPanel from '@/shared/components/tasks/TaskCenterPanel.vue'
|
||||
import type { TaskItemView, TaskStatCard } from '@/shared/components/tasks/types'
|
||||
import {
|
||||
addWithdrawCandidate,
|
||||
createWithdrawTask,
|
||||
@@ -419,6 +312,43 @@ const hasQueuedTaskWork = computed(
|
||||
const hasQueueWork = computed(() => queueWorkerRunning.value || hasQueuedTaskWork.value);
|
||||
const isQueueBusy = computed(() => queueWorkerRunning.value && hasQueuedTaskWork.value);
|
||||
|
||||
// ---- 右侧任务面板统一视图(TaskCenterPanel)----
|
||||
const withdrawCards = computed<TaskStatCard[]>(() => [
|
||||
{ label: '运行中任务', value: currentTaskViews.value.length },
|
||||
{ label: '已结束任务', value: historyTaskViews.value.length },
|
||||
{ label: '成功任务', value: historyTaskViews.value.filter((view) => view.statusClass === 'success').length },
|
||||
{ label: '失败任务', value: historyTaskViews.value.filter((view) => view.statusClass === 'failed').length },
|
||||
]);
|
||||
|
||||
/** 当前任务:按任务分组后非终态的任务卡 */
|
||||
const currentTaskViews = computed<TaskItemView[]>(() =>
|
||||
currentSectionItems.value.map(toWithdrawTaskView),
|
||||
);
|
||||
|
||||
/** 历史任务:按任务分组后已终态的任务卡 */
|
||||
const historyTaskViews = computed<TaskItemView[]>(() =>
|
||||
historySectionItems.value.map(toWithdrawTaskView),
|
||||
);
|
||||
|
||||
function itemSource(item: TaskItemView): WithdrawTaskGroupItem {
|
||||
return item.source as WithdrawTaskGroupItem;
|
||||
}
|
||||
|
||||
/** 一任务一卡:标题优先店铺名(多店以“、”连接),其次结果文件名 */
|
||||
function toWithdrawTaskView(item: WithdrawTaskGroupItem): TaskItemView {
|
||||
return {
|
||||
key: `withdraw-${item.taskId ?? item.resultId ?? item.shopName}`,
|
||||
title: item.shopName || item.outputFilename || `取款任务 ${item.taskId ?? "-"}`,
|
||||
taskId: item.taskId ?? "-",
|
||||
startedAt: formatDateTime(taskStartTime(item.taskId) || item.createdAt),
|
||||
finishedAt: formatDateTime(item.finishedAt),
|
||||
statusText: statusText(item.taskStatus),
|
||||
statusClass: statusClass(item.taskStatus),
|
||||
extraLines: item.error ? [`错误:${item.error}`] : [],
|
||||
source: item,
|
||||
};
|
||||
}
|
||||
|
||||
function uidForStorage() {
|
||||
return typeof window !== "undefined"
|
||||
? window.localStorage.getItem("uid") || "0"
|
||||
@@ -604,7 +534,7 @@ function formatMatchRemark(row: WithdrawShopQueueItem) {
|
||||
const message = (row.matchMessage || "").trim();
|
||||
if (message) return message;
|
||||
if (row.matched && row.matchStatus === "MATCHED") {
|
||||
return "索引已命中,可推送到 Python 队列";
|
||||
return "索引已命中,可启动任务";
|
||||
}
|
||||
if (row.matched) {
|
||||
return "已命中索引,请结合状态列确认";
|
||||
@@ -1313,6 +1243,26 @@ onUnmounted(() => {
|
||||
clearSleepTimers();
|
||||
timers.clearScope();
|
||||
});
|
||||
|
||||
/**
|
||||
* 历史记录批量删除:复用单条删除逻辑(删除后原函数各自刷新列表)
|
||||
*/
|
||||
async function batchDeleteHistory(views: TaskItemView[]) {
|
||||
if (!views.length) return
|
||||
let failed = 0
|
||||
for (const view of views) {
|
||||
try {
|
||||
await deleteTaskRecord(itemSource(view))
|
||||
} catch {
|
||||
failed += 1
|
||||
}
|
||||
}
|
||||
if (failed > 0) {
|
||||
ElMessage.warning(`删除完成,${failed} 条失败`)
|
||||
} else {
|
||||
ElMessage.success(`已删除 ${views.length} 条历史记录`)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -1347,43 +1297,74 @@ onUnmounted(() => {
|
||||
.queue-debug-title { margin-bottom: 8px; }
|
||||
.queue-debug-line { color: #a0acbe; font-size: 12px; line-height: 1.6; margin-bottom: 8px; }
|
||||
.queue-debug-payload { margin: 0; max-height: 220px; overflow: auto; padding: 10px; border-radius: 8px; background: #10141f; color: #8fd3ff; font-size: 11px; line-height: 1.5; white-space: pre-wrap; word-break: break-all; }
|
||||
.panel-header { padding: 16px 20px; font-size: 15px; font-weight: 600; color: #f5f8fc; border-bottom: 1px solid #2e3a52; }
|
||||
.task-list-wrap { flex: 1; padding: 16px 20px; overflow: auto; }
|
||||
.clean-result-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 16px; }
|
||||
.summary-card { padding: 14px 16px; border: 1px solid #2e3a52; border-radius: 10px; background: #1c2333; }
|
||||
.summary-card strong { display: block; margin-top: 8px; font-size: 22px; color: #f5f8fc; }
|
||||
.summary-label { font-size: 12px; color: #5e6878; }
|
||||
.subsection-title { font-size: 13px; color: #a0acbe; margin: 8px 0 10px; }
|
||||
.empty-tasks { color: #5e6878; font-size: 13px; padding: 16px; text-align: center; }
|
||||
.empty-tasks.narrow { padding: 12px 8px; }
|
||||
.match-table { margin-bottom: 18px; }
|
||||
.match-zone {
|
||||
flex: 0 0 auto;
|
||||
margin: 16px 20px 0;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1c2333;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.match-zone-header {
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 13px;
|
||||
color: #a0acbe;
|
||||
}
|
||||
|
||||
.match-zone-empty {
|
||||
padding: 18px 16px;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.match-zone-scroll {
|
||||
max-height: 260px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.match-table :deep(.el-table__body tr:hover > td.el-table__cell), .match-table :deep(.el-table__body tr.hover-row > td.el-table__cell), .match-table :deep(.el-table__body tr.current-row > td.el-table__cell) { background-color: var(--el-table-tr-bg-color, #222) !important; }
|
||||
.result-table { --el-table-bg-color: #222; --el-table-tr-bg-color: #222; --el-table-header-bg-color: #2e3a52; --el-table-text-color: #c8d2e2; --el-table-border-color: #333; }
|
||||
.ok { color: #27ae60; }
|
||||
.fail { color: #e67e22; }
|
||||
.result-list-wrap { border: 1px solid #2e3a52; border-radius: 10px; background: #1c2333; min-height: 220px; margin-top: 8px; }
|
||||
.result-list-header { padding: 12px 16px; border-bottom: 1px solid #2e3a52; font-size: 14px; color: #c8d2e2; }
|
||||
.result-subsection { padding: 12px 12px 4px; }
|
||||
.result-subsection-title { font-size: 12px; color: #5e6878; margin-bottom: 8px; }
|
||||
.task-list { list-style: none; margin: 0; padding: 0; }
|
||||
.task-item { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 12px 14px; border: 1px solid #2e3a52; border-radius: 8px; margin-bottom: 8px; background: #222; }
|
||||
.left { flex: 1; min-width: 0; }
|
||||
.split-result-main { display: flex; flex-direction: column; gap: 4px; }
|
||||
.id { font-weight: 600; color: #f5f8fc; font-size: 13px; }
|
||||
|
||||
.files { font-size: 12px; color: #5e6878; }
|
||||
.task-right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
|
||||
.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; }
|
||||
.archive { padding: 6px 10px; border-radius: 6px; font-size: 12px; background: rgba(52, 152, 219, 0.18); color: #69b6ff; border: none; cursor: pointer; }
|
||||
.archive:hover { background: rgba(52, 152, 219, 0.28); }
|
||||
.btn-delete { padding: 6px 10px; border-radius: 6px; font-size: 12px; background: rgba(231, 76, 60, 0.12); color: #ff8f8f; border: none; cursor: pointer; }
|
||||
|
||||
.shop-detail-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.download {
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
background: rgba(52, 152, 219, 0.18);
|
||||
color: #69b6ff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.download:hover { background: rgba(52, 152, 219, 0.28); }
|
||||
|
||||
.btn-delete {
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
background: rgba(231, 76, 60, 0.12);
|
||||
color: #ff8f8f;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-delete:hover { background: rgba(231, 76, 60, 0.22); }
|
||||
@media (max-width: 1100px) {
|
||||
.main-content { flex-direction: column; height: auto; }
|
||||
.left-panel { width: 100%; border-right: none; border-bottom: 1px solid #2e3a52; }
|
||||
.clean-result-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user