feat(frontend-vue): 亚马逊运营工具台与新功能页接入——新版工具台(amazon-console+tool-catalog)、品牌检测/ASIN变体采集/查询ASIN 三个工具页 Vue 化并对接既有后端与桌面桥接;桌面首页/登录页 Vue 化(沿用旧视觉);补充 vc_*/get_device_id/save_file_from_url 桥类型、dev 代理;品牌默认勾选排名+FBM;修正 query-asin 标题
含未提交新版改造累积:Brand*Tab 套入工具台壳(AmazonToolPageShell/AmazonTopBar)与目录/权限联动。
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="appearance-patent" />
|
||||
<AmazonToolPageShell tool-id="patent">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel left-panel--with-template">
|
||||
@@ -133,13 +133,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from '@/pages/brand/components/BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import ModuleTemplateDownload from '@/shared/components/ModuleTemplateDownload.vue'
|
||||
import {
|
||||
activateAppearancePatentTask,
|
||||
@@ -981,60 +982,60 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-page { min-height: 100vh; background: #1a1a1a; }
|
||||
.module-page { min-height: 100vh; background: #151a25; }
|
||||
.main-content { display: flex; height: calc(100vh - 56px); min-height: calc(100vh - 56px); }
|
||||
.left-panel { width: 400px; background: #1e1e1e; padding: 20px; overflow-y: auto; border-right: 1px solid #2a2a2a; }
|
||||
.right-panel { flex: 1; min-width: 0; background: #1a1a1a; display: flex; flex-direction: column; }
|
||||
.section-title, .subsection-title { font-size: 13px; color: #bbb; margin-bottom: 10px; }
|
||||
.upload-zone { border: 1px dashed #3a3a3a; border-radius: 10px; padding: 18px; background: #252525; margin-bottom: 18px; }
|
||||
.hint, .loading-msg, .files, .muted { color: #888; font-size: 12px; line-height: 1.5; }
|
||||
.left-panel { width: 400px; background: #1c2333; padding: 20px; overflow-y: auto; border-right: 1px solid #2e3a52; }
|
||||
.right-panel { flex: 1; min-width: 0; background: #151a25; display: flex; flex-direction: column; }
|
||||
.section-title, .subsection-title { font-size: 13px; color: #a0acbe; margin-bottom: 10px; }
|
||||
.upload-zone { border: 1px dashed #3e4a62; border-radius: 10px; padding: 18px; background: #2e3a52; margin-bottom: 18px; }
|
||||
.hint, .loading-msg, .files, .muted { color: #5e6878; font-size: 12px; line-height: 1.5; }
|
||||
.link { color: #6ea8fe; text-decoration: none; }
|
||||
.link:hover { color: #9fc5ff; }
|
||||
.btns, .run-row { display: flex; gap: 10px; flex-wrap: wrap; }
|
||||
.opt-btn, .btn-run, .btn-delete, .download { border: none; cursor: pointer; border-radius: 7px; }
|
||||
.opt-btn { padding: 8px 14px; color: #ccc; background: #2a2a2a; border: 1px solid #3a3a3a; }
|
||||
.btn-run { padding: 10px 18px; color: #fff; background: #3498db; font-weight: 600; }
|
||||
.opt-btn { padding: 8px 14px; color: #c8d2e2; background: #2e3a52; border: 1px solid #3e4a62; }
|
||||
.btn-run { padding: 10px 18px; color: #f5f8fc; background: #3498db; font-weight: 600; }
|
||||
.btn-queue { background: #27ae60; }
|
||||
.btn-run:disabled { opacity: .55; cursor: not-allowed; }
|
||||
.selected-files { margin-top: 14px; color: #999; font-size: 12px; word-break: break-all; }
|
||||
.selected-files { margin-top: 14px; color: #a0acbe; font-size: 12px; word-break: break-all; }
|
||||
.selected-files span { display: block; margin: 4px 0; }
|
||||
.prompt-card, .secret-card { margin-bottom: 18px; }
|
||||
.secret-card { display: none; }
|
||||
.prompt-input { width: 100%; box-sizing: border-box; resize: vertical; min-height: 180px; padding: 10px 12px; border: 1px solid #333; border-radius: 8px; background: #202020; color: #d8d8d8; font-size: 12px; line-height: 1.6; outline: none; }
|
||||
.secret-input { width: 100%; box-sizing: border-box; height: 38px; padding: 0 12px; border: 1px solid #333; border-radius: 8px; background: #202020; color: #d8d8d8; font-size: 12px; outline: none; }
|
||||
.prompt-input { width: 100%; box-sizing: border-box; resize: vertical; min-height: 180px; padding: 10px 12px; border: 1px solid #333; border-radius: 8px; background: #222b3d; color: #c8d2e2; font-size: 12px; line-height: 1.6; outline: none; }
|
||||
.secret-input { width: 100%; box-sizing: border-box; height: 38px; padding: 0 12px; border: 1px solid #333; border-radius: 8px; background: #222b3d; color: #c8d2e2; font-size: 12px; outline: none; }
|
||||
.prompt-input:focus, .secret-input:focus { border-color: #3498db; }
|
||||
.parse-card, .queue-payload { margin-top: 14px; padding: 12px; border: 1px solid #2a2a2a; border-radius: 8px; background: #202020; color: #b8c1cc; font-size: 12px; }
|
||||
.parse-card, .queue-payload { margin-top: 14px; padding: 12px; border: 1px solid #2e3a52; border-radius: 8px; background: #222b3d; color: #a0acbe; font-size: 12px; }
|
||||
.queue-payload { max-height: 220px; overflow: auto; color: #8fd3ff; white-space: pre-wrap; }
|
||||
.panel-header { padding: 16px 20px; border-bottom: 1px solid #2a2a2a; font-size: 15px; font-weight: 600; color: #ddd; }
|
||||
.panel-header { padding: 16px 20px; border-bottom: 1px solid #2e3a52; font-size: 15px; font-weight: 600; color: #c8d2e2; }
|
||||
.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 #2a2a2a; border-radius: 8px; background: #1e1e1e; }
|
||||
.summary-card strong { display: block; margin-top: 8px; color: #eaf4ff; font-size: 22px; }
|
||||
.summary-label { color: #8d8d8d; font-size: 12px; }
|
||||
.result-list-wrap { border: 1px solid #2a2a2a; border-radius: 8px; background: #1e1e1e; min-height: 180px; margin: 0 0 16px; }
|
||||
.result-list-header { display: flex; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid #2a2a2a; color: #ddd; font-size: 14px; }
|
||||
.empty-tasks { color: #666; font-size: 13px; padding: 18px; text-align: center; }
|
||||
.result-table { --el-table-bg-color: #222; --el-table-tr-bg-color: #222; --el-table-header-bg-color: #2a2a2a; --el-table-text-color: #ccc; --el-table-border-color: #333; }
|
||||
.summary-card { padding: 14px 16px; border: 1px solid #2e3a52; border-radius: 8px; background: #1c2333; }
|
||||
.summary-card strong { display: block; margin-top: 8px; color: #f5f8fc; font-size: 22px; }
|
||||
.summary-label { color: #5e6878; font-size: 12px; }
|
||||
.result-list-wrap { border: 1px solid #2e3a52; border-radius: 8px; background: #1c2333; min-height: 180px; margin: 0 0 16px; }
|
||||
.result-list-header { display: flex; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid #2e3a52; color: #c8d2e2; font-size: 14px; }
|
||||
.empty-tasks { color: #5e6878; font-size: 13px; padding: 18px; text-align: center; }
|
||||
.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; }
|
||||
.task-list { list-style: none; margin: 0; padding: 12px; }
|
||||
.task-item { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 12px 14px; border: 1px solid #2a2a2a; border-radius: 8px; margin-bottom: 8px; background: #222; }
|
||||
.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; }
|
||||
.id { color: #e0e0e0; font-size: 13px; font-weight: 600; }
|
||||
.id { color: #f5f8fc; font-size: 13px; font-weight: 600; }
|
||||
.task-right { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
||||
.status { padding: 4px 10px; border-radius: 6px; font-size: 12px; }
|
||||
.status.success { background: rgba(46, 204, 113, .18); color: #2ecc71; }
|
||||
.status.failed { background: rgba(231, 76, 60, .18); color: #ff6b6b; }
|
||||
.status.running { background: rgba(52, 152, 219, .18); color: #3498db; }
|
||||
.status.pending { background: rgba(149, 165, 166, .18); color: #bdc3c7; }
|
||||
.status.pending { background: rgba(149, 165, 166, .18); color: #a0acbe; }
|
||||
.result-hint { margin-top: 6px; color: #e0b96d; }
|
||||
.file-progress { margin-top: 8px; max-width: 520px; }
|
||||
.file-progress-meta { display: flex; justify-content: space-between; gap: 12px; color: #d8c278; font-size: 12px; }
|
||||
.file-progress-track { margin-top: 5px; height: 8px; border-radius: 999px; overflow: hidden; background: #303030; border: 1px solid #3b3b3b; }
|
||||
.file-progress-track { margin-top: 5px; height: 8px; border-radius: 999px; overflow: hidden; background: #333f55; border: 1px solid #3b3b3b; }
|
||||
.file-progress-bar { height: 100%; border-radius: inherit; background: linear-gradient(90deg, #4aa3ff, #f0c75e); transition: width .25s ease; }
|
||||
.download { padding: 6px 10px; color: #d6ecff; background: rgba(52, 152, 219, .18); }
|
||||
.download { padding: 6px 10px; color: #c8d2e2; background: rgba(52, 152, 219, .18); }
|
||||
.btn-delete { padding: 6px 10px; color: #ff8f8f; background: rgba(231, 76, 60, .12); }
|
||||
@media (max-width: 1100px) {
|
||||
.main-content { flex-direction: column; height: auto; }
|
||||
.left-panel { width: 100%; border-right: none; border-bottom: 1px solid #2a2a2a; }
|
||||
.left-panel { width: 100%; border-right: none; border-bottom: 1px solid #2e3a52; }
|
||||
.clean-result-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,494 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<AmazonToolPageShell tool-id="brand">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel">
|
||||
<div class="section-title">待检品牌文件</div>
|
||||
<div class="upload-zone">
|
||||
<div class="hint">
|
||||
Excel 每行含「品牌」列即会加入检测。可多选文件或选择整个文件夹(.xlsx)。检测由本机客户端执行,请在桌面客户端中打开本页面。
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button type="button" class="opt-btn" :disabled="!hasBridge" @click="selectFiles">选择 Excel</button>
|
||||
<button type="button" class="opt-btn" :disabled="!hasBridge" @click="selectFolder">选择文件夹</button>
|
||||
</div>
|
||||
<div class="selected-files">
|
||||
<template v-if="selectedPaths.length">
|
||||
<span v-for="name in displayPaths" :key="name" :title="name">{{ baseName(name) }}</span>
|
||||
<span v-if="selectedPaths.length > displayPaths.length" class="more-line">
|
||||
还有 {{ selectedPaths.length - displayPaths.length }} 个文件未展开显示
|
||||
</span>
|
||||
</template>
|
||||
<span v-else>暂未选择文件</span>
|
||||
</div>
|
||||
<div v-if="!hasBridge" class="bridge-tip">当前浏览器环境无桌面客户端桥接,仅可预览页面;请用本机客户端打开。</div>
|
||||
</div>
|
||||
|
||||
<div class="section-title">匹配策略</div>
|
||||
<div class="radio-cards">
|
||||
<label class="radio-card" :class="{ selected: strategy === 'Terms' }">
|
||||
<input type="radio" name="brand-strategy" value="Terms" v-model="strategy" />
|
||||
<span class="radio-card-name">词条 / 嵌入匹配(Terms)</span>
|
||||
<span class="radio-card-desc">按品牌词条做宽范围比对,误杀率低,推荐第一遍使用</span>
|
||||
</label>
|
||||
<label class="radio-card" :class="{ selected: strategy === 'Simple' }">
|
||||
<input type="radio" name="brand-strategy" value="Simple" v-model="strategy" />
|
||||
<span class="radio-card-name">精确匹配(Simple)</span>
|
||||
<span class="radio-card-desc">仅精确命中才判为疑似,误杀最低</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="section-title">运行方式</div>
|
||||
<div class="run-mode-zone">
|
||||
<label class="run-mode-row">
|
||||
<input type="radio" name="brand-run-mode" value="immediate" v-model="runMode" />
|
||||
<span class="run-mode-text">立即执行(前台运行,可实时取消)</span>
|
||||
</label>
|
||||
<label class="run-mode-row">
|
||||
<input type="radio" name="brand-run-mode" value="queue" v-model="runMode" />
|
||||
<span class="run-mode-text">添加到任务队列(后台排队执行)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="run-row">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-run"
|
||||
:disabled="!selectedPaths.length || submitting || !hasBridge || !hasUid"
|
||||
@click="submitRun"
|
||||
>
|
||||
{{ submitting ? '提交中...' : runMode === 'immediate' ? '立即运行品牌检测' : '添加到任务队列' }}
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="!hasBridge || !hasUid" class="env-tip">
|
||||
品牌检测在桌面客户端内执行:请在本机客户端登录后打开本页面使用(浏览器仅可预览界面)。
|
||||
</p>
|
||||
<p v-else class="loading-msg">
|
||||
检测完成后自动生成结果 Excel(含原始数据、不符合品牌、查询失败品牌),到右侧任务列表下载。
|
||||
</p>
|
||||
|
||||
<div class="section-title">文档模板</div>
|
||||
<div class="btns">
|
||||
<button type="button" class="opt-btn" :disabled="!hasBridge" @click="downloadTemplate('xlsx')">下载 Excel 模板</button>
|
||||
<button type="button" class="opt-btn" :disabled="!hasBridge" @click="downloadTemplate('zip')">下载文件夹模板</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<section class="right-panel">
|
||||
<div class="panel-header">
|
||||
<span>品牌检测任务</span>
|
||||
<button type="button" class="btn-refresh" @click="loadTasks">刷新任务状态</button>
|
||||
</div>
|
||||
<div class="task-list-wrap">
|
||||
<div v-if="!tasks.length" class="empty-tasks">
|
||||
暂无品牌检测任务。选择待检文件后点击「运行」或「添加到任务队列」。
|
||||
</div>
|
||||
<ul v-else class="task-list">
|
||||
<li v-for="item in tasks" :key="`brand-${item.id}`" class="task-item">
|
||||
<div class="left">
|
||||
<span class="id" :title="taskDesc(item)">{{ taskDesc(item) }}</span>
|
||||
<div class="files">任务 ID:{{ item.id }}</div>
|
||||
<div v-if="item.file_paths?.length" class="files">文件:{{ item.file_paths.length }} 个</div>
|
||||
<div class="files">创建时间:{{ formatDateTime(item.created_at) }}</div>
|
||||
<template v-if="isRunning(item)">
|
||||
<div class="task-progress">
|
||||
<div class="task-progress-meta">
|
||||
<span>任务进度</span>
|
||||
<span>{{ taskProgress(item) }}%</span>
|
||||
</div>
|
||||
<div class="task-progress-track">
|
||||
<div class="task-progress-fill" :style="{ width: `${taskProgress(item)}%` }"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="errorMessage(item)" class="files error">{{ errorMessage(item) }}</div>
|
||||
</template>
|
||||
<div v-else-if="errorMessage(item)" class="files error">错误:{{ errorMessage(item) }}</div>
|
||||
</div>
|
||||
<div class="task-right">
|
||||
<span class="status" :class="statusClass(item.status)">{{ statusText(item.status) }}</span>
|
||||
<button v-if="isRunning(item)" type="button" class="act-btn danger" @click="cancelTask(item)">取消</button>
|
||||
<button v-if="canDownload(item)" type="button" class="act-btn ok" @click="downloadResult(item)">下载结果</button>
|
||||
<button v-if="canDelete(item)" type="button" class="act-btn" @click="deleteTask(item)">删除</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue'
|
||||
import { getPywebviewApi } from '@/shared/bridges/pywebview'
|
||||
import { requestPostJson } from '@/shared/api/http'
|
||||
import {
|
||||
cancelBrandTask,
|
||||
createBrandTask,
|
||||
deleteBrandTask,
|
||||
getBrandTaskDownloadUrl,
|
||||
getBrandTasks,
|
||||
runBrandNow,
|
||||
type BrandTaskItem,
|
||||
} from '@/shared/api/brand'
|
||||
|
||||
interface BrandExpandFolderPathsResponse {
|
||||
success: boolean
|
||||
paths?: string[]
|
||||
error?: string
|
||||
}
|
||||
|
||||
const selectedPaths = ref<string[]>([])
|
||||
const strategy = ref<'Terms' | 'Simple'>('Terms')
|
||||
const runMode = ref<'immediate' | 'queue'>('immediate')
|
||||
const submitting = ref(false)
|
||||
const tasks = ref<BrandTaskItem[]>([])
|
||||
|
||||
const hasBridge = computed(() => Boolean(getPywebviewApi()))
|
||||
const hasUid = computed(() => {
|
||||
const raw = typeof window === 'undefined' ? '' : window.localStorage.getItem('uid') || ''
|
||||
const numeric = Number(raw.trim())
|
||||
return Number.isFinite(numeric) && numeric > 0
|
||||
})
|
||||
const displayPaths = computed(() => selectedPaths.value.slice(0, 8))
|
||||
|
||||
function baseName(path: string) {
|
||||
return path.split(/[\\/]/).pop() || path
|
||||
}
|
||||
|
||||
let pollTimer: number | null = null
|
||||
|
||||
function hasRunning() {
|
||||
return tasks.value.some((item) => isRunning(item))
|
||||
}
|
||||
|
||||
function isRunning(item: BrandTaskItem) {
|
||||
return (item.status || '').toLowerCase() === 'running'
|
||||
}
|
||||
|
||||
function isTerminal(item: BrandTaskItem) {
|
||||
const status = (item.status || '').toLowerCase()
|
||||
return status === 'success' || status === 'failed' || status === 'cancelled'
|
||||
}
|
||||
|
||||
function canDelete(item: BrandTaskItem) {
|
||||
return !isRunning(item)
|
||||
}
|
||||
|
||||
function canDownload(item: BrandTaskItem) {
|
||||
const status = (item.status || '').toLowerCase()
|
||||
return status === 'success' && Boolean(item.result_paths?.zip_url)
|
||||
}
|
||||
|
||||
function taskProgress(item: BrandTaskItem) {
|
||||
const total = Number(item.progress_total) || 0
|
||||
const current = Number(item.progress_current) || 0
|
||||
if (!total) return 0
|
||||
return Math.max(0, Math.min(100, Math.round((current / total) * 100)))
|
||||
}
|
||||
|
||||
function taskDesc(item: BrandTaskItem) {
|
||||
const desc = (item.desc || '').trim()
|
||||
return desc ? (desc.length > 60 ? `${desc.slice(0, 60)}…` : desc) : `任务 #${item.id}`
|
||||
}
|
||||
|
||||
function errorMessage(item: BrandTaskItem) {
|
||||
return (item.error_message || '').trim()
|
||||
}
|
||||
|
||||
function statusText(status?: BrandTaskItem['status']) {
|
||||
const value = (status || '').toLowerCase()
|
||||
const map: Record<string, string> = {
|
||||
pending: '等待中',
|
||||
running: '执行中',
|
||||
success: '已完成',
|
||||
failed: '失败',
|
||||
cancelled: '已取消',
|
||||
}
|
||||
return map[value] || String(status ?? '未知')
|
||||
}
|
||||
|
||||
function statusClass(status?: BrandTaskItem['status']) {
|
||||
const value = (status || '').toLowerCase()
|
||||
if (value === 'running') return 'running'
|
||||
if (value === 'success') return 'success'
|
||||
if (value === 'failed') return 'failed'
|
||||
if (value === 'cancelled') return 'cancelled'
|
||||
return 'pending'
|
||||
}
|
||||
|
||||
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 normalizeSelected(paths: string[]) {
|
||||
selectedPaths.value = paths
|
||||
.filter((path) => /\.xlsx$/i.test(path))
|
||||
.filter((path, index, array) => array.indexOf(path) === index)
|
||||
}
|
||||
|
||||
async function selectFiles() {
|
||||
const bridge = getPywebviewApi()
|
||||
if (!bridge?.select_brand_xlsx_files) {
|
||||
ElMessage.warning('当前环境不支持文件选择,请在本机客户端中打开')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const paths = await bridge.select_brand_xlsx_files()
|
||||
if (!paths?.length) return
|
||||
normalizeSelected(paths)
|
||||
ElMessage.success(`已选择 ${selectedPaths.value.length} 个 Excel 文件`)
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '文件选择失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function selectFolder() {
|
||||
const bridge = getPywebviewApi()
|
||||
if (!bridge?.select_brand_folder) {
|
||||
ElMessage.warning('当前环境不支持文件夹选择,请在本机客户端中打开')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const folder = await bridge.select_brand_folder()
|
||||
if (!folder) return
|
||||
const res = await requestPostJson<BrandExpandFolderPathsResponse>('/api/brand/expand-folder', { folder })
|
||||
if (!res.success || !res.paths?.length) {
|
||||
ElMessage.warning(res.error || '该文件夹下没有 xlsx 文件')
|
||||
return
|
||||
}
|
||||
normalizeSelected(res.paths)
|
||||
ElMessage.success(`已选择文件夹内 ${selectedPaths.value.length} 个 Excel 文件`)
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '文件夹选择失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function submitRun() {
|
||||
if (!hasBridge.value) {
|
||||
ElMessage.warning('请在桌面客户端中打开本页面执行品牌检测')
|
||||
return
|
||||
}
|
||||
if (!hasUid.value) {
|
||||
ElMessage.warning('未获取到登录用户,请先在本机客户端登录后再试')
|
||||
return
|
||||
}
|
||||
if (!selectedPaths.value.length) {
|
||||
ElMessage.warning('请先选择待检 Excel 文件')
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
if (runMode.value === 'immediate') {
|
||||
const res = await runBrandNow(selectedPaths.value, strategy.value)
|
||||
if (res.success && res.task_id) {
|
||||
ElMessage.success(`已创建任务 ${res.task_id},客户端开始检测`)
|
||||
} else {
|
||||
throw new Error((res as { error?: string }).error || '运行失败')
|
||||
}
|
||||
} else {
|
||||
const res = await createBrandTask(selectedPaths.value, strategy.value)
|
||||
if (res.success && res.task_id) {
|
||||
ElMessage.success(`任务 ${res.task_id} 已添加到队列`)
|
||||
} else {
|
||||
throw new Error((res as { error?: string }).error || '添加失败')
|
||||
}
|
||||
}
|
||||
selectedPaths.value = []
|
||||
await loadTasks()
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '提交失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTasks() {
|
||||
try {
|
||||
const res = await getBrandTasks()
|
||||
if (res.success && Array.isArray(res.items)) {
|
||||
tasks.value = res.items
|
||||
}
|
||||
} catch (error) {
|
||||
// 浏览器预览(无 uid / 无桌面后端)时静默,避免误报
|
||||
if (hasBridge.value && hasUid.value) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '任务列表加载失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelTask(item: BrandTaskItem) {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确认取消任务 ${item.id}?`, '取消任务', { type: 'warning' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await cancelBrandTask(item.id)
|
||||
if (!res.success) throw new Error((res as { error?: string }).error || '取消失败')
|
||||
ElMessage.success('已取消')
|
||||
await loadTasks()
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '取消失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteTask(item: BrandTaskItem) {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确认删除任务 ${item.id}?删除后不可恢复。`, '删除任务', { type: 'warning' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await deleteBrandTask(item.id)
|
||||
if (!res.success) throw new Error((res as { error?: string }).error || '删除失败')
|
||||
ElMessage.success('已删除')
|
||||
await loadTasks()
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '删除失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadResult(item: BrandTaskItem) {
|
||||
const zipUrl = item.result_paths?.zip_url
|
||||
const filename = `brand_task_${item.id}.zip`
|
||||
const bridge = getPywebviewApi()
|
||||
if (bridge?.save_file_from_url && zipUrl) {
|
||||
try {
|
||||
const res = await bridge.save_file_from_url(zipUrl, filename)
|
||||
if (res.success) {
|
||||
ElMessage.success(`已保存:${res.path || filename}`)
|
||||
} else if (res.error && res.error !== '用户取消') {
|
||||
ElMessage.error(res.error)
|
||||
}
|
||||
return
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '下载失败')
|
||||
return
|
||||
}
|
||||
}
|
||||
if (zipUrl) {
|
||||
window.open(zipUrl, '_blank')
|
||||
return
|
||||
}
|
||||
window.open(getBrandTaskDownloadUrl(item.id), '_blank')
|
||||
}
|
||||
|
||||
async function downloadTemplate(kind: 'xlsx' | 'zip') {
|
||||
const bridge = getPywebviewApi()
|
||||
try {
|
||||
let res: { success: boolean; path?: string; error?: string } | undefined
|
||||
if (kind === 'xlsx') {
|
||||
res = bridge?.save_template_xlsx ? await bridge.save_template_xlsx() : undefined
|
||||
} else {
|
||||
res = bridge?.save_template_zip ? await bridge.save_template_zip() : undefined
|
||||
}
|
||||
if (res?.success) {
|
||||
ElMessage.success(`模板已保存至:${res.path}`)
|
||||
} else if (res?.error) {
|
||||
ElMessage.warning(res.error)
|
||||
} else {
|
||||
ElMessage.warning('当前环境不支持模板下载,请在本机客户端中打开')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '模板下载失败')
|
||||
}
|
||||
}
|
||||
|
||||
function schedulePoll() {
|
||||
if (pollTimer) {
|
||||
window.clearTimeout(pollTimer)
|
||||
pollTimer = null
|
||||
}
|
||||
pollTimer = window.setTimeout(async () => {
|
||||
await loadTasks()
|
||||
if (hasRunning()) {
|
||||
schedulePoll()
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void loadTasks()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (pollTimer) {
|
||||
window.clearTimeout(pollTimer)
|
||||
pollTimer = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-page { min-height: 100vh; background: #151a25; }
|
||||
.main-content { display: flex; height: calc(100vh - 56px); min-height: calc(100vh - 56px); }
|
||||
.left-panel { width: 420px; background: #1c2333; padding: 20px; overflow-y: auto; border-right: 1px solid #2e3a52; }
|
||||
.right-panel { flex: 1; min-width: 0; background: #151a25; display: flex; flex-direction: column; }
|
||||
.section-title { font-size: 13px; color: #a0acbe; margin: 12px 0 10px; }
|
||||
.hint, .loading-msg, .files, .muted { color: #5e6878; font-size: 12px; line-height: 1.5; }
|
||||
.hint { margin-bottom: 12px; }
|
||||
.upload-zone { border: 1px dashed #3e4a62; border-radius: 10px; padding: 16px; background: #2e3a52; margin-bottom: 6px; }
|
||||
.btns, .run-row { display: flex; gap: 10px; flex-wrap: wrap; }
|
||||
.run-row { margin-top: 14px; }
|
||||
.opt-btn { padding: 8px 14px; color: #c8d2e2; background: #2e3a52; border: 1px solid #3e4a62; border-radius: 7px; cursor: pointer; font-size: 12px; }
|
||||
.opt-btn:hover:not(:disabled) { color: #3498db; border-color: #3498db; }
|
||||
.opt-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.selected-files { margin-top: 12px; color: #a0acbe; font-size: 12px; word-break: break-all; }
|
||||
.selected-files span { display: block; margin: 3px 0; }
|
||||
.selected-files .more-line { color: #5e6878; }
|
||||
.bridge-tip { margin-top: 10px; color: #e6a23c; font-size: 11px; }
|
||||
.env-tip { margin-top: 10px; padding: 8px 10px; border-radius: 8px; background: rgba(230, 162, 60, 0.1); color: #e6a23c; font-size: 12px; line-height: 1.6; }
|
||||
.radio-cards { display: flex; flex-direction: column; gap: 8px; margin-bottom: 6px; }
|
||||
.radio-card { display: flex; flex-direction: column; gap: 3px; padding: 10px 12px; border: 1px solid #333; border-radius: 8px; background: #2e3a52; cursor: pointer; }
|
||||
.radio-card.selected { border-color: #409eff; background: rgba(64, 158, 255, 0.1); }
|
||||
.radio-card input { position: absolute; opacity: 0; pointer-events: none; }
|
||||
.radio-card-name { color: #c8d2e2; font-size: 13px; }
|
||||
.radio-card-desc { color: #5e6878; font-size: 11px; }
|
||||
.run-mode-zone { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border: 1px solid #333; border-radius: 8px; background: #2e3a52; }
|
||||
.run-mode-row { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; color: #c8d2e2; font-size: 12px; }
|
||||
.run-mode-row input { width: 15px; height: 15px; accent-color: #409eff; }
|
||||
.btn-run { padding: 10px 18px; color: #f5f8fc; background: #3498db; border: none; border-radius: 8px; font-weight: 600; cursor: pointer; }
|
||||
.btn-run:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.loading-msg { margin-top: 10px; }
|
||||
.panel-header { display: flex; justify-content: space-between; align-items: center; padding: 14px 20px; border-bottom: 1px solid #2e3a52; color: #f5f8fc; font-size: 15px; font-weight: 600; }
|
||||
.btn-refresh { padding: 6px 12px; color: #c8d2e2; background: #2e3a52; border: 1px solid #3e4a62; border-radius: 6px; cursor: pointer; font-size: 12px; }
|
||||
.task-list-wrap { flex: 1; padding: 16px 20px; overflow: auto; }
|
||||
.empty-tasks { color: #5e6878; font-size: 13px; padding: 28px; text-align: center; }
|
||||
.task-list { list-style: none; margin: 0; padding: 0; }
|
||||
.task-item { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; padding: 12px 14px; margin-bottom: 8px; border: 1px solid #2e3a52; border-radius: 8px; background: #1c2333; }
|
||||
.left { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
|
||||
.id { color: #f5f8fc; font-size: 13px; font-weight: 600; }
|
||||
.files.error, .error { color: #ff6b6b; }
|
||||
.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; }
|
||||
.status.pending { background: rgba(149, 165, 166, 0.18); color: #a0acbe; }
|
||||
.status.running { background: rgba(52, 152, 219, 0.18); color: #3498db; }
|
||||
.status.success { background: rgba(46, 204, 113, 0.18); color: #2ecc71; }
|
||||
.status.failed { background: rgba(231, 76, 60, 0.18); color: #ff6b6b; }
|
||||
.status.cancelled { background: rgba(149, 165, 166, 0.18); color: #a0acbe; }
|
||||
.act-btn { padding: 5px 10px; border-radius: 6px; font-size: 12px; background: #2e3a52; color: #c8d2e2; border: 1px solid #3e4a62; cursor: pointer; }
|
||||
.act-btn.ok { background: rgba(52, 152, 219, 0.18); color: #69b6ff; border-color: transparent; }
|
||||
.act-btn.danger { background: rgba(231, 76, 60, 0.15); color: #ff8f8f; border-color: transparent; }
|
||||
.task-progress { margin-top: 6px; max-width: 460px; }
|
||||
.task-progress-meta { display: flex; justify-content: space-between; color: #5e6878; font-size: 11px; margin-bottom: 4px; }
|
||||
.task-progress-track { height: 5px; border-radius: 3px; background: #333; overflow: hidden; }
|
||||
.task-progress-fill { height: 100%; background: #3498db; transition: width 0.25s ease; }
|
||||
@media (max-width: 1100px) {
|
||||
.main-content { flex-direction: column; height: auto; }
|
||||
.left-panel { width: 100%; border-right: none; border-bottom: 1px solid #2e3a52; }
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="collect-data" />
|
||||
<AmazonToolPageShell tool-id="collect">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel left-panel--with-template">
|
||||
@@ -206,13 +206,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from '@/pages/brand/components/BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import ModuleTemplateDownload from '@/shared/components/ModuleTemplateDownload.vue'
|
||||
import { expandBrandFolderRecursive, type BrandExpandFolderItem } from '@/shared/api/brand'
|
||||
import { getPywebviewApi } from '@/shared/bridges/pywebview'
|
||||
@@ -266,9 +267,9 @@ const filters = reactive<{
|
||||
}>({
|
||||
minAmount: null,
|
||||
maxAmount: null,
|
||||
rank: false,
|
||||
rank: true,
|
||||
fba: false,
|
||||
fbm: false,
|
||||
fbm: true,
|
||||
})
|
||||
|
||||
const selectedCountryCode = ref<string>('DE')
|
||||
@@ -761,28 +762,28 @@ onBeforeUnmount(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-page { min-height: 100vh; background: #1a1a1a; }
|
||||
.module-page { min-height: 100vh; background: #151a25; }
|
||||
.main-content { display: flex; height: calc(100vh - 56px); min-height: calc(100vh - 56px); }
|
||||
.left-panel { width: 400px; background: #1e1e1e; padding: 20px; overflow-y: auto; border-right: 1px solid #2a2a2a; }
|
||||
.right-panel { flex: 1; min-width: 0; background: #1a1a1a; display: flex; flex-direction: column; }
|
||||
.section-title, .subsection-title { font-size: 13px; color: #bbb; margin: 12px 0 10px; }
|
||||
.upload-zone { border: 1px dashed #3a3a3a; border-radius: 10px; padding: 18px; background: #252525; margin-bottom: 18px; }
|
||||
.hint, .loading-msg, .files, .muted { color: #888; font-size: 12px; line-height: 1.5; }
|
||||
.left-panel { width: 400px; background: #1c2333; padding: 20px; overflow-y: auto; border-right: 1px solid #2e3a52; }
|
||||
.right-panel { flex: 1; min-width: 0; background: #151a25; display: flex; flex-direction: column; }
|
||||
.section-title, .subsection-title { font-size: 13px; color: #a0acbe; margin: 12px 0 10px; }
|
||||
.upload-zone { border: 1px dashed #3e4a62; border-radius: 10px; padding: 18px; background: #2e3a52; margin-bottom: 18px; }
|
||||
.hint, .loading-msg, .files, .muted { color: #5e6878; font-size: 12px; line-height: 1.5; }
|
||||
.btns, .run-row { display: flex; gap: 10px; flex-wrap: wrap; }
|
||||
.opt-btn, .btn-run, .btn-delete, .download { border: none; cursor: pointer; border-radius: 7px; }
|
||||
.opt-btn { padding: 8px 14px; color: #ccc; background: #2a2a2a; border: 1px solid #3a3a3a; }
|
||||
.btn-run { padding: 10px 18px; color: #fff; background: #3498db; font-weight: 600; }
|
||||
.opt-btn { padding: 8px 14px; color: #c8d2e2; background: #2e3a52; border: 1px solid #3e4a62; }
|
||||
.btn-run { padding: 10px 18px; color: #f5f8fc; background: #3498db; font-weight: 600; }
|
||||
.btn-run.btn-queue { background: #27ae60; }
|
||||
.btn-run:disabled { opacity: .55; cursor: not-allowed; }
|
||||
.selected-files { margin-top: 14px; color: #999; font-size: 12px; word-break: break-all; }
|
||||
.selected-files { margin-top: 14px; color: #a0acbe; font-size: 12px; word-break: break-all; }
|
||||
.selected-files span { display: block; margin: 4px 0; }
|
||||
.selected-files .more-line { color: #777; }
|
||||
.selected-files .more-line { color: #5e6878; }
|
||||
|
||||
.filter-zone {
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -795,70 +796,70 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
.filter-label {
|
||||
width: 110px;
|
||||
color: #bbb;
|
||||
color: #a0acbe;
|
||||
font-size: 12px;
|
||||
}
|
||||
.filter-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 6px 10px;
|
||||
background: #1e1e1e;
|
||||
border: 1px solid #3a3a3a;
|
||||
background: #1c2333;
|
||||
border: 1px solid #3e4a62;
|
||||
border-radius: 6px;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
font-size: 13px;
|
||||
outline: none;
|
||||
}
|
||||
.filter-input:focus { border-color: #3498db; }
|
||||
.range-inputs { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; }
|
||||
.range-separator { color: #777; font-size: 12px; white-space: nowrap; }
|
||||
.range-separator { color: #5e6878; font-size: 12px; white-space: nowrap; }
|
||||
.filter-checks { display: flex; gap: 16px; }
|
||||
.filter-check-row { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; color: #ccc; font-size: 12px; }
|
||||
.filter-check-row { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; color: #c8d2e2; font-size: 12px; }
|
||||
.filter-check-input { width: 16px; height: 16px; accent-color: #409eff; cursor: pointer; }
|
||||
|
||||
.country-pref-hint { margin-top: -4px; }
|
||||
.country-section-title { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
||||
.country-pref-saving { color: #3498db; font-size: 11px; }
|
||||
.country-pref-radios { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-bottom: 14px; }
|
||||
.country-check-row { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; font-size: 12px; color: #ccc; padding: 8px 10px; border: 1px solid #333; border-radius: 8px; background: #252525; }
|
||||
.country-check-row { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; font-size: 12px; color: #c8d2e2; padding: 8px 10px; border: 1px solid #333; border-radius: 8px; background: #2e3a52; }
|
||||
.country-check-row.selected { border-color: #409eff; background: rgba(64, 158, 255, .12); color: #e6f2ff; }
|
||||
.country-check-input { width: 16px; height: 16px; accent-color: #409eff; cursor: pointer; flex-shrink: 0; }
|
||||
.country-check-text { line-height: 1.3; }
|
||||
|
||||
.queue-debug-card { margin-top: 14px; border: 1px solid #2a2a2a; border-radius: 8px; padding: 10px 12px; background: #1f1f1f; }
|
||||
.queue-debug-card { margin-top: 14px; border: 1px solid #2e3a52; border-radius: 8px; padding: 10px 12px; background: #1f1f1f; }
|
||||
.queue-debug-title { margin-top: 0; }
|
||||
.queue-debug-line { color: #9ad; font-size: 12px; }
|
||||
.queue-debug-payload { margin-top: 8px; padding: 8px 10px; background: #181818; border-radius: 6px; color: #bbb; font-size: 11px; overflow: auto; max-height: 200px; }
|
||||
.queue-debug-payload { margin-top: 8px; padding: 8px 10px; background: #151a25; border-radius: 6px; color: #a0acbe; font-size: 11px; overflow: auto; max-height: 200px; }
|
||||
|
||||
.panel-header { padding: 16px 20px; border-bottom: 1px solid #2a2a2a; font-size: 15px; font-weight: 600; color: #ddd; }
|
||||
.panel-header { padding: 16px 20px; border-bottom: 1px solid #2e3a52; font-size: 15px; font-weight: 600; color: #c8d2e2; }
|
||||
.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 #2a2a2a; border-radius: 8px; background: #1e1e1e; }
|
||||
.summary-card strong { display: block; margin-top: 8px; color: #eaf4ff; font-size: 22px; }
|
||||
.summary-label { color: #8d8d8d; font-size: 12px; }
|
||||
.result-list-wrap { border: 1px solid #2a2a2a; border-radius: 8px; background: #1e1e1e; min-height: 180px; margin: 0 0 16px; }
|
||||
.result-list-header { display: flex; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid #2a2a2a; color: #ddd; font-size: 14px; }
|
||||
.empty-tasks { color: #666; font-size: 13px; padding: 18px; text-align: center; }
|
||||
.summary-card { padding: 14px 16px; border: 1px solid #2e3a52; border-radius: 8px; background: #1c2333; }
|
||||
.summary-card strong { display: block; margin-top: 8px; color: #f5f8fc; font-size: 22px; }
|
||||
.summary-label { color: #5e6878; font-size: 12px; }
|
||||
.result-list-wrap { border: 1px solid #2e3a52; border-radius: 8px; background: #1c2333; min-height: 180px; margin: 0 0 16px; }
|
||||
.result-list-header { display: flex; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid #2e3a52; color: #c8d2e2; font-size: 14px; }
|
||||
.empty-tasks { color: #5e6878; font-size: 13px; padding: 18px; text-align: center; }
|
||||
.task-list { list-style: none; margin: 0; padding: 12px; }
|
||||
.task-item { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 12px 14px; border: 1px solid #2a2a2a; border-radius: 8px; margin-bottom: 8px; background: #222; }
|
||||
.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; }
|
||||
.id { color: #e0e0e0; font-size: 13px; font-weight: 600; }
|
||||
.id { color: #f5f8fc; font-size: 13px; font-weight: 600; }
|
||||
.task-right { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
||||
.status { padding: 4px 10px; border-radius: 6px; font-size: 12px; }
|
||||
.status.success { background: rgba(46, 204, 113, .18); color: #2ecc71; }
|
||||
.status.failed { background: rgba(231, 76, 60, .18); color: #ff6b6b; }
|
||||
.status.running { background: rgba(52, 152, 219, .18); color: #3498db; }
|
||||
.status.pending { background: rgba(149, 165, 166, .18); color: #bdc3c7; }
|
||||
.status.pending { background: rgba(149, 165, 166, .18); color: #a0acbe; }
|
||||
.status-filter-summary { font-weight: 400; }
|
||||
.download { padding: 6px 10px; color: #d6ecff; background: rgba(52, 152, 219, .18); }
|
||||
.download { padding: 6px 10px; color: #c8d2e2; background: rgba(52, 152, 219, .18); }
|
||||
.btn-delete { padding: 6px 10px; color: #ff8f8f; background: rgba(231, 76, 60, .12); }
|
||||
.task-progress { margin-top: 8px; max-width: 520px; }
|
||||
.task-progress-meta { display: flex; justify-content: space-between; color: #888; font-size: 11px; margin-bottom: 4px; }
|
||||
.task-progress-meta { display: flex; justify-content: space-between; color: #5e6878; font-size: 11px; margin-bottom: 4px; }
|
||||
.task-progress-track { height: 5px; border-radius: 3px; background: #333; overflow: hidden; }
|
||||
.task-progress-fill { height: 100%; border-radius: inherit; background: #3498db; transition: width .25s ease; }
|
||||
@media (max-width: 1100px) {
|
||||
.main-content { flex-direction: column; height: auto; }
|
||||
.left-panel { width: 100%; border-right: none; border-bottom: 1px solid #2a2a2a; }
|
||||
.left-panel { width: 100%; border-right: none; border-bottom: 1px solid #2e3a52; }
|
||||
.clean-result-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="convert" />
|
||||
<AmazonToolPageShell tool-id="convert">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel">
|
||||
@@ -166,13 +166,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from './BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import { expandBrandFolderRecursive, getBrandTemplateXlsxUrl, getBrandTemplateZipUrl } from '@/shared/api/brand'
|
||||
import type { BrandExpandFolderItem } from '@/shared/api/brand'
|
||||
import {
|
||||
@@ -530,7 +531,7 @@ onMounted(() => {
|
||||
<style scoped>
|
||||
.module-page {
|
||||
min-height: 100vh;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
@@ -541,10 +542,10 @@ onMounted(() => {
|
||||
|
||||
.left-panel {
|
||||
width: 380px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #2a2a2a;
|
||||
border-right: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
@@ -552,32 +553,32 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
color: #bbb;
|
||||
color: #a0acbe;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.upload-zone {
|
||||
border: 1px dashed #3a3a3a;
|
||||
border: 1px dashed #3e4a62;
|
||||
border-radius: 10px;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.upload-zone:hover {
|
||||
border-color: #3498db;
|
||||
background: #2a2a2a;
|
||||
background: #2e3a52;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.5;
|
||||
@@ -603,9 +604,9 @@ onMounted(() => {
|
||||
.opt-btn {
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
background: #2a2a2a;
|
||||
border: 1px solid #3a3a3a;
|
||||
color: #c8d2e2;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #3e4a62;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
@@ -633,7 +634,7 @@ onMounted(() => {
|
||||
.selected-files {
|
||||
margin-top: 14px;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
max-height: 112px;
|
||||
overflow-y: auto;
|
||||
text-align: left;
|
||||
@@ -646,7 +647,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.more-line {
|
||||
color: #b8c1cc;
|
||||
color: #a0acbe;
|
||||
}
|
||||
|
||||
.option-group {
|
||||
@@ -660,8 +661,8 @@ onMounted(() => {
|
||||
cursor: pointer;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
background: #252525;
|
||||
border: 1px solid #2a2a2a;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #2e3a52;
|
||||
margin-bottom: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
@@ -676,8 +677,8 @@ onMounted(() => {
|
||||
|
||||
.radio-item:hover,
|
||||
.radio-item.active {
|
||||
background: #2a2a2a;
|
||||
border-color: #3a3a3a;
|
||||
background: #2e3a52;
|
||||
border-color: #3e4a62;
|
||||
}
|
||||
|
||||
.radio-item input {
|
||||
@@ -686,13 +687,13 @@ onMounted(() => {
|
||||
|
||||
.label {
|
||||
font-weight: 500;
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-top: 2px;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
@@ -719,9 +720,9 @@ onMounted(() => {
|
||||
|
||||
.empty-option {
|
||||
padding: 12px;
|
||||
border: 1px dashed #3a3a3a;
|
||||
border: 1px dashed #3e4a62;
|
||||
border-radius: 8px;
|
||||
color: #777;
|
||||
color: #5e6878;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -735,7 +736,7 @@ onMounted(() => {
|
||||
.template-actions-hint {
|
||||
grid-column: 1 / -1;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@@ -752,7 +753,7 @@ onMounted(() => {
|
||||
.template-download-row {
|
||||
width: 100%;
|
||||
padding: 14px 16px;
|
||||
background: linear-gradient(135deg, #252525 0%, #1f2b1f 100%);
|
||||
background: linear-gradient(135deg, #2e3a52 0%, #1f2b1f 100%);
|
||||
border: 1px solid #2a3a2a;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
@@ -763,7 +764,7 @@ onMounted(() => {
|
||||
|
||||
.template-download-row:hover {
|
||||
border-color: #2e7d32;
|
||||
background: linear-gradient(135deg, #2a2a2a 0%, #243324 100%);
|
||||
background: linear-gradient(135deg, #2e3a52 0%, #243324 100%);
|
||||
box-shadow: 0 4px 12px rgba(46, 125, 50, 0.15);
|
||||
}
|
||||
|
||||
@@ -776,7 +777,7 @@ onMounted(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
color: #f5f8fc;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
@@ -790,14 +791,14 @@ onMounted(() => {
|
||||
|
||||
.template-download-text .title {
|
||||
font-size: 14px;
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.template-download-text .hint {
|
||||
margin: 2px 0 0;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
}
|
||||
|
||||
.run-row {
|
||||
@@ -811,7 +812,7 @@ onMounted(() => {
|
||||
.btn-run {
|
||||
padding: 10px 22px;
|
||||
background: #3498db;
|
||||
color: #fff;
|
||||
color: #f5f8fc;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -822,7 +823,7 @@ onMounted(() => {
|
||||
|
||||
.btn-run:disabled {
|
||||
background: #555;
|
||||
color: #999;
|
||||
color: #a0acbe;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -833,10 +834,10 @@ onMounted(() => {
|
||||
|
||||
.panel-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.task-list-wrap {
|
||||
@@ -857,10 +858,10 @@ onMounted(() => {
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.left {
|
||||
@@ -871,13 +872,13 @@ onMounted(() => {
|
||||
.id {
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.files {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
@@ -931,7 +932,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.empty-tasks {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
padding: 24px 8px;
|
||||
}
|
||||
@@ -949,27 +950,27 @@ onMounted(() => {
|
||||
|
||||
.summary-card {
|
||||
padding: 16px 18px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.summary-card strong {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
font-size: 24px;
|
||||
color: #eaf4ff;
|
||||
color: #f5f8fc;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: #8d8d8d;
|
||||
color: #5e6878;
|
||||
}
|
||||
|
||||
.result-list-wrap {
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
min-height: 260px;
|
||||
}
|
||||
|
||||
@@ -979,9 +980,9 @@ onMounted(() => {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 14px;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
@@ -993,7 +994,7 @@ onMounted(() => {
|
||||
.left-panel {
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="dedupe" />
|
||||
<AmazonToolPageShell tool-id="dedupe">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel">
|
||||
@@ -140,13 +140,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from './BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import { expandBrandFolderRecursive, type BrandExpandFolderItem } from '@/shared/api/brand'
|
||||
import { deleteDedupeHistory, getDedupeHistory, getDedupeResultDownloadUrl, getDedupeRunProgress, getExcelInfo, runDedupe, type DedupeResultItem, type DedupeRunProgressVo, type DedupeRunVo } from '@/shared/api/java-modules'
|
||||
import { getPywebviewApi, type UploadedJavaFile } from '@/shared/bridges/pywebview'
|
||||
@@ -414,7 +415,7 @@ onMounted(() => {
|
||||
<style scoped>
|
||||
.module-page {
|
||||
min-height: 100vh;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
@@ -425,10 +426,10 @@ onMounted(() => {
|
||||
|
||||
.left-panel {
|
||||
width: 380px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #2a2a2a;
|
||||
border-right: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
@@ -436,32 +437,32 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
color: #bbb;
|
||||
color: #a0acbe;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.upload-zone {
|
||||
border: 1px dashed #3a3a3a;
|
||||
border: 1px dashed #3e4a62;
|
||||
border-radius: 10px;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.upload-zone:hover {
|
||||
border-color: #3498db;
|
||||
background: #2a2a2a;
|
||||
background: #2e3a52;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.5;
|
||||
@@ -486,9 +487,9 @@ onMounted(() => {
|
||||
.opt-btn {
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
background: #2a2a2a;
|
||||
border: 1px solid #3a3a3a;
|
||||
color: #c8d2e2;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #3e4a62;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
@@ -506,7 +507,7 @@ onMounted(() => {
|
||||
.selected-files {
|
||||
margin-top: 14px;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
max-height: 112px;
|
||||
overflow-y: auto;
|
||||
text-align: left;
|
||||
@@ -519,7 +520,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.more-line {
|
||||
color: #b8c1cc;
|
||||
color: #a0acbe;
|
||||
}
|
||||
|
||||
.option-group {
|
||||
@@ -529,8 +530,8 @@ onMounted(() => {
|
||||
.column-option-group {
|
||||
padding: 14px;
|
||||
border-radius: 10px;
|
||||
background: #252525;
|
||||
border: 1px solid #2a2a2a;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.column-toolbar {
|
||||
@@ -543,7 +544,7 @@ onMounted(() => {
|
||||
|
||||
.column-count {
|
||||
font-size: 12px;
|
||||
color: #a9b4bf;
|
||||
color: #a0acbe;
|
||||
}
|
||||
|
||||
.column-actions {
|
||||
@@ -562,8 +563,8 @@ onMounted(() => {
|
||||
.empty-column-state {
|
||||
padding: 16px 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px dashed #3a3a3a;
|
||||
background: #202020;
|
||||
border: 1px dashed #3e4a62;
|
||||
background: #222b3d;
|
||||
color: #7f8790;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
@@ -575,8 +576,8 @@ onMounted(() => {
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #2f2f2f;
|
||||
background: #202020;
|
||||
border: 1px solid #323e58;
|
||||
background: #222b3d;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-size: 13px;
|
||||
@@ -604,16 +605,16 @@ onMounted(() => {
|
||||
cursor: pointer;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
background: #252525;
|
||||
border: 1px solid #2a2a2a;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #2e3a52;
|
||||
margin-bottom: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.radio-item:hover,
|
||||
.radio-item.active {
|
||||
background: #2a2a2a;
|
||||
border-color: #3a3a3a;
|
||||
background: #2e3a52;
|
||||
border-color: #3e4a62;
|
||||
}
|
||||
|
||||
.radio-item input {
|
||||
@@ -622,13 +623,13 @@ onMounted(() => {
|
||||
|
||||
.label {
|
||||
font-weight: 500;
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-top: 2px;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
@@ -645,7 +646,7 @@ onMounted(() => {
|
||||
.btn-run {
|
||||
padding: 10px 22px;
|
||||
background: #3498db;
|
||||
color: #fff;
|
||||
color: #f5f8fc;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -656,7 +657,7 @@ onMounted(() => {
|
||||
|
||||
.btn-run:disabled {
|
||||
background: #555;
|
||||
color: #999;
|
||||
color: #a0acbe;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -667,10 +668,10 @@ onMounted(() => {
|
||||
|
||||
.panel-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.task-list-wrap {
|
||||
@@ -691,10 +692,10 @@ onMounted(() => {
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.left {
|
||||
@@ -705,13 +706,13 @@ onMounted(() => {
|
||||
.id {
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.files {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
@@ -765,7 +766,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.empty-tasks {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
padding: 24px 8px;
|
||||
}
|
||||
@@ -783,27 +784,27 @@ onMounted(() => {
|
||||
|
||||
.summary-card {
|
||||
padding: 16px 18px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.summary-card strong {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
font-size: 24px;
|
||||
color: #eaf4ff;
|
||||
color: #f5f8fc;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: #8d8d8d;
|
||||
color: #5e6878;
|
||||
}
|
||||
|
||||
.result-list-wrap {
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
min-height: 260px;
|
||||
}
|
||||
|
||||
@@ -813,9 +814,9 @@ onMounted(() => {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 14px;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
@@ -827,7 +828,7 @@ onMounted(() => {
|
||||
.left-panel {
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="delete-brand" />
|
||||
<AmazonToolPageShell tool-id="delasin">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel left-panel--with-template">
|
||||
@@ -208,13 +208,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from './BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import ModuleTemplateDownload from '@/shared/components/ModuleTemplateDownload.vue'
|
||||
import ZiniaoVersionSetting from '@/shared/components/ZiniaoVersionSetting.vue'
|
||||
import { expandBrandFolderRecursive, type BrandExpandFolderItem } from '@/shared/api/brand'
|
||||
@@ -1540,9 +1541,9 @@ onUnmounted(() => {
|
||||
.queue-debug-card {
|
||||
margin-top: 18px;
|
||||
padding: 14px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #202020;
|
||||
background: #222b3d;
|
||||
}
|
||||
|
||||
.queue-debug-title {
|
||||
@@ -1550,7 +1551,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.queue-debug-line {
|
||||
color: #b8c1cc;
|
||||
color: #a0acbe;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 8px;
|
||||
@@ -1562,7 +1563,7 @@ onUnmounted(() => {
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
background: #141414;
|
||||
background: #10141f;
|
||||
color: #8fd3ff;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
@@ -1572,7 +1573,7 @@ onUnmounted(() => {
|
||||
|
||||
.module-page {
|
||||
min-height: 100vh;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
@@ -1583,10 +1584,10 @@ onUnmounted(() => {
|
||||
|
||||
.left-panel {
|
||||
width: 380px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #2a2a2a;
|
||||
border-right: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
@@ -1594,32 +1595,32 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
color: #bbb;
|
||||
color: #a0acbe;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.upload-zone {
|
||||
border: 1px dashed #3a3a3a;
|
||||
border: 1px dashed #3e4a62;
|
||||
border-radius: 10px;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.upload-zone:hover {
|
||||
border-color: #3498db;
|
||||
background: #2a2a2a;
|
||||
background: #2e3a52;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.5;
|
||||
@@ -1644,9 +1645,9 @@ onUnmounted(() => {
|
||||
.opt-btn {
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
background: #2a2a2a;
|
||||
border: 1px solid #3a3a3a;
|
||||
color: #c8d2e2;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #3e4a62;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
@@ -1659,7 +1660,7 @@ onUnmounted(() => {
|
||||
.selected-files {
|
||||
margin-top: 14px;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
max-height: 112px;
|
||||
overflow-y: auto;
|
||||
text-align: left;
|
||||
@@ -1672,7 +1673,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.more-line {
|
||||
color: #b8c1cc;
|
||||
color: #a0acbe;
|
||||
}
|
||||
|
||||
.split-selected-files {
|
||||
@@ -1692,16 +1693,16 @@ onUnmounted(() => {
|
||||
cursor: pointer;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
background: #252525;
|
||||
border: 1px solid #2a2a2a;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #2e3a52;
|
||||
margin-bottom: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.radio-item:hover,
|
||||
.radio-item.active {
|
||||
background: #2a2a2a;
|
||||
border-color: #3a3a3a;
|
||||
background: #2e3a52;
|
||||
border-color: #3e4a62;
|
||||
}
|
||||
|
||||
.radio-item input {
|
||||
@@ -1710,13 +1711,13 @@ onUnmounted(() => {
|
||||
|
||||
.label {
|
||||
font-weight: 500;
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-top: 2px;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
@@ -1733,7 +1734,7 @@ onUnmounted(() => {
|
||||
.btn-run {
|
||||
padding: 10px 22px;
|
||||
background: #3498db;
|
||||
color: #fff;
|
||||
color: #f5f8fc;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -1744,7 +1745,7 @@ onUnmounted(() => {
|
||||
|
||||
.btn-run:disabled {
|
||||
background: #555;
|
||||
color: #999;
|
||||
color: #a0acbe;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -1755,10 +1756,10 @@ onUnmounted(() => {
|
||||
|
||||
.panel-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.task-list-wrap {
|
||||
@@ -1779,10 +1780,10 @@ onUnmounted(() => {
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.split-result-item {
|
||||
@@ -1803,19 +1804,19 @@ onUnmounted(() => {
|
||||
.id {
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.files {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
@@ -1926,7 +1927,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.empty-tasks {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
padding: 24px 8px;
|
||||
}
|
||||
@@ -1944,27 +1945,27 @@ onUnmounted(() => {
|
||||
|
||||
.summary-card {
|
||||
padding: 16px 18px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.summary-card strong {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
font-size: 24px;
|
||||
color: #eaf4ff;
|
||||
color: #f5f8fc;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: #8d8d8d;
|
||||
color: #5e6878;
|
||||
}
|
||||
|
||||
.result-list-wrap {
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
min-height: 260px;
|
||||
}
|
||||
|
||||
@@ -1974,9 +1975,9 @@ onUnmounted(() => {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 14px;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
@@ -1988,7 +1989,7 @@ onUnmounted(() => {
|
||||
.left-panel {
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="patrol-delete" />
|
||||
<AmazonToolPageShell tool-id="patrol">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel">
|
||||
@@ -365,13 +365,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import BrandTopBar from "@/pages/brand/components/BrandTopBar.vue";
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import {
|
||||
addPatrolDeleteCandidate,
|
||||
addPatrolDeleteCondition,
|
||||
@@ -1322,29 +1323,29 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-page { min-height: 100vh; background: #1a1a1a; }
|
||||
.module-page { min-height: 100vh; background: #151a25; }
|
||||
.main-content { display: flex; min-height: calc(100vh - 56px); height: calc(100vh - 56px); }
|
||||
.left-panel { width: 400px; background: #1e1e1e; padding: 20px; overflow-y: auto; border-right: 1px solid #2a2a2a; }
|
||||
.right-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; background: #1a1a1a; }
|
||||
.section-title { font-size: 13px; color: #bbb; margin-bottom: 10px; }
|
||||
.left-panel { width: 400px; background: #1c2333; padding: 20px; overflow-y: auto; border-right: 1px solid #2e3a52; }
|
||||
.right-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; background: #151a25; }
|
||||
.section-title { font-size: 13px; color: #a0acbe; margin-bottom: 10px; }
|
||||
.condition-title { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
||||
.condition-title-hint { flex: 1; min-width: 0; color: #d6a95a; font-size: 12px; text-align: right; line-height: 1.35; }
|
||||
.input-zone { border: 1px dashed #3a3a3a; border-radius: 10px; padding: 16px; background: #252525; margin-bottom: 20px; }
|
||||
.hint { color: #888; font-size: 12px; margin-bottom: 12px; line-height: 1.5; text-align: left; }
|
||||
.input-zone { border: 1px dashed #3e4a62; border-radius: 10px; padding: 16px; background: #2e3a52; margin-bottom: 20px; }
|
||||
.hint { color: #5e6878; font-size: 12px; margin-bottom: 12px; line-height: 1.5; text-align: left; }
|
||||
.input-row { display: flex; gap: 10px; align-items: center; }
|
||||
.input-row :deep(.el-input) { flex: 1; }
|
||||
.opt-btn { padding: 8px 12px; font-size: 12px; color: #ccc; background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 6px; cursor: pointer; white-space: nowrap; }
|
||||
.opt-btn { padding: 8px 12px; font-size: 12px; color: #c8d2e2; background: #2e3a52; border: 1px solid #3e4a62; border-radius: 6px; cursor: pointer; white-space: nowrap; }
|
||||
.opt-btn:hover:not(:disabled) { color: #3498db; border-color: #3498db; }
|
||||
.opt-btn:disabled { opacity: 0.6; cursor: not-allowed; }
|
||||
.empty-candidates { color: #666; font-size: 13px; padding: 16px; border: 1px dashed #333; border-radius: 8px; margin-bottom: 16px; }
|
||||
.candidate-table-scroll { max-height: 320px; overflow: auto; margin-bottom: 18px; border-radius: 8px; border: 1px solid #2a2a2a; }
|
||||
.candidate-table { --el-table-bg-color: #252525; --el-table-tr-bg-color: #252525; --el-table-header-bg-color: #2a2a2a; --el-table-text-color: #ccc; --el-table-border-color: #333; }
|
||||
.condition-panel { margin-bottom: 18px; border: 1px solid #2a2a2a; border-radius: 8px; background: #222; padding: 12px; }
|
||||
.empty-candidates { color: #5e6878; font-size: 13px; padding: 16px; border: 1px dashed #333; border-radius: 8px; margin-bottom: 16px; }
|
||||
.candidate-table-scroll { max-height: 320px; overflow: auto; margin-bottom: 18px; border-radius: 8px; border: 1px solid #2e3a52; }
|
||||
.candidate-table { --el-table-bg-color: #2e3a52; --el-table-tr-bg-color: #2e3a52; --el-table-header-bg-color: #2e3a52; --el-table-text-color: #c8d2e2; --el-table-border-color: #333; }
|
||||
.condition-panel { margin-bottom: 18px; border: 1px solid #2e3a52; border-radius: 8px; background: #222; padding: 12px; }
|
||||
.condition-input-row { display: flex; gap: 10px; align-items: center; margin-bottom: 10px; }
|
||||
.condition-input-row :deep(.el-input) { flex: 1; }
|
||||
.empty-conditions { color: #666; font-size: 12px; padding: 10px 4px; }
|
||||
.empty-conditions { color: #5e6878; font-size: 12px; padding: 10px 4px; }
|
||||
.condition-list { list-style: none; margin: 0; padding: 0; max-height: 150px; overflow: auto; display: flex; flex-direction: column; gap: 8px; }
|
||||
.condition-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 6px; border-bottom: 1px solid #303030; }
|
||||
.condition-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 6px; border-bottom: 1px solid #333f55; }
|
||||
.condition-item:last-child { border-bottom: none; }
|
||||
.condition-check { display: flex; align-items: center; gap: 8px; min-width: 0; color: #cfd6df; font-size: 12px; cursor: pointer; }
|
||||
.condition-check input { width: 16px; height: 16px; margin: 0; flex: 0 0 auto; }
|
||||
@@ -1352,40 +1353,40 @@ onUnmounted(() => {
|
||||
.link-danger { background: none; border: none; color: #e74c3c; cursor: pointer; font-size: 12px; padding: 0; }
|
||||
.link-danger:hover { text-decoration: underline; }
|
||||
.run-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
|
||||
.btn-run { padding: 10px 18px; font-size: 14px; font-weight: 600; color: #fff; background: #3498db; border: none; border-radius: 8px; cursor: pointer; }
|
||||
.btn-run { padding: 10px 18px; font-size: 14px; font-weight: 600; color: #f5f8fc; background: #3498db; border: none; border-radius: 8px; cursor: pointer; }
|
||||
.btn-run:hover:not(:disabled) { background: #2980b9; }
|
||||
.btn-run:disabled { opacity: 0.55; cursor: not-allowed; }
|
||||
.btn-queue { background: #27ae60; }
|
||||
.btn-queue:hover:not(:disabled) { background: #219a52; }
|
||||
.loading-msg { margin-top: 12px; font-size: 12px; color: #777; line-height: 1.5; }
|
||||
.queue-debug-card { margin-top: 18px; padding: 14px; border: 1px solid #2a2a2a; border-radius: 10px; background: #202020; }
|
||||
.loading-msg { margin-top: 12px; font-size: 12px; color: #5e6878; line-height: 1.5; }
|
||||
.queue-debug-card { margin-top: 18px; padding: 14px; border: 1px solid #2e3a52; border-radius: 10px; background: #222b3d; }
|
||||
.queue-debug-title { margin-bottom: 8px; }
|
||||
.queue-debug-line { color: #b8c1cc; 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: #141414; 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: #e0e0e0; border-bottom: 1px solid #2a2a2a; }
|
||||
.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 #2a2a2a; border-radius: 10px; background: #1e1e1e; }
|
||||
.summary-card strong { display: block; margin-top: 8px; font-size: 22px; color: #eaf4ff; }
|
||||
.summary-label { font-size: 12px; color: #8d8d8d; }
|
||||
.subsection-title { font-size: 13px; color: #999; margin: 8px 0 10px; }
|
||||
.empty-tasks { color: #666; font-size: 13px; padding: 16px; text-align: center; }
|
||||
.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-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: #2a2a2a; --el-table-text-color: #ccc; --el-table-border-color: #333; }
|
||||
.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 #2a2a2a; border-radius: 10px; background: #1e1e1e; min-height: 220px; margin-top: 8px; }
|
||||
.result-list-header { padding: 12px 16px; border-bottom: 1px solid #2a2a2a; font-size: 14px; color: #ddd; }
|
||||
.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: #888; margin-bottom: 8px; }
|
||||
.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 #2a2a2a; border-radius: 8px; margin-bottom: 8px; background: #222; }
|
||||
.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: #e0e0e0; font-size: 13px; }
|
||||
.files { font-size: 12px; color: #888; }
|
||||
.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; }
|
||||
@@ -1397,7 +1398,7 @@ onUnmounted(() => {
|
||||
.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 #2a2a2a; }
|
||||
.left-panel { width: 100%; border-right: none; border-bottom: 1px solid #2e3a52; }
|
||||
.clean-result-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="pricing" />
|
||||
<AmazonToolPageShell tool-id="follow">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel left-panel--with-template">
|
||||
@@ -257,13 +257,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from '@/pages/brand/components/BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import ModuleTemplateDownload from '@/shared/components/ModuleTemplateDownload.vue'
|
||||
import ZiniaoVersionSetting from '@/shared/components/ZiniaoVersionSetting.vue'
|
||||
import { LISTING_FILTER_OPTIONS, type ListingFilterValue } from '@/pages/brand/components/listingFilters'
|
||||
@@ -1774,7 +1775,7 @@ onUnmounted(() => {
|
||||
<style scoped>
|
||||
.module-page {
|
||||
min-height: 100vh;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
@@ -1785,10 +1786,10 @@ onUnmounted(() => {
|
||||
|
||||
.left-panel {
|
||||
width: 400px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #2a2a2a;
|
||||
border-right: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.price-track-shop-candidates {
|
||||
@@ -1800,25 +1801,25 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
color: #bbb;
|
||||
color: #a0acbe;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.upload-zone {
|
||||
border: 1px dashed #3a3a3a;
|
||||
border: 1px dashed #3e4a62;
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
font-size: 12px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.5;
|
||||
@@ -1840,7 +1841,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.clean-placeholder {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1848,24 +1849,24 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.selected-files span:not(.more-line) {
|
||||
color: #aaa;
|
||||
color: #a0acbe;
|
||||
}
|
||||
|
||||
.more-line {
|
||||
color: #777;
|
||||
color: #5e6878;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #777;
|
||||
color: #5e6878;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.mode-zone {
|
||||
border: 1px dashed #3a3a3a;
|
||||
border: 1px dashed #3e4a62;
|
||||
border-radius: 10px;
|
||||
padding: 14px 16px;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1891,12 +1892,12 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.mode-check-text {
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mode-hint-inline {
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -1912,8 +1913,8 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.loop-round-input :deep(.el-input__wrapper) {
|
||||
background-color: #2a2a2a;
|
||||
box-shadow: 0 0 0 1px #3a3a3a inset;
|
||||
background-color: #2e3a52;
|
||||
box-shadow: 0 0 0 1px #3e4a62 inset;
|
||||
}
|
||||
|
||||
.tag-badge {
|
||||
@@ -1953,14 +1954,14 @@ onUnmounted(() => {
|
||||
|
||||
.mode-status-text {
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
color: #a0acbe;
|
||||
}
|
||||
|
||||
.input-zone {
|
||||
border: 1px dashed #3a3a3a;
|
||||
border: 1px dashed #3e4a62;
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@@ -1977,9 +1978,9 @@ onUnmounted(() => {
|
||||
.opt-btn {
|
||||
padding: 8px 14px;
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
background: #2a2a2a;
|
||||
border: 1px solid #3a3a3a;
|
||||
color: #c8d2e2;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #3e4a62;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
@@ -2013,7 +2014,7 @@ onUnmounted(() => {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.country-check-input {
|
||||
@@ -2032,13 +2033,13 @@ onUnmounted(() => {
|
||||
border: 1px solid #333;
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.country-order-caption {
|
||||
font-size: 11px;
|
||||
color: #777;
|
||||
color: #5e6878;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@@ -2055,7 +2056,7 @@ onUnmounted(() => {
|
||||
padding: 8px 10px;
|
||||
background: #2c2c2c;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #3a3a3a;
|
||||
border: 1px solid #3e4a62;
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
}
|
||||
@@ -2066,19 +2067,19 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 12px;
|
||||
letter-spacing: -2px;
|
||||
}
|
||||
|
||||
.country-drag-label {
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.country-pref-status {
|
||||
font-size: 11px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@@ -2095,13 +2096,13 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.listing-filter-row :deep(.el-select__wrapper) {
|
||||
background-color: #2a2a2a;
|
||||
box-shadow: 0 0 0 1px #3a3a3a inset;
|
||||
background-color: #2e3a52;
|
||||
box-shadow: 0 0 0 1px #3e4a62 inset;
|
||||
}
|
||||
|
||||
.listing-filter-row :deep(.el-select__placeholder),
|
||||
.listing-filter-row :deep(.el-select__selected-item) {
|
||||
color: #ccc;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
|
||||
@@ -2112,7 +2113,7 @@ onUnmounted(() => {
|
||||
|
||||
|
||||
.empty-candidates {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
padding: 16px;
|
||||
border: 1px dashed #333;
|
||||
@@ -2125,14 +2126,14 @@ onUnmounted(() => {
|
||||
overflow: auto;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.candidate-table {
|
||||
--el-table-bg-color: #252525;
|
||||
--el-table-tr-bg-color: #252525;
|
||||
--el-table-header-bg-color: #2a2a2a;
|
||||
--el-table-text-color: #ccc;
|
||||
--el-table-bg-color: #2e3a52;
|
||||
--el-table-tr-bg-color: #2e3a52;
|
||||
--el-table-header-bg-color: #2e3a52;
|
||||
--el-table-text-color: #c8d2e2;
|
||||
--el-table-border-color: #333;
|
||||
}
|
||||
|
||||
@@ -2160,7 +2161,7 @@ onUnmounted(() => {
|
||||
padding: 10px 18px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
color: #f5f8fc;
|
||||
background: #3498db;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
@@ -2187,7 +2188,7 @@ onUnmounted(() => {
|
||||
.loading-msg {
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
color: #777;
|
||||
color: #5e6878;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@@ -2221,9 +2222,9 @@ onUnmounted(() => {
|
||||
.queue-debug-card {
|
||||
margin-top: 18px;
|
||||
padding: 14px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #202020;
|
||||
background: #222b3d;
|
||||
}
|
||||
|
||||
.queue-debug-title {
|
||||
@@ -2231,7 +2232,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.queue-debug-line {
|
||||
color: #b8c1cc;
|
||||
color: #a0acbe;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 8px;
|
||||
@@ -2243,7 +2244,7 @@ onUnmounted(() => {
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
background: #141414;
|
||||
background: #10141f;
|
||||
color: #8fd3ff;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
@@ -2256,8 +2257,8 @@ onUnmounted(() => {
|
||||
padding: 16px 20px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #e0e0e0;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
color: #f5f8fc;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.task-list-wrap {
|
||||
@@ -2275,31 +2276,31 @@ onUnmounted(() => {
|
||||
|
||||
.summary-card {
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.summary-card strong {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
font-size: 22px;
|
||||
color: #eaf4ff;
|
||||
color: #f5f8fc;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: #8d8d8d;
|
||||
color: #5e6878;
|
||||
}
|
||||
|
||||
.subsection-title {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
color: #a0acbe;
|
||||
margin: 8px 0 10px;
|
||||
}
|
||||
|
||||
.empty-tasks {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
@@ -2316,8 +2317,8 @@ onUnmounted(() => {
|
||||
.result-table {
|
||||
--el-table-bg-color: #222;
|
||||
--el-table-tr-bg-color: #222;
|
||||
--el-table-header-bg-color: #2a2a2a;
|
||||
--el-table-text-color: #ccc;
|
||||
--el-table-header-bg-color: #2e3a52;
|
||||
--el-table-text-color: #c8d2e2;
|
||||
--el-table-border-color: #333;
|
||||
}
|
||||
|
||||
@@ -2325,18 +2326,18 @@ onUnmounted(() => {
|
||||
.fail { color: #e67e22; }
|
||||
|
||||
.result-list-wrap {
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
min-height: 200px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.result-list-header {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 14px;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.result-subsection {
|
||||
@@ -2345,7 +2346,7 @@ onUnmounted(() => {
|
||||
|
||||
.result-subsection-title {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@@ -2361,7 +2362,7 @@ onUnmounted(() => {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
background: #222;
|
||||
@@ -2375,13 +2376,13 @@ onUnmounted(() => {
|
||||
|
||||
.id {
|
||||
font-weight: 600;
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.files {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
}
|
||||
|
||||
.task-right {
|
||||
@@ -2451,7 +2452,7 @@ onUnmounted(() => {
|
||||
.left-panel {
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.pr-summary {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="product-risk" />
|
||||
<AmazonToolPageShell tool-id="risk">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel">
|
||||
@@ -190,13 +190,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from '@/pages/brand/components/BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import { LISTING_FILTER_OPTIONS, type ListingFilterValue } from '@/pages/brand/components/listingFilters'
|
||||
import {
|
||||
addProductRiskCandidate,
|
||||
@@ -1271,7 +1272,7 @@ onUnmounted(() => {
|
||||
<style scoped>
|
||||
.module-page {
|
||||
min-height: 100vh;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
@@ -1282,10 +1283,10 @@ onUnmounted(() => {
|
||||
|
||||
.left-panel {
|
||||
width: 400px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #2a2a2a;
|
||||
border-right: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
@@ -1293,25 +1294,25 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
color: #bbb;
|
||||
color: #a0acbe;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.input-zone {
|
||||
border: 1px dashed #3a3a3a;
|
||||
border: 1px dashed #3e4a62;
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
font-size: 12px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.5;
|
||||
@@ -1336,7 +1337,7 @@ onUnmounted(() => {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.country-check-input {
|
||||
@@ -1355,13 +1356,13 @@ onUnmounted(() => {
|
||||
border: 1px solid #333;
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.country-order-caption {
|
||||
font-size: 11px;
|
||||
color: #777;
|
||||
color: #5e6878;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@@ -1378,7 +1379,7 @@ onUnmounted(() => {
|
||||
padding: 8px 10px;
|
||||
background: #2c2c2c;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #3a3a3a;
|
||||
border: 1px solid #3e4a62;
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
}
|
||||
@@ -1389,19 +1390,19 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 12px;
|
||||
letter-spacing: -2px;
|
||||
}
|
||||
|
||||
.country-drag-label {
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.country-pref-status {
|
||||
font-size: 11px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@@ -1418,13 +1419,13 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.listing-filter-row :deep(.el-select__wrapper) {
|
||||
background-color: #2a2a2a;
|
||||
box-shadow: 0 0 0 1px #3a3a3a inset;
|
||||
background-color: #2e3a52;
|
||||
box-shadow: 0 0 0 1px #3e4a62 inset;
|
||||
}
|
||||
|
||||
.listing-filter-row :deep(.el-select__placeholder),
|
||||
.listing-filter-row :deep(.el-select__selected-item) {
|
||||
color: #ccc;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.input-row {
|
||||
@@ -1440,9 +1441,9 @@ onUnmounted(() => {
|
||||
.opt-btn {
|
||||
padding: 8px 14px;
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
background: #2a2a2a;
|
||||
border: 1px solid #3a3a3a;
|
||||
color: #c8d2e2;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #3e4a62;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
@@ -1459,7 +1460,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.empty-candidates {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
padding: 16px;
|
||||
border: 1px dashed #333;
|
||||
@@ -1472,14 +1473,14 @@ onUnmounted(() => {
|
||||
overflow: auto;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.candidate-table {
|
||||
--el-table-bg-color: #252525;
|
||||
--el-table-tr-bg-color: #252525;
|
||||
--el-table-header-bg-color: #2a2a2a;
|
||||
--el-table-text-color: #ccc;
|
||||
--el-table-bg-color: #2e3a52;
|
||||
--el-table-tr-bg-color: #2e3a52;
|
||||
--el-table-header-bg-color: #2e3a52;
|
||||
--el-table-text-color: #c8d2e2;
|
||||
--el-table-border-color: #333;
|
||||
}
|
||||
|
||||
@@ -1507,7 +1508,7 @@ onUnmounted(() => {
|
||||
padding: 10px 18px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
color: #f5f8fc;
|
||||
background: #3498db;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
@@ -1534,7 +1535,7 @@ onUnmounted(() => {
|
||||
.loading-msg {
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
color: #777;
|
||||
color: #5e6878;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@@ -1546,9 +1547,9 @@ onUnmounted(() => {
|
||||
.queue-debug-card {
|
||||
margin-top: 18px;
|
||||
padding: 14px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #202020;
|
||||
background: #222b3d;
|
||||
}
|
||||
|
||||
.queue-debug-title {
|
||||
@@ -1556,7 +1557,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.queue-debug-line {
|
||||
color: #b8c1cc;
|
||||
color: #a0acbe;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 8px;
|
||||
@@ -1568,7 +1569,7 @@ onUnmounted(() => {
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
background: #141414;
|
||||
background: #10141f;
|
||||
color: #8fd3ff;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
@@ -1580,8 +1581,8 @@ onUnmounted(() => {
|
||||
padding: 16px 20px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #e0e0e0;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
color: #f5f8fc;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.task-list-wrap {
|
||||
@@ -1599,31 +1600,31 @@ onUnmounted(() => {
|
||||
|
||||
.summary-card {
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.summary-card strong {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
font-size: 22px;
|
||||
color: #eaf4ff;
|
||||
color: #f5f8fc;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: #8d8d8d;
|
||||
color: #5e6878;
|
||||
}
|
||||
|
||||
.subsection-title {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
color: #a0acbe;
|
||||
margin: 8px 0 10px;
|
||||
}
|
||||
|
||||
.empty-tasks {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
@@ -1647,8 +1648,8 @@ onUnmounted(() => {
|
||||
.result-table {
|
||||
--el-table-bg-color: #222;
|
||||
--el-table-tr-bg-color: #222;
|
||||
--el-table-header-bg-color: #2a2a2a;
|
||||
--el-table-text-color: #ccc;
|
||||
--el-table-header-bg-color: #2e3a52;
|
||||
--el-table-text-color: #c8d2e2;
|
||||
--el-table-border-color: #333;
|
||||
}
|
||||
|
||||
@@ -1661,18 +1662,18 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.result-list-wrap {
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
min-height: 200px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.result-list-header {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 14px;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.result-subsection {
|
||||
@@ -1681,7 +1682,7 @@ onUnmounted(() => {
|
||||
|
||||
.result-subsection-title {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@@ -1697,7 +1698,7 @@ onUnmounted(() => {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
background: #222;
|
||||
@@ -1716,13 +1717,13 @@ onUnmounted(() => {
|
||||
|
||||
.id {
|
||||
font-weight: 600;
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.files {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
}
|
||||
|
||||
.task-right {
|
||||
@@ -1792,7 +1793,7 @@ onUnmounted(() => {
|
||||
.left-panel {
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.clean-result-summary {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="publish" />
|
||||
<AmazonToolPageShell tool-id="list">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel left-panel--with-template">
|
||||
@@ -197,6 +197,7 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -204,7 +205,7 @@
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
import BrandTopBar from './BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import ModuleTemplateDownload from '@/shared/components/ModuleTemplateDownload.vue'
|
||||
import ZiniaoVersionSetting from '@/shared/components/ZiniaoVersionSetting.vue'
|
||||
import { expandBrandFolderRecursive, type BrandExpandFolderItem } from '@/shared/api/brand'
|
||||
@@ -1160,47 +1161,47 @@ onBeforeUnmount(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-page { min-height: 100vh; background: #1a1a1a; }
|
||||
.module-page { min-height: 100vh; background: #151a25; }
|
||||
.main-content { display: flex; min-height: calc(100vh - 88px); height: calc(100vh - 88px); }
|
||||
.left-panel { width: 400px; background: #1e1e1e; padding: 20px; overflow-y: auto; border-right: 1px solid #2a2a2a; }
|
||||
.right-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; background: #1a1a1a; }
|
||||
.section-title { margin-bottom: 10px; color: #bbb; font-size: 13px; }
|
||||
.upload-zone { margin-bottom: 20px; padding: 20px; border: 1px dashed #3a3a3a; border-radius: 8px; background: #252525; text-align: center; }
|
||||
.hint, .loading-msg, .task-meta, .file-info, .history-count { color: #888; font-size: 12px; line-height: 1.5; }
|
||||
.left-panel { width: 400px; background: #1c2333; padding: 20px; overflow-y: auto; border-right: 1px solid #2e3a52; }
|
||||
.right-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; background: #151a25; }
|
||||
.section-title { margin-bottom: 10px; color: #a0acbe; font-size: 13px; }
|
||||
.upload-zone { margin-bottom: 20px; padding: 20px; border: 1px dashed #3e4a62; border-radius: 8px; background: #2e3a52; text-align: center; }
|
||||
.hint, .loading-msg, .task-meta, .file-info, .history-count { color: #5e6878; font-size: 12px; line-height: 1.5; }
|
||||
.btns, .run-row, .task-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
|
||||
.btns { justify-content: center; }
|
||||
.opt-btn, .btn-run, .download, .btn-delete { border: 0; border-radius: 6px; cursor: pointer; transition: background-color .15s ease, color .15s ease, opacity .15s ease; }
|
||||
.opt-btn { padding: 8px 16px; border: 1px solid #3a3a3a; background: #2a2a2a; color: #ccc; font-size: 13px; }
|
||||
.opt-btn { padding: 8px 16px; border: 1px solid #3e4a62; background: #2e3a52; color: #c8d2e2; font-size: 13px; }
|
||||
.opt-btn:hover:not(:disabled) { border-color: #3498db; color: #67b7ef; }
|
||||
.opt-btn:disabled, .btn-run:disabled { opacity: .55; cursor: not-allowed; }
|
||||
.selected-files { max-height: 120px; min-height: 72px; margin-top: 14px; overflow-y: auto; color: #888; font-size: 12px; text-align: left; }
|
||||
.selected-files { max-height: 120px; min-height: 72px; margin-top: 14px; overflow-y: auto; color: #5e6878; font-size: 12px; text-align: left; }
|
||||
.selected-files span { display: block; margin: 4px 0; word-break: break-all; }
|
||||
.more-line { color: #b8c1cc; }
|
||||
.country-group { margin-bottom: 12px; padding: 14px; border: 1px solid #2f2f2f; border-radius: 8px; background: #252525; }
|
||||
.country-group-title { margin-bottom: 10px; color: #a9b4bf; font-size: 12px; }
|
||||
.more-line { color: #a0acbe; }
|
||||
.country-group { margin-bottom: 12px; padding: 14px; border: 1px solid #323e58; border-radius: 8px; background: #2e3a52; }
|
||||
.country-group-title { margin-bottom: 10px; color: #a0acbe; font-size: 12px; }
|
||||
.country-checks { display: flex; flex-wrap: wrap; gap: 10px 16px; }
|
||||
.country-check-row { display: inline-flex; align-items: center; gap: 8px; color: #ccc; cursor: pointer; font-size: 12px; user-select: none; }
|
||||
.country-check-row { display: inline-flex; align-items: center; gap: 8px; color: #c8d2e2; cursor: pointer; font-size: 12px; user-select: none; }
|
||||
.country-check-row.disabled { opacity: .45; cursor: not-allowed; }
|
||||
.country-check-input { width: 16px; height: 16px; flex-shrink: 0; accent-color: #409eff; cursor: pointer; }
|
||||
.country-check-input:disabled { cursor: not-allowed; }
|
||||
.country-hint { margin: 10px 0 0; color: #777; font-size: 11px; }
|
||||
.country-hint { margin: 10px 0 0; color: #5e6878; font-size: 11px; }
|
||||
.run-row { margin-top: 16px; }
|
||||
.btn-run { min-height: 38px; padding: 9px 20px; background: #3498db; color: #fff; font-size: 14px; }
|
||||
.btn-run { min-height: 38px; padding: 9px 20px; background: #3498db; color: #f5f8fc; font-size: 14px; }
|
||||
.btn-run:hover:not(:disabled) { background: #2980b9; }
|
||||
.queue-status { margin-top: 16px; padding: 12px; border-left: 3px solid #409eff; background: #252525; color: #b8d8ef; font-size: 12px; line-height: 1.6; }
|
||||
.queue-status { margin-top: 16px; padding: 12px; border-left: 3px solid #409eff; background: #2e3a52; color: #b8d8ef; font-size: 12px; line-height: 1.6; }
|
||||
.queue-status-title { margin-bottom: 4px; }
|
||||
.panel-header { padding: 16px 20px; border-bottom: 1px solid #2a2a2a; color: #ddd; font-size: 15px; font-weight: 600; }
|
||||
.panel-header { padding: 16px 20px; border-bottom: 1px solid #2e3a52; color: #c8d2e2; font-size: 15px; font-weight: 600; }
|
||||
.task-list-wrap { flex: 1; padding: 16px; overflow-y: auto; }
|
||||
.clean-result-summary { display: grid; grid-template-columns: repeat(4, minmax(120px, 1fr)); gap: 12px; margin-bottom: 18px; }
|
||||
.summary-card { padding: 14px 16px; border: 1px solid #2a2a2a; border-radius: 8px; background: #1e1e1e; }
|
||||
.summary-card strong { display: block; margin-top: 8px; color: #eaf4ff; font-size: 22px; }
|
||||
.summary-label { color: #8d8d8d; font-size: 12px; }
|
||||
.result-list-wrap { min-height: 260px; border: 1px solid #2a2a2a; border-radius: 8px; background: #1e1e1e; }
|
||||
.result-list-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; border-bottom: 1px solid #2a2a2a; color: #ddd; font-size: 14px; }
|
||||
.empty-tasks { padding: 24px 16px; color: #666; font-size: 13px; }
|
||||
.task-section + .task-section { border-top: 1px solid #303030; }
|
||||
.summary-card { padding: 14px 16px; border: 1px solid #2e3a52; border-radius: 8px; background: #1c2333; }
|
||||
.summary-card strong { display: block; margin-top: 8px; color: #f5f8fc; font-size: 22px; }
|
||||
.summary-label { color: #5e6878; font-size: 12px; }
|
||||
.result-list-wrap { min-height: 260px; border: 1px solid #2e3a52; border-radius: 8px; background: #1c2333; }
|
||||
.result-list-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; border-bottom: 1px solid #2e3a52; color: #c8d2e2; font-size: 14px; }
|
||||
.empty-tasks { padding: 24px 16px; color: #5e6878; font-size: 13px; }
|
||||
.task-section + .task-section { border-top: 1px solid #333f55; }
|
||||
.task-section-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 14px 16px; background: #222; }
|
||||
.task-title-wrap { display: flex; min-width: 0; flex-direction: column; gap: 4px; color: #ddd; }
|
||||
.task-title-wrap { display: flex; min-width: 0; flex-direction: column; gap: 4px; color: #c8d2e2; }
|
||||
.task-error, .file-error { color: #ef8d8d; font-size: 12px; }
|
||||
.task-error { padding: 10px 16px 0; }
|
||||
.download { padding: 7px 12px; background: #2d6b46; color: #dff7e8; font-size: 12px; white-space: nowrap; }
|
||||
@@ -1219,7 +1220,7 @@ onBeforeUnmount(() => {
|
||||
.file-title { overflow: hidden; color: #e1e1e1; font-size: 13px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.file-info { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 6px; }
|
||||
.file-progress-header { display: flex; justify-content: space-between; gap: 12px; margin-top: 10px; color: #969696; font-size: 11px; }
|
||||
.file-progress-track { height: 6px; margin-top: 5px; overflow: hidden; border-radius: 3px; background: #303030; }
|
||||
.file-progress-track { height: 6px; margin-top: 5px; overflow: hidden; border-radius: 3px; background: #333f55; }
|
||||
.file-progress-fill { height: 100%; border-radius: inherit; background: #8d7b3f; transition: width .25s ease; }
|
||||
.file-progress-fill.running { background: #409eff; }
|
||||
.file-progress-fill.success { background: #42b36b; }
|
||||
@@ -1229,7 +1230,7 @@ onBeforeUnmount(() => {
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.main-content { height: auto; flex-direction: column; }
|
||||
.left-panel { width: 100%; border-right: 0; border-bottom: 1px solid #2a2a2a; }
|
||||
.left-panel { width: 100%; border-right: 0; border-bottom: 1px solid #2e3a52; }
|
||||
.right-panel { min-height: 420px; }
|
||||
.clean-result-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="query-asin" />
|
||||
<AmazonToolPageShell tool-id="qasin">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel">
|
||||
@@ -312,13 +312,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import BrandTopBar from "@/pages/brand/components/BrandTopBar.vue";
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import {
|
||||
addQueryAsinCandidate,
|
||||
createQueryAsinTask,
|
||||
@@ -1208,58 +1209,58 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-page { min-height: 100vh; background: #1a1a1a; }
|
||||
.module-page { min-height: 100vh; background: #151a25; }
|
||||
.main-content { display: flex; min-height: calc(100vh - 56px); height: calc(100vh - 56px); }
|
||||
.left-panel { width: 400px; background: #1e1e1e; padding: 20px; overflow-y: auto; border-right: 1px solid #2a2a2a; }
|
||||
.right-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; background: #1a1a1a; }
|
||||
.section-title { font-size: 13px; color: #bbb; margin-bottom: 10px; }
|
||||
.input-zone { border: 1px dashed #3a3a3a; border-radius: 10px; padding: 16px; background: #252525; margin-bottom: 20px; }
|
||||
.hint { color: #888; font-size: 12px; margin-bottom: 12px; line-height: 1.5; text-align: left; }
|
||||
.left-panel { width: 400px; background: #1c2333; padding: 20px; overflow-y: auto; border-right: 1px solid #2e3a52; }
|
||||
.right-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; background: #151a25; }
|
||||
.section-title { font-size: 13px; color: #a0acbe; margin-bottom: 10px; }
|
||||
.input-zone { border: 1px dashed #3e4a62; border-radius: 10px; padding: 16px; background: #2e3a52; margin-bottom: 20px; }
|
||||
.hint { color: #5e6878; font-size: 12px; margin-bottom: 12px; line-height: 1.5; text-align: left; }
|
||||
.input-row { display: flex; gap: 10px; align-items: center; }
|
||||
.input-row :deep(.el-input) { flex: 1; }
|
||||
.opt-btn { padding: 8px 12px; font-size: 12px; color: #ccc; background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 6px; cursor: pointer; white-space: nowrap; }
|
||||
.opt-btn { padding: 8px 12px; font-size: 12px; color: #c8d2e2; background: #2e3a52; border: 1px solid #3e4a62; border-radius: 6px; cursor: pointer; white-space: nowrap; }
|
||||
.opt-btn:hover:not(:disabled) { color: #3498db; border-color: #3498db; }
|
||||
.opt-btn:disabled { opacity: 0.6; cursor: not-allowed; }
|
||||
.empty-candidates { color: #666; font-size: 13px; padding: 16px; border: 1px dashed #333; border-radius: 8px; margin-bottom: 16px; }
|
||||
.candidate-table-scroll { max-height: 320px; overflow: auto; margin-bottom: 18px; border-radius: 8px; border: 1px solid #2a2a2a; }
|
||||
.candidate-table { --el-table-bg-color: #252525; --el-table-tr-bg-color: #252525; --el-table-header-bg-color: #2a2a2a; --el-table-text-color: #ccc; --el-table-border-color: #333; }
|
||||
.empty-candidates { color: #5e6878; font-size: 13px; padding: 16px; border: 1px dashed #333; border-radius: 8px; margin-bottom: 16px; }
|
||||
.candidate-table-scroll { max-height: 320px; overflow: auto; margin-bottom: 18px; border-radius: 8px; border: 1px solid #2e3a52; }
|
||||
.candidate-table { --el-table-bg-color: #2e3a52; --el-table-tr-bg-color: #2e3a52; --el-table-header-bg-color: #2e3a52; --el-table-text-color: #c8d2e2; --el-table-border-color: #333; }
|
||||
.link-danger { background: none; border: none; color: #e74c3c; cursor: pointer; font-size: 12px; padding: 0; }
|
||||
.link-danger:hover { text-decoration: underline; }
|
||||
.run-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
|
||||
.btn-run { padding: 10px 18px; font-size: 14px; font-weight: 600; color: #fff; background: #3498db; border: none; border-radius: 8px; cursor: pointer; }
|
||||
.btn-run { padding: 10px 18px; font-size: 14px; font-weight: 600; color: #f5f8fc; background: #3498db; border: none; border-radius: 8px; cursor: pointer; }
|
||||
.btn-run:hover:not(:disabled) { background: #2980b9; }
|
||||
.btn-run:disabled { opacity: 0.55; cursor: not-allowed; }
|
||||
.btn-queue { background: #27ae60; }
|
||||
.btn-queue:hover:not(:disabled) { background: #219a52; }
|
||||
.loading-msg { margin-top: 12px; font-size: 12px; color: #777; line-height: 1.5; }
|
||||
.queue-debug-card { margin-top: 18px; padding: 14px; border: 1px solid #2a2a2a; border-radius: 10px; background: #202020; }
|
||||
.loading-msg { margin-top: 12px; font-size: 12px; color: #5e6878; line-height: 1.5; }
|
||||
.queue-debug-card { margin-top: 18px; padding: 14px; border: 1px solid #2e3a52; border-radius: 10px; background: #222b3d; }
|
||||
.queue-debug-title { margin-bottom: 8px; }
|
||||
.queue-debug-line { color: #b8c1cc; 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: #141414; 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: #e0e0e0; border-bottom: 1px solid #2a2a2a; }
|
||||
.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 #2a2a2a; border-radius: 10px; background: #1e1e1e; }
|
||||
.summary-card strong { display: block; margin-top: 8px; font-size: 22px; color: #eaf4ff; }
|
||||
.summary-label { font-size: 12px; color: #8d8d8d; }
|
||||
.subsection-title { font-size: 13px; color: #999; margin: 8px 0 10px; }
|
||||
.empty-tasks { color: #666; font-size: 13px; padding: 16px; text-align: center; }
|
||||
.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-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: #2a2a2a; --el-table-text-color: #ccc; --el-table-border-color: #333; }
|
||||
.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 #2a2a2a; border-radius: 10px; background: #1e1e1e; min-height: 220px; margin-top: 8px; }
|
||||
.result-list-header { padding: 12px 16px; border-bottom: 1px solid #2a2a2a; font-size: 14px; color: #ddd; }
|
||||
.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: #888; margin-bottom: 8px; }
|
||||
.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 #2a2a2a; border-radius: 8px; margin-bottom: 8px; background: #222; }
|
||||
.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: #e0e0e0; font-size: 13px; }
|
||||
.files { font-size: 12px; color: #888; }
|
||||
.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; }
|
||||
@@ -1271,7 +1272,7 @@ onUnmounted(() => {
|
||||
.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 #2a2a2a; }
|
||||
.left-panel { width: 100%; border-right: none; border-bottom: 1px solid #2e3a52; }
|
||||
.clean-result-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="shop-data-crawl" />
|
||||
<AmazonToolPageShell tool-id="scrape">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel">
|
||||
@@ -114,13 +114,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, defineComponent, h, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from '@/pages/brand/components/BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import ZiniaoVersionSetting from '@/shared/components/ZiniaoVersionSetting.vue'
|
||||
import { useZiniaoVersion } from '@/shared/utils/ziniao-version'
|
||||
import { getPywebviewApi } from '@/shared/bridges/pywebview'
|
||||
@@ -568,51 +569,51 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-page { min-height: 100vh; background: #1a1a1a; }
|
||||
.module-page { min-height: 100vh; background: #151a25; }
|
||||
.main-content { display: flex; height: calc(100vh - 56px); min-height: calc(100vh - 56px); }
|
||||
.left-panel { width: 400px; padding: 20px; overflow-y: auto; border-right: 1px solid #2a2a2a; background: #1e1e1e; }
|
||||
.right-panel { flex: 1; min-width: 0; background: #1a1a1a; }
|
||||
.section-title { margin-bottom: 10px; color: #bbb; font-size: 13px; }
|
||||
.input-zone { margin-bottom: 18px; padding: 14px; border: 1px dashed #3a3a3a; border-radius: 8px; background: #252525; }
|
||||
.left-panel { width: 400px; padding: 20px; overflow-y: auto; border-right: 1px solid #2e3a52; background: #1c2333; }
|
||||
.right-panel { flex: 1; min-width: 0; background: #151a25; }
|
||||
.section-title { margin-bottom: 10px; color: #a0acbe; font-size: 13px; }
|
||||
.input-zone { margin-bottom: 18px; padding: 14px; border: 1px dashed #3e4a62; border-radius: 8px; background: #2e3a52; }
|
||||
.input-row { display: flex; gap: 10px; }
|
||||
.opt-btn, .btn-run { min-height: 36px; padding: 0 16px; border: 0; border-radius: 5px; background: #409eff; color: #fff; white-space: nowrap; cursor: pointer; }
|
||||
.opt-btn, .btn-run { min-height: 36px; padding: 0 16px; border: 0; border-radius: 5px; background: #409eff; color: #f5f8fc; white-space: nowrap; cursor: pointer; }
|
||||
.opt-btn { flex: 0 0 auto; }
|
||||
.opt-btn:disabled, .btn-run:disabled { opacity: .5; cursor: not-allowed; }
|
||||
.empty-candidates, .empty-tasks { padding: 16px; border: 1px dashed #333; border-radius: 6px; color: #777; font-size: 13px; }
|
||||
.candidate-table-scroll { margin-bottom: 18px; border: 1px solid #2a2a2a; border-radius: 6px; overflow: hidden; }
|
||||
.candidate-table { --el-table-bg-color: #252525; --el-table-tr-bg-color: #252525; --el-table-header-bg-color: #2a2a2a; --el-table-text-color: #ccc; --el-table-border-color: #333; }
|
||||
.empty-candidates, .empty-tasks { padding: 16px; border: 1px dashed #333; border-radius: 6px; color: #5e6878; font-size: 13px; }
|
||||
.candidate-table-scroll { margin-bottom: 18px; border: 1px solid #2e3a52; border-radius: 6px; overflow: hidden; }
|
||||
.candidate-table { --el-table-bg-color: #2e3a52; --el-table-tr-bg-color: #2e3a52; --el-table-header-bg-color: #2e3a52; --el-table-text-color: #c8d2e2; --el-table-border-color: #333; }
|
||||
.link-danger { border: 0; background: transparent; color: #f56c6c; cursor: pointer; }
|
||||
.country-pref-checks { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-bottom: 10px; }
|
||||
.country-check-row { display: flex; align-items: center; gap: 7px; min-height: 32px; color: #ccc; font-size: 13px; }
|
||||
.country-check-row { display: flex; align-items: center; gap: 7px; min-height: 32px; color: #c8d2e2; font-size: 13px; }
|
||||
.country-check-input { width: 15px; height: 15px; }
|
||||
.country-order-panel { margin-bottom: 8px; padding: 10px; border: 1px solid #333; border-radius: 6px; background: #242424; }
|
||||
.country-order-caption { margin-bottom: 8px; color: #888; font-size: 12px; }
|
||||
.country-order-caption { margin-bottom: 8px; color: #5e6878; font-size: 12px; }
|
||||
.country-order-list { display: flex; flex-direction: column; gap: 6px; }
|
||||
.country-drag-row { display: flex; align-items: center; gap: 8px; min-height: 30px; padding: 0 9px; border: 1px solid #383838; border-radius: 4px; color: #ccc; font-size: 13px; cursor: grab; }
|
||||
.country-drag-row { display: flex; align-items: center; gap: 8px; min-height: 30px; padding: 0 9px; border: 1px solid #383838; border-radius: 4px; color: #c8d2e2; font-size: 13px; cursor: grab; }
|
||||
.country-drag-row.dragging { opacity: .5; }
|
||||
.drag-handle { color: #777; }
|
||||
.drag-handle { color: #5e6878; }
|
||||
.country-pref-status, .queue-status { color: #8dc4ff; font-size: 12px; line-height: 1.5; }
|
||||
.run-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 16px; }
|
||||
.btn-queue { background: #67c23a; }
|
||||
.panel-header { height: 52px; padding: 16px 22px; border-bottom: 1px solid #2a2a2a; color: #eee; font-size: 15px; font-weight: 700; }
|
||||
.panel-header { height: 52px; padding: 16px 22px; border-bottom: 1px solid #2e3a52; color: #f5f8fc; font-size: 15px; font-weight: 700; }
|
||||
.task-list-wrap { height: calc(100% - 52px); padding: 18px 22px 28px; overflow-y: auto; }
|
||||
.clean-result-summary { display: grid; grid-template-columns: repeat(4, minmax(100px, 1fr)); gap: 12px; margin-bottom: 20px; }
|
||||
.summary-card { display: flex; min-height: 70px; flex-direction: column; justify-content: center; padding: 12px 16px; border: 1px solid #303030; border-radius: 6px; background: #222; }
|
||||
.summary-label { margin-bottom: 4px; color: #888; font-size: 12px; }
|
||||
.summary-card strong { color: #fff; font-size: 22px; }
|
||||
.subsection-title, .result-subsection-title { margin: 18px 0 10px; color: #bbb; font-size: 13px; font-weight: 700; }
|
||||
.match-table { margin-bottom: 20px; --el-table-bg-color: #222; --el-table-tr-bg-color: #222; --el-table-header-bg-color: #292929; --el-table-text-color: #ccc; --el-table-border-color: #333; }
|
||||
.summary-card { display: flex; min-height: 70px; flex-direction: column; justify-content: center; padding: 12px 16px; border: 1px solid #333f55; border-radius: 6px; background: #222; }
|
||||
.summary-label { margin-bottom: 4px; color: #5e6878; font-size: 12px; }
|
||||
.summary-card strong { color: #f5f8fc; font-size: 22px; }
|
||||
.subsection-title, .result-subsection-title { margin: 18px 0 10px; color: #a0acbe; font-size: 13px; font-weight: 700; }
|
||||
.match-table { margin-bottom: 20px; --el-table-bg-color: #222; --el-table-tr-bg-color: #222; --el-table-header-bg-color: #292929; --el-table-text-color: #c8d2e2; --el-table-border-color: #333; }
|
||||
.ok { color: #67c23a; }.fail, .error-text { color: #f56c6c; }
|
||||
.result-list-header { padding: 13px 0; border-bottom: 1px solid #303030; color: #eee; font-weight: 700; }
|
||||
.result-list-header { padding: 13px 0; border-bottom: 1px solid #333f55; color: #f5f8fc; font-weight: 700; }
|
||||
.task-list { margin: 0; padding: 0; list-style: none; }
|
||||
:deep(.task-item) { display: flex; align-items: center; justify-content: space-between; gap: 18px; min-height: 84px; padding: 14px 0; border-bottom: 1px solid #2b2b2b; }
|
||||
:deep(.split-result-main) { min-width: 0; }
|
||||
:deep(.id) { display: block; margin-bottom: 5px; color: #eee; font-weight: 700; }
|
||||
:deep(.files) { margin-top: 3px; color: #888; font-size: 12px; }
|
||||
:deep(.id) { display: block; margin-bottom: 5px; color: #f5f8fc; font-weight: 700; }
|
||||
:deep(.files) { margin-top: 3px; color: #5e6878; font-size: 12px; }
|
||||
:deep(.task-right) { display: flex; align-items: center; gap: 10px; }
|
||||
:deep(.status) { min-width: 88px; font-size: 12px; text-align: center; white-space: nowrap; }
|
||||
:deep(.status.success) { color: #67c23a; }:deep(.status.failed) { color: #f56c6c; }:deep(.status.running) { color: #e6a23c; }
|
||||
:deep(.download), :deep(.btn-delete) { padding: 5px 10px; border: 1px solid #444; border-radius: 4px; background: transparent; color: #ccc; cursor: pointer; }
|
||||
:deep(.download), :deep(.btn-delete) { padding: 5px 10px; border: 1px solid #444; border-radius: 4px; background: transparent; color: #c8d2e2; cursor: pointer; }
|
||||
:deep(.download) { border-color: #409eff; color: #8dc4ff; }:deep(.btn-delete) { color: #f56c6c; }
|
||||
@media (max-width: 900px) { .main-content { height: auto; flex-direction: column; }.left-panel { width: 100%; border-right: 0; }.clean-result-summary { grid-template-columns: repeat(2, 1fr); } }
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="shop-match" />
|
||||
<AmazonToolPageShell tool-id="time">
|
||||
<div class="main-content">
|
||||
<aside class="left-panel">
|
||||
<div class="section-title">店铺候选</div>
|
||||
@@ -102,13 +102,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from '@/pages/brand/components/BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import ZiniaoVersionSetting from '@/shared/components/ZiniaoVersionSetting.vue'
|
||||
import { LISTING_FILTER_OPTIONS, type ListingFilterValue } from '@/pages/brand/components/listingFilters'
|
||||
import { activateShopMatchTask, addShopMatchCandidate, createShopMatchTask, deleteShopMatchCandidate, deleteShopMatchHistory, deleteShopMatchTask, getShopMatchCountryPreference, getShopMatchDashboard, getShopMatchHistory, getShopMatchResultDownloadUrl, getShopMatchTaskProgressBatch, getShopMatchTasksBatch, listShopMatchCandidates, matchShopMatchShops, putShopMatchCountryPreference, type ShopMatchCandidateVo, type ShopMatchCreateTaskItem, type ShopMatchDashboardVo, type ShopMatchHistoryItem, type ShopMatchShopQueueItem, type ShopMatchTaskDetailVo } from '@/shared/api/java-modules'
|
||||
@@ -509,80 +510,80 @@ onUnmounted(() => { disposed = true; stopPolling(); stopScheduleHeartbeat(); cle
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-page { min-height: 100vh; background: #1a1a1a; }
|
||||
.module-page { min-height: 100vh; background: #151a25; }
|
||||
.main-content { display: flex; min-height: calc(100vh - 56px); height: calc(100vh - 56px); }
|
||||
.left-panel { width: 400px; background: #1e1e1e; padding: 20px; overflow-y: auto; border-right: 1px solid #2a2a2a; }
|
||||
.right-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; background: #1a1a1a; }
|
||||
.section-title { font-size: 13px; color: #bbb; margin-bottom: 10px; }
|
||||
.input-zone { border: 1px dashed #3a3a3a; border-radius: 10px; padding: 16px; background: #252525; margin-bottom: 20px; }
|
||||
.hint { color: #888; font-size: 12px; margin-bottom: 12px; line-height: 1.5; text-align: left; }
|
||||
.left-panel { width: 400px; background: #1c2333; padding: 20px; overflow-y: auto; border-right: 1px solid #2e3a52; }
|
||||
.right-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; background: #151a25; }
|
||||
.section-title { font-size: 13px; color: #a0acbe; margin-bottom: 10px; }
|
||||
.input-zone { border: 1px dashed #3e4a62; border-radius: 10px; padding: 16px; background: #2e3a52; margin-bottom: 20px; }
|
||||
.hint { color: #5e6878; font-size: 12px; margin-bottom: 12px; line-height: 1.5; text-align: left; }
|
||||
.country-pref-hint { margin-top: -4px; }
|
||||
.country-pref-checks { display: flex; flex-wrap: wrap; gap: 10px 16px; margin-bottom: 12px; }
|
||||
.country-check-row { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; font-size: 12px; color: #ccc; }
|
||||
.country-check-row { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; font-size: 12px; color: #c8d2e2; }
|
||||
.country-check-input { width: 16px; height: 16px; accent-color: #409eff; cursor: pointer; flex-shrink: 0; }
|
||||
.country-check-text { line-height: 1.3; }
|
||||
.country-order-panel { border: 1px solid #333; border-radius: 8px; padding: 10px 12px; background: #252525; margin-bottom: 14px; }
|
||||
.country-order-caption { font-size: 11px; color: #777; margin-bottom: 8px; }
|
||||
.country-order-panel { border: 1px solid #333; border-radius: 8px; padding: 10px 12px; background: #2e3a52; margin-bottom: 14px; }
|
||||
.country-order-caption { font-size: 11px; color: #5e6878; margin-bottom: 8px; }
|
||||
.country-order-list { display: flex; flex-direction: column; gap: 6px; }
|
||||
.country-drag-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px; background: #2c2c2c; border-radius: 6px; border: 1px solid #3a3a3a; cursor: grab; user-select: none; }
|
||||
.country-drag-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px; background: #2c2c2c; border-radius: 6px; border: 1px solid #3e4a62; cursor: grab; user-select: none; }
|
||||
.country-drag-row.dragging { opacity: 0.55; border-color: #3498db; }
|
||||
.drag-handle { color: #666; font-size: 12px; letter-spacing: -2px; }
|
||||
.country-drag-label { font-size: 12px; color: #ccc; }
|
||||
.country-pref-status { font-size: 11px; color: #888; margin-bottom: 10px; }
|
||||
.drag-handle { color: #5e6878; font-size: 12px; letter-spacing: -2px; }
|
||||
.country-drag-label { font-size: 12px; color: #c8d2e2; }
|
||||
.country-pref-status { font-size: 11px; color: #5e6878; margin-bottom: 10px; }
|
||||
.input-row { display: flex; gap: 10px; align-items: center; }
|
||||
.input-row :deep(.el-input) { flex: 1; }
|
||||
.opt-btn,.schedule-remove,.schedule-add { padding: 8px 12px; font-size: 12px; color: #ccc; background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 6px; cursor: pointer; white-space: nowrap; }
|
||||
.opt-btn,.schedule-remove,.schedule-add { padding: 8px 12px; font-size: 12px; color: #c8d2e2; background: #2e3a52; border: 1px solid #3e4a62; border-radius: 6px; cursor: pointer; white-space: nowrap; }
|
||||
.opt-btn:hover:not(:disabled),.schedule-remove:hover:not(:disabled),.schedule-add:hover:not(:disabled) { color: #3498db; border-color: #3498db; }
|
||||
.opt-btn:disabled,.schedule-remove:disabled,.schedule-add:disabled { opacity: 0.6; cursor: not-allowed; }
|
||||
.empty-candidates { color: #666; font-size: 13px; padding: 16px; border: 1px dashed #333; border-radius: 8px; margin-bottom: 16px; }
|
||||
.candidate-table-scroll { max-height: 280px; overflow: auto; margin-bottom: 16px; border-radius: 8px; border: 1px solid #2a2a2a; }
|
||||
.candidate-table { --el-table-bg-color: #252525; --el-table-tr-bg-color: #252525; --el-table-header-bg-color: #2a2a2a; --el-table-text-color: #ccc; --el-table-border-color: #333; }
|
||||
.schedule-switch { display: flex; align-items: center; gap: 8px; color: #ccc; font-size: 12px; margin-bottom: 12px; }
|
||||
.schedule-config { border: 1px solid #333; border-radius: 8px; padding: 12px; background: #252525; margin-bottom: 16px; }
|
||||
.empty-candidates { color: #5e6878; font-size: 13px; padding: 16px; border: 1px dashed #333; border-radius: 8px; margin-bottom: 16px; }
|
||||
.candidate-table-scroll { max-height: 280px; overflow: auto; margin-bottom: 16px; border-radius: 8px; border: 1px solid #2e3a52; }
|
||||
.candidate-table { --el-table-bg-color: #2e3a52; --el-table-tr-bg-color: #2e3a52; --el-table-header-bg-color: #2e3a52; --el-table-text-color: #c8d2e2; --el-table-border-color: #333; }
|
||||
.schedule-switch { display: flex; align-items: center; gap: 8px; color: #c8d2e2; font-size: 12px; margin-bottom: 12px; }
|
||||
.schedule-config { border: 1px solid #333; border-radius: 8px; padding: 12px; background: #2e3a52; margin-bottom: 16px; }
|
||||
.schedule-row { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
|
||||
.schedule-picker { flex: 1; }
|
||||
.schedule-picker :deep(.el-input__wrapper) { background-color: #2a2a2a; box-shadow: 0 0 0 1px #3a3a3a inset; }
|
||||
.schedule-picker :deep(.el-input__inner) { color: #ccc; }
|
||||
.schedule-picker :deep(.el-input__wrapper) { background-color: #2e3a52; box-shadow: 0 0 0 1px #3e4a62 inset; }
|
||||
.schedule-picker :deep(.el-input__inner) { color: #c8d2e2; }
|
||||
.schedule-add { margin-top: 2px; }
|
||||
.schedule-hint { margin-top: 10px; margin-bottom: 0; }
|
||||
.link-danger { background: none; border: none; color: #e74c3c; cursor: pointer; font-size: 12px; padding: 0; }
|
||||
.link-danger:hover { text-decoration: underline; }
|
||||
.run-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
|
||||
.btn-run { padding: 10px 18px; font-size: 14px; font-weight: 600; color: #fff; background: #3498db; border: none; border-radius: 8px; cursor: pointer; }
|
||||
.btn-run { padding: 10px 18px; font-size: 14px; font-weight: 600; color: #f5f8fc; background: #3498db; border: none; border-radius: 8px; cursor: pointer; }
|
||||
.btn-run:hover:not(:disabled) { background: #2980b9; }
|
||||
.btn-run:disabled { opacity: 0.55; cursor: not-allowed; }
|
||||
.btn-queue { background: #27ae60; }
|
||||
.btn-queue:hover:not(:disabled) { background: #219a52; }
|
||||
.loading-msg { margin-top: 12px; font-size: 12px; color: #777; line-height: 1.5; }
|
||||
.loading-msg { margin-top: 12px; font-size: 12px; color: #5e6878; line-height: 1.5; }
|
||||
.loading-msg code { font-size: 11px; color: #8fd3ff; }
|
||||
.queue-debug-card { margin-top: 18px; padding: 14px; border: 1px solid #2a2a2a; border-radius: 10px; background: #202020; }
|
||||
.queue-debug-card { margin-top: 18px; padding: 14px; border: 1px solid #2e3a52; border-radius: 10px; background: #222b3d; }
|
||||
.queue-debug-title { margin-bottom: 8px; }
|
||||
.queue-debug-line { color: #b8c1cc; 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: #141414; 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: #e0e0e0; border-bottom: 1px solid #2a2a2a; }
|
||||
.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 #2a2a2a; border-radius: 10px; background: #1e1e1e; }
|
||||
.summary-card strong { display: block; margin-top: 8px; font-size: 22px; color: #eaf4ff; }
|
||||
.summary-label { font-size: 12px; color: #8d8d8d; }
|
||||
.subsection-title { font-size: 13px; color: #999; margin: 8px 0 10px; }
|
||||
.empty-tasks { color: #666; font-size: 13px; padding: 16px; text-align: center; }
|
||||
.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-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: #2a2a2a; --el-table-text-color: #ccc; --el-table-border-color: #333; }
|
||||
.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 #2a2a2a; border-radius: 10px; background: #1e1e1e; min-height: 200px; margin-top: 8px; }
|
||||
.result-list-header { padding: 12px 16px; border-bottom: 1px solid #2a2a2a; font-size: 14px; color: #ddd; }
|
||||
.result-list-wrap { border: 1px solid #2e3a52; border-radius: 10px; background: #1c2333; min-height: 200px; 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: #888; margin-bottom: 8px; }
|
||||
.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 #2a2a2a; border-radius: 8px; margin-bottom: 8px; background: #222; }
|
||||
.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: #e0e0e0; font-size: 13px; }
|
||||
.files { font-size: 12px; color: #888; }
|
||||
.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; }
|
||||
@@ -592,6 +593,6 @@ onUnmounted(() => { disposed = true; stopPolling(); stopScheduleHeartbeat(); cle
|
||||
.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 #2a2a2a; } .clean-result-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); } .schedule-row { flex-direction: column; align-items: stretch; } }
|
||||
@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)); } .schedule-row { flex-direction: column; align-items: stretch; } }
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="similar-asin" />
|
||||
<AmazonToolPageShell tool-id="source">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel left-panel--with-template">
|
||||
@@ -181,13 +181,14 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from '@/pages/brand/components/BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import ModuleTemplateDownload from '@/shared/components/ModuleTemplateDownload.vue'
|
||||
import {
|
||||
activateSimilarAsinTask,
|
||||
@@ -1126,7 +1127,7 @@ onUnmounted(() => {
|
||||
<style scoped>
|
||||
.module-page {
|
||||
min-height: 100vh;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
@@ -1137,16 +1138,16 @@ onUnmounted(() => {
|
||||
|
||||
.left-panel {
|
||||
width: 400px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #2a2a2a;
|
||||
border-right: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -1154,15 +1155,15 @@ onUnmounted(() => {
|
||||
.section-title,
|
||||
.subsection-title {
|
||||
font-size: 13px;
|
||||
color: #bbb;
|
||||
color: #a0acbe;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.upload-zone {
|
||||
border: 1px dashed #3a3a3a;
|
||||
border: 1px dashed #3e4a62;
|
||||
border-radius: 10px;
|
||||
padding: 18px;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
@@ -1171,7 +1172,7 @@ onUnmounted(() => {
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #2f3a34;
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(135deg, #222a25, #202020);
|
||||
background: linear-gradient(135deg, #222a25, #222b3d);
|
||||
}
|
||||
|
||||
.aliprice-card {
|
||||
@@ -1187,7 +1188,7 @@ onUnmounted(() => {
|
||||
grid-template-columns: 44px minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: #bbb;
|
||||
color: #a0acbe;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@@ -1204,7 +1205,7 @@ onUnmounted(() => {
|
||||
border-radius: 6px;
|
||||
outline: none;
|
||||
background: #1b1b1b;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
@@ -1253,7 +1254,7 @@ onUnmounted(() => {
|
||||
height: 24px;
|
||||
flex: 0 0 auto;
|
||||
border-radius: 999px;
|
||||
background: #3a3a3a;
|
||||
background: #3e4a62;
|
||||
box-shadow: inset 0 0 0 1px #4b4b4b;
|
||||
transition: background .2s ease, box-shadow .2s ease;
|
||||
}
|
||||
@@ -1284,7 +1285,7 @@ onUnmounted(() => {
|
||||
.loading-msg,
|
||||
.files,
|
||||
.muted {
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
@@ -1316,14 +1317,14 @@ onUnmounted(() => {
|
||||
|
||||
.opt-btn {
|
||||
padding: 8px 14px;
|
||||
color: #ccc;
|
||||
background: #2a2a2a;
|
||||
border: 1px solid #3a3a3a;
|
||||
color: #c8d2e2;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #3e4a62;
|
||||
}
|
||||
|
||||
.btn-run {
|
||||
padding: 10px 18px;
|
||||
color: #fff;
|
||||
color: #f5f8fc;
|
||||
background: #3498db;
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -1339,7 +1340,7 @@ onUnmounted(() => {
|
||||
|
||||
.selected-files {
|
||||
margin-top: 14px;
|
||||
color: #999;
|
||||
color: #a0acbe;
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
}
|
||||
@@ -1353,10 +1354,10 @@ onUnmounted(() => {
|
||||
.queue-payload {
|
||||
margin-top: 14px;
|
||||
padding: 12px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
background: #202020;
|
||||
color: #b8c1cc;
|
||||
background: #222b3d;
|
||||
color: #a0acbe;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -1369,10 +1370,10 @@ onUnmounted(() => {
|
||||
|
||||
.panel-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.task-list-wrap {
|
||||
@@ -1390,27 +1391,27 @@ onUnmounted(() => {
|
||||
|
||||
.summary-card {
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.summary-card strong {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
color: #eaf4ff;
|
||||
color: #f5f8fc;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
color: #8d8d8d;
|
||||
color: #5e6878;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.result-list-wrap {
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
min-height: 180px;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
@@ -1419,13 +1420,13 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
color: #ddd;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
color: #c8d2e2;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.empty-tasks {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
padding: 18px;
|
||||
text-align: center;
|
||||
@@ -1443,7 +1444,7 @@ onUnmounted(() => {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
background: #222;
|
||||
@@ -1455,7 +1456,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.id {
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -1490,7 +1491,7 @@ onUnmounted(() => {
|
||||
|
||||
.status.pending {
|
||||
background: rgba(149, 165, 166, .18);
|
||||
color: #bdc3c7;
|
||||
color: #a0acbe;
|
||||
}
|
||||
|
||||
.result-hint {
|
||||
@@ -1516,7 +1517,7 @@ onUnmounted(() => {
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
background: #303030;
|
||||
background: #333f55;
|
||||
border: 1px solid #3b3b3b;
|
||||
}
|
||||
|
||||
@@ -1535,7 +1536,7 @@ onUnmounted(() => {
|
||||
|
||||
.download {
|
||||
padding: 6px 10px;
|
||||
color: #d6ecff;
|
||||
color: #c8d2e2;
|
||||
background: rgba(52, 152, 219, .18);
|
||||
}
|
||||
|
||||
@@ -1554,7 +1555,7 @@ onUnmounted(() => {
|
||||
.left-panel {
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.clean-result-summary {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="split" />
|
||||
<AmazonToolPageShell tool-id="split">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel">
|
||||
@@ -159,13 +159,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from './BrandTopBar.vue'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import { expandBrandFolderRecursive, type BrandExpandFolderItem } from '@/shared/api/brand'
|
||||
import { deleteSplitHistory, getExcelInfo, getSplitHistory, getSplitResultDownloadUrl, runSplit, type SplitResultItem, type SplitRunVo } from '@/shared/api/java-modules'
|
||||
import { getPywebviewApi, type UploadedJavaFile } from '@/shared/bridges/pywebview'
|
||||
@@ -391,7 +392,7 @@ onMounted(() => {
|
||||
<style scoped>
|
||||
.module-page {
|
||||
min-height: 100vh;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
@@ -402,10 +403,10 @@ onMounted(() => {
|
||||
|
||||
.left-panel {
|
||||
width: 380px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #2a2a2a;
|
||||
border-right: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
@@ -413,32 +414,32 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
background: #1a1a1a;
|
||||
background: #151a25;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
color: #bbb;
|
||||
color: #a0acbe;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.upload-zone {
|
||||
border: 1px dashed #3a3a3a;
|
||||
border: 1px dashed #3e4a62;
|
||||
border-radius: 10px;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
background: #252525;
|
||||
background: #2e3a52;
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.upload-zone:hover {
|
||||
border-color: #3498db;
|
||||
background: #2a2a2a;
|
||||
background: #2e3a52;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.5;
|
||||
@@ -463,9 +464,9 @@ onMounted(() => {
|
||||
.opt-btn {
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
background: #2a2a2a;
|
||||
border: 1px solid #3a3a3a;
|
||||
color: #c8d2e2;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #3e4a62;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
@@ -483,7 +484,7 @@ onMounted(() => {
|
||||
.selected-files {
|
||||
margin-top: 14px;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
max-height: 112px;
|
||||
overflow-y: auto;
|
||||
text-align: left;
|
||||
@@ -496,7 +497,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.more-line {
|
||||
color: #b8c1cc;
|
||||
color: #a0acbe;
|
||||
}
|
||||
|
||||
.split-selected-files {
|
||||
@@ -512,8 +513,8 @@ onMounted(() => {
|
||||
.column-option-group {
|
||||
padding: 14px;
|
||||
border-radius: 10px;
|
||||
background: #252525;
|
||||
border: 1px solid #2a2a2a;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.column-toolbar {
|
||||
@@ -526,7 +527,7 @@ onMounted(() => {
|
||||
|
||||
.column-count {
|
||||
font-size: 12px;
|
||||
color: #a9b4bf;
|
||||
color: #a0acbe;
|
||||
}
|
||||
|
||||
.column-actions {
|
||||
@@ -545,8 +546,8 @@ onMounted(() => {
|
||||
.empty-column-state {
|
||||
padding: 16px 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px dashed #3a3a3a;
|
||||
background: #202020;
|
||||
border: 1px dashed #3e4a62;
|
||||
background: #222b3d;
|
||||
color: #7f8790;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
@@ -558,8 +559,8 @@ onMounted(() => {
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #2f2f2f;
|
||||
background: #202020;
|
||||
border: 1px solid #323e58;
|
||||
background: #222b3d;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-size: 13px;
|
||||
@@ -587,16 +588,16 @@ onMounted(() => {
|
||||
cursor: pointer;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
background: #252525;
|
||||
border: 1px solid #2a2a2a;
|
||||
background: #2e3a52;
|
||||
border: 1px solid #2e3a52;
|
||||
margin-bottom: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.radio-item:hover,
|
||||
.radio-item.active {
|
||||
background: #2a2a2a;
|
||||
border-color: #3a3a3a;
|
||||
background: #2e3a52;
|
||||
border-color: #3e4a62;
|
||||
}
|
||||
|
||||
.radio-item input {
|
||||
@@ -605,13 +606,13 @@ onMounted(() => {
|
||||
|
||||
.label {
|
||||
font-weight: 500;
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-top: 2px;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
@@ -623,17 +624,17 @@ onMounted(() => {
|
||||
gap: 10px;
|
||||
padding: 14px 16px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #2f2f2f;
|
||||
background: #252525;
|
||||
border: 1px solid #323e58;
|
||||
background: #2e3a52;
|
||||
}
|
||||
|
||||
.split-number-input {
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #3a3a3a;
|
||||
border: 1px solid #3e4a62;
|
||||
background: #1b1b1b;
|
||||
color: #eaf4ff;
|
||||
color: #f5f8fc;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
@@ -664,7 +665,7 @@ onMounted(() => {
|
||||
.btn-run {
|
||||
padding: 10px 22px;
|
||||
background: #3498db;
|
||||
color: #fff;
|
||||
color: #f5f8fc;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -675,7 +676,7 @@ onMounted(() => {
|
||||
|
||||
.btn-run:disabled {
|
||||
background: #555;
|
||||
color: #999;
|
||||
color: #a0acbe;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -686,10 +687,10 @@ onMounted(() => {
|
||||
|
||||
.panel-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
.task-list-wrap {
|
||||
@@ -710,10 +711,10 @@ onMounted(() => {
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.split-result-item {
|
||||
@@ -734,19 +735,19 @@ onMounted(() => {
|
||||
.id {
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
color: #e0e0e0;
|
||||
color: #f5f8fc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.files {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: #5e6878;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
@@ -813,7 +814,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.empty-tasks {
|
||||
color: #666;
|
||||
color: #5e6878;
|
||||
font-size: 13px;
|
||||
padding: 24px 8px;
|
||||
}
|
||||
@@ -831,27 +832,27 @@ onMounted(() => {
|
||||
|
||||
.summary-card {
|
||||
padding: 16px 18px;
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
}
|
||||
|
||||
.summary-card strong {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
font-size: 24px;
|
||||
color: #eaf4ff;
|
||||
color: #f5f8fc;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: #8d8d8d;
|
||||
color: #5e6878;
|
||||
}
|
||||
|
||||
.result-list-wrap {
|
||||
border: 1px solid #2a2a2a;
|
||||
border: 1px solid #2e3a52;
|
||||
border-radius: 10px;
|
||||
background: #1e1e1e;
|
||||
background: #1c2333;
|
||||
min-height: 260px;
|
||||
}
|
||||
|
||||
@@ -861,9 +862,9 @@ onMounted(() => {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
font-size: 14px;
|
||||
color: #ddd;
|
||||
color: #c8d2e2;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
@@ -875,7 +876,7 @@ onMounted(() => {
|
||||
.left-panel {
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
border-bottom: 1px solid #2e3a52;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
|
||||
@@ -56,6 +56,7 @@ type ActiveNavKey =
|
||||
| 'withdraw'
|
||||
| 'collect-data'
|
||||
| 'image-video'
|
||||
| 'amazon-console'
|
||||
|
||||
type NavItem = {
|
||||
key: string
|
||||
@@ -94,9 +95,9 @@ const navGroups: ReadonlyArray<NavGroup> = [
|
||||
label: '前端工具',
|
||||
items: [
|
||||
{ key: 'collect-data', label: '采集数据', href: '/new_web_source/collect-data.html' },
|
||||
{ key: 'variant-collection', label: 'ASIN变体采集', href: '/web_source/variant-collection.html' },
|
||||
{ key: 'variant-collection', label: 'ASIN变体采集', href: '/new_web_source/variant-collection.html' },
|
||||
// { key: 'image-video', label: '视频', href: '/new_web_source/image-video.html' },
|
||||
{ key: 'brand', label: '品牌检测', href: '/brand' },
|
||||
{ key: 'brand', label: '品牌检测', href: '/new_web_source/brand.html' },
|
||||
{ key: 'appearance-patent', label: '外观专利检测', href: '/new_web_source/appearance-patent.html' },
|
||||
{ key: 'similar-asin', label: '货源查询', href: '/new_web_source/similar-asin.html' },
|
||||
{ key: 'dedupe', label: '数据去重', href: '/new_web_source/dedupe.html' },
|
||||
|
||||
@@ -0,0 +1,688 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<AmazonToolPageShell tool-id="variant">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel">
|
||||
<div class="section-title">输入方式</div>
|
||||
<div class="input-tabs">
|
||||
<button
|
||||
type="button"
|
||||
class="input-tab"
|
||||
:class="{ active: inputMode === 'asin' }"
|
||||
@click="inputMode = 'asin'"
|
||||
>粘贴 ASIN</button>
|
||||
<button
|
||||
type="button"
|
||||
class="input-tab"
|
||||
:class="{ active: inputMode === 'file' }"
|
||||
@click="inputMode = 'file'"
|
||||
>Excel 文件</button>
|
||||
</div>
|
||||
|
||||
<div v-if="inputMode === 'asin'" class="input-panel">
|
||||
<div class="hint">每行一个 ASIN,支持批量粘贴。</div>
|
||||
<textarea
|
||||
v-model="asinText"
|
||||
class="asin-textarea"
|
||||
rows="9"
|
||||
placeholder="每行一个 ASIN,例如: B0xxxxxxxx"
|
||||
></textarea>
|
||||
<div class="asin-count">已输入 <b>{{ asinLines.length }}</b> 个 ASIN</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="input-panel">
|
||||
<div class="hint">选择含 ASIN 的 Excel(需含表头行)或多个文件。</div>
|
||||
<div class="btns">
|
||||
<button type="button" class="opt-btn" :disabled="!hasBridge" @click="selectFiles">
|
||||
选择 Excel
|
||||
</button>
|
||||
</div>
|
||||
<div class="selected-files">
|
||||
<template v-if="selectedFiles.length">
|
||||
<span v-for="file in selectedFiles" :key="file" :title="file">{{ baseName(file) }}</span>
|
||||
</template>
|
||||
<span v-else>暂未选择文件</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-title">采集国家(可多选)</div>
|
||||
<div class="country-group">
|
||||
<label
|
||||
v-for="country in COUNTRY_OPTIONS"
|
||||
:key="country.code"
|
||||
class="country-chip"
|
||||
:class="{ selected: selectedCountries.includes(country.code) }"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:value="country.code"
|
||||
v-model="selectedCountries"
|
||||
/>
|
||||
<span>{{ country.label }}({{ country.code }})</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="country-hint" :class="{ error: selectedCountries.length === 0 }">
|
||||
{{ selectedCountries.length === 0 ? '至少选择一个国家' : `已选 ${selectedCountries.length} 个国家` }}
|
||||
</p>
|
||||
|
||||
<div v-if="!hasBridge" class="bridge-tip">
|
||||
变体采集在本机客户端执行,当前浏览器环境无桌面桥接,仅可预览页面。
|
||||
</div>
|
||||
|
||||
<div class="run-row">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-run"
|
||||
:disabled="!hasBridge || submitting || !canSubmit"
|
||||
@click="submitTask"
|
||||
>
|
||||
{{ submitting ? '提交中...' : '开始采集(添加到任务队列)' }}
|
||||
</button>
|
||||
</div>
|
||||
<p class="loading-msg">
|
||||
任务在远程批处理队列执行,提交后到右侧查看进度并下载结果文件。
|
||||
</p>
|
||||
</aside>
|
||||
|
||||
<section class="right-panel">
|
||||
<div class="panel-header">
|
||||
<span>变体采集任务</span>
|
||||
<div class="panel-actions">
|
||||
<button type="button" class="btn-refresh" @click="refreshList">刷新任务状态</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-list-wrap">
|
||||
<div v-if="!currentTasks.length" class="empty-tasks">暂无变体采集任务</div>
|
||||
<ul v-else class="task-list">
|
||||
<li v-for="item in currentTasks" :key="`vc-${item.task_id}`" class="task-item">
|
||||
<div class="left">
|
||||
<span class="id">任务 {{ item.task_id }}</span>
|
||||
<div v-if="taskMeta(item).source" class="files">来源:{{ taskMeta(item).source }}</div>
|
||||
<div class="files">采集国家:{{ countryText(item) || '-' }}</div>
|
||||
<div class="files">更新时间:{{ item.update_time || formatDateTime(item.updated_at) }}</div>
|
||||
<div v-if="latestMessage(item)" class="files">{{ latestMessage(item) }}</div>
|
||||
</div>
|
||||
<div class="task-right">
|
||||
<span class="status" :class="statusClass(item.status)">{{ statusText(item.status) }}</span>
|
||||
<button type="button" class="act-btn" @click="refreshTask(item)">更新</button>
|
||||
<button type="button" class="act-btn" @click="showDetail(item)">详情</button>
|
||||
<button
|
||||
v-if="hasResultFiles(item)"
|
||||
type="button"
|
||||
class="act-btn ok"
|
||||
@click="downloadResult(item)"
|
||||
>下载结果</button>
|
||||
<button
|
||||
v-if="hasSourceFile(item)"
|
||||
type="button"
|
||||
class="act-btn"
|
||||
@click="downloadSource(item)"
|
||||
>下载源文件</button>
|
||||
<button type="button" class="act-btn warn" @click="reexportTask(item)">重新导出</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div v-if="currentTasks.length" class="pagination">
|
||||
<span class="page-info">共 {{ totalCount }} 条 · 第 {{ currentPage }} / {{ totalPages }} 页</span>
|
||||
<div class="page-btns">
|
||||
<button type="button" class="page-btn" :disabled="currentPage <= 1" @click="goPage(currentPage - 1)">上一页</button>
|
||||
<button type="button" class="page-btn" :disabled="currentPage >= totalPages" @click="goPage(currentPage + 1)">下一页</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
|
||||
<el-dialog v-model="detailVisible" title="任务详情" width="620px">
|
||||
<div v-if="detailItem" class="detail-body">
|
||||
<div class="kv-row"><span class="kv-key">任务 ID</span><span class="kv-val">{{ detailItem.task_id }}</span></div>
|
||||
<div class="kv-row"><span class="kv-key">状态</span><span class="kv-val">{{ statusText(detailItem.status) }}</span></div>
|
||||
<div class="kv-row"><span class="kv-key">采集国家</span><span class="kv-val">{{ countryText(detailItem) || '-' }}</span></div>
|
||||
<div class="kv-row"><span class="kv-key">更新时间</span><span class="kv-val">{{ detailItem.update_time || formatDateTime(detailItem.updated_at) }}</span></div>
|
||||
<div class="kv-row"><span class="kv-key">进度信息</span><span class="kv-val">{{ latestMessage(detailItem) || '-' }}</span></div>
|
||||
<template v-if="resultFiles(detailItem).length">
|
||||
<div class="kv-section">结果文件</div>
|
||||
<div v-for="(url, index) in resultFiles(detailItem)" :key="index" class="kv-row">
|
||||
<span class="kv-key">文件 {{ index + 1 }}</span>
|
||||
<a class="kv-val link" :href="url" target="_blank" rel="noopener">{{ url }}</a>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="detailItem.task_data?.file_url">
|
||||
<div class="kv-section">源文件</div>
|
||||
<div class="kv-row">
|
||||
<span class="kv-key">源文件</span>
|
||||
<a class="kv-val link" :href="detailItem.task_data.file_url" target="_blank" rel="noopener">{{ detailItem.task_data.file_url }}</a>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue'
|
||||
import {
|
||||
getPywebviewApi,
|
||||
type VariantTaskListItem,
|
||||
} from '@/shared/bridges/pywebview'
|
||||
import { saveUrlWithProgress } from '@/shared/utils/download-progress'
|
||||
|
||||
/** 本地任务记录:桥接返回的列表项 + 前端维护字段 */
|
||||
type VariantEntry = VariantTaskListItem & {
|
||||
updated_at?: number
|
||||
message?: string
|
||||
meta?: { source?: string; execute_country?: string[] }
|
||||
}
|
||||
|
||||
const COUNTRY_OPTIONS = [
|
||||
{ code: 'DE', label: '德国' },
|
||||
{ code: 'UK', label: '英国' },
|
||||
{ code: 'FR', label: '法国' },
|
||||
{ code: 'IT', label: '意大利' },
|
||||
{ code: 'ES', label: '西班牙' },
|
||||
] as const
|
||||
|
||||
const PAGE_SIZE = 8
|
||||
|
||||
const inputMode = ref<'asin' | 'file'>('asin')
|
||||
const asinText = ref('')
|
||||
const selectedFiles = ref<string[]>([])
|
||||
const selectedCountries = ref<string[]>([])
|
||||
const submitting = ref(false)
|
||||
|
||||
const tasks = reactive<Record<string, VariantEntry>>({})
|
||||
const currentTaskIds = ref<string[]>([])
|
||||
const currentPage = ref(1)
|
||||
const totalPages = ref(1)
|
||||
const totalCount = ref(0)
|
||||
const isLoading = ref(false)
|
||||
|
||||
const detailVisible = ref(false)
|
||||
const detailItem = ref<VariantEntry | null>(null)
|
||||
|
||||
const hasBridge = computed(() => Boolean(getPywebviewApi()?.vc_start_collect))
|
||||
const asinLines = computed(() => asinText.value.split(/\r?\n/).map((s) => s.trim()).filter(Boolean))
|
||||
const currentTasks = computed(() =>
|
||||
currentTaskIds.value.map((id) => tasks[id]).filter((item): item is VariantEntry => Boolean(item)),
|
||||
)
|
||||
const canSubmit = computed(() =>
|
||||
inputMode.value === 'asin' ? asinLines.value.length > 0 : selectedFiles.value.length > 0,
|
||||
)
|
||||
|
||||
let autoTimer: number | null = null
|
||||
let refreshTimer: number | null = null
|
||||
|
||||
function uid() {
|
||||
const raw = typeof window === 'undefined' ? '' : window.localStorage.getItem('uid') || ''
|
||||
return raw.trim()
|
||||
}
|
||||
|
||||
function baseName(path: string) {
|
||||
return path.split(/[\\/]/).pop() || path
|
||||
}
|
||||
|
||||
function statusText(status?: number | string) {
|
||||
const numeric = Number(status)
|
||||
if (numeric === 0 || String(status).toLowerCase() === 'pending' || String(status).toLowerCase() === 'queued') return '排队中'
|
||||
if (numeric === 1 || String(status).toLowerCase() === 'running' || String(status).toLowerCase() === 'processing') return '执行中'
|
||||
if (numeric === 2 || ['success', 'completed', 'done'].includes(String(status).toLowerCase())) return '已完成'
|
||||
if (numeric === 3 || ['failed', 'error'].includes(String(status).toLowerCase())) return '失败'
|
||||
if (['cancelled', 'stopped'].includes(String(status).toLowerCase())) return '已取消'
|
||||
if (status === 0 || status === 1 || status === 2 || status === 3) return statusText(String(status))
|
||||
return String(status ?? '未知')
|
||||
}
|
||||
|
||||
function statusClass(status?: number | string) {
|
||||
const numeric = Number(status)
|
||||
if (numeric === 1 || String(status).toLowerCase() === 'running') return 'running'
|
||||
if (numeric === 2 || ['success', 'completed', 'done'].includes(String(status).toLowerCase())) return 'success'
|
||||
if (numeric === 3 || ['failed', 'error'].includes(String(status).toLowerCase())) return 'failed'
|
||||
if (['cancelled', 'stopped'].includes(String(status).toLowerCase())) return 'cancelled'
|
||||
return 'pending'
|
||||
}
|
||||
|
||||
function isBusy(status?: number | string) {
|
||||
const numeric = Number(status)
|
||||
return numeric === 0 || numeric === 1 || String(status).toLowerCase() === 'pending' || String(status).toLowerCase() === 'running'
|
||||
}
|
||||
|
||||
function toArray(value?: string[] | string) {
|
||||
if (!value) return []
|
||||
return Array.isArray(value) ? value.filter(Boolean) : [value]
|
||||
}
|
||||
|
||||
function countriesOf(item?: VariantEntry) {
|
||||
return item?.meta?.execute_country || item?.task_data?.execute_country || []
|
||||
}
|
||||
|
||||
function countryText(item?: VariantEntry) {
|
||||
return countriesOf(item).join('、') || '-'
|
||||
}
|
||||
|
||||
function latestMessage(item: VariantEntry) {
|
||||
const raw = item.res_mes || item.message
|
||||
return (raw || '').trim() || '等待后台处理…'
|
||||
}
|
||||
|
||||
function taskMeta(item: VariantEntry) {
|
||||
return { source: item.meta?.source || '' }
|
||||
}
|
||||
|
||||
function resultFiles(item?: VariantEntry) {
|
||||
return toArray(item?.file_url)
|
||||
}
|
||||
|
||||
function hasResultFiles(item: VariantTaskListItem) {
|
||||
const resType = Number(item.res_type)
|
||||
return (resType === 1 || String(item.res_type) === '1') && resultFiles(item).length > 0
|
||||
}
|
||||
|
||||
function hasSourceFile(item: VariantTaskListItem) {
|
||||
return Boolean(item.task_data?.file_url)
|
||||
}
|
||||
|
||||
function formatDateTime(value?: number | string) {
|
||||
if (!value) return '-'
|
||||
const ms = typeof value === 'number' ? value : Number(new Date(String(value)))
|
||||
if (!Number.isFinite(ms)) return String(value)
|
||||
const date = new Date(ms)
|
||||
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}`
|
||||
}
|
||||
|
||||
async function selectFiles() {
|
||||
const bridge = getPywebviewApi()
|
||||
if (!bridge?.vc_select_input_files) {
|
||||
ElMessage.warning('当前环境不支持文件选择,请在本机客户端中打开')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const files = await bridge.vc_select_input_files()
|
||||
selectedFiles.value = files || []
|
||||
if (selectedFiles.value.length) {
|
||||
ElMessage.success(`已选择 ${selectedFiles.value.length} 个文件`)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '选择文件失败')
|
||||
}
|
||||
}
|
||||
|
||||
function registerTask(taskId: string, meta?: { source?: string; execute_country?: string[] }) {
|
||||
if (!taskId) return
|
||||
if (!tasks[taskId]) {
|
||||
tasks[taskId] = { task_id: taskId, status: 0, updated_at: Date.now(), meta: meta || {} }
|
||||
} else if (meta) {
|
||||
tasks[taskId].meta = { ...tasks[taskId].meta, ...meta }
|
||||
}
|
||||
}
|
||||
|
||||
async function submitTask() {
|
||||
if (!hasBridge.value) {
|
||||
ElMessage.warning('请在桌面客户端中打开本页面执行变体采集')
|
||||
return
|
||||
}
|
||||
if (!selectedCountries.value.length) {
|
||||
ElMessage.warning('请至少选择一个采集国家')
|
||||
return
|
||||
}
|
||||
if (!uid()) {
|
||||
ElMessage.warning('未获取到用户信息,请重新登录后再试')
|
||||
return
|
||||
}
|
||||
const bridge = getPywebviewApi()!
|
||||
const params: {
|
||||
mode: 'asin' | 'file'
|
||||
countries: string[]
|
||||
uid: string
|
||||
asins?: string[]
|
||||
files?: string[]
|
||||
} = {
|
||||
mode: inputMode.value,
|
||||
countries: selectedCountries.value,
|
||||
uid: uid(),
|
||||
}
|
||||
if (inputMode.value === 'asin') {
|
||||
params.asins = asinLines.value
|
||||
} else {
|
||||
params.files = selectedFiles.value
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
const res = await bridge.vc_start_collect!(params)
|
||||
if (res?.success) {
|
||||
const added = res.tasks || []
|
||||
ElMessage.success(`已添加 ${added.length} 个任务`)
|
||||
added.forEach((task) => {
|
||||
registerTask(task.task_id, { source: task.source, execute_country: task.execute_country })
|
||||
})
|
||||
if (inputMode.value === 'asin') {
|
||||
asinText.value = ''
|
||||
} else {
|
||||
selectedFiles.value = []
|
||||
}
|
||||
await loadTasks(1)
|
||||
} else {
|
||||
throw new Error(res?.error || '添加任务失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '添加任务失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshTask(item: VariantTaskListItem) {
|
||||
const bridge = getPywebviewApi()
|
||||
if (!bridge?.vc_query_task) return
|
||||
try {
|
||||
const res = await bridge.vc_query_task(item.task_id)
|
||||
if (!res?.success) {
|
||||
ElMessage.error(res?.error || '查询失败')
|
||||
return
|
||||
}
|
||||
const entry = tasks[item.task_id] || { task_id: item.task_id, meta: {} }
|
||||
if (res.status !== undefined && res.status !== null) {
|
||||
entry.status = res.status
|
||||
}
|
||||
const data = res.data
|
||||
if (data && typeof data === 'object') {
|
||||
entry.message = (data as { message?: string }).message || (data as { msg?: string }).msg || ''
|
||||
if (data.res_mes) {
|
||||
entry.res_mes = data.res_mes
|
||||
entry.message = data.res_mes
|
||||
}
|
||||
if (data.res_type !== undefined) entry.res_type = data.res_type
|
||||
if (data.file_url) entry.file_url = data.file_url
|
||||
if (data.task_data) entry.task_data = data.task_data
|
||||
} else if (typeof data === 'string') {
|
||||
entry.message = data
|
||||
}
|
||||
entry.updated_at = Date.now()
|
||||
tasks[item.task_id] = entry
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '查询失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTasks(page?: number) {
|
||||
const bridge = getPywebviewApi()
|
||||
if (!bridge?.vc_list_tasks) return
|
||||
if (isLoading.value) return
|
||||
if (!uid()) return
|
||||
isLoading.value = true
|
||||
try {
|
||||
const targetPage = page || currentPage.value || 1
|
||||
const res = await bridge.vc_list_tasks({ uid: uid(), page: targetPage, page_size: PAGE_SIZE })
|
||||
if (!res?.success) return
|
||||
currentPage.value = res.page || targetPage || 1
|
||||
totalPages.value = Math.max(1, res.total_page || 1)
|
||||
totalCount.value = res.total || (res.tasks || []).length
|
||||
|
||||
const ids: string[] = []
|
||||
;(res.tasks || []).forEach((item) => {
|
||||
if (!item.task_id) return
|
||||
registerTask(item.task_id, {})
|
||||
const entry = tasks[item.task_id]
|
||||
entry.status = item.status
|
||||
if (item.update_time) entry.update_time = item.update_time
|
||||
entry.res_type = item.res_type
|
||||
entry.res_mes = item.res_mes
|
||||
entry.file_url = item.file_url
|
||||
entry.task_data = item.task_data
|
||||
if (item.res_mes) entry.message = item.res_mes
|
||||
entry.updated_at = Date.now()
|
||||
ids.push(item.task_id)
|
||||
})
|
||||
currentTaskIds.value = ids
|
||||
} catch {
|
||||
/* 静默:列表加载失败时保留现状 */
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function goPage(page: number) {
|
||||
if (page < 1 || page > totalPages.value) return
|
||||
void loadTasks(page)
|
||||
}
|
||||
|
||||
function refreshList() {
|
||||
ElMessage.info('正在刷新任务状态…')
|
||||
void loadTasks(currentPage.value || 1)
|
||||
}
|
||||
|
||||
async function downloadResult(item: VariantTaskListItem) {
|
||||
const bridge = getPywebviewApi()
|
||||
const urls = resultFiles(item)
|
||||
if (!bridge?.vc_download_files_as_zip) {
|
||||
ElMessage.warning('当前环境不支持下载,请在桌面客户端中打开')
|
||||
return
|
||||
}
|
||||
if (!urls.length) {
|
||||
ElMessage.warning('没有可下载的结果文件')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const downloadId = `res_${item.task_id}_${Date.now()}`
|
||||
const res = await bridge.vc_download_files_as_zip({
|
||||
urls,
|
||||
zip_name: `variant_collection_${item.task_id}.zip`,
|
||||
download_id: downloadId,
|
||||
})
|
||||
if (res?.success) {
|
||||
ElMessage.success('结果已打包保存')
|
||||
} else {
|
||||
throw new Error(res?.error || '下载失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '下载失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadSource(item: VariantTaskListItem) {
|
||||
const srcUrl = item.task_data?.file_url
|
||||
if (!srcUrl) {
|
||||
ElMessage.warning('没有可下载的源文件')
|
||||
return
|
||||
}
|
||||
const name = srcUrl.split(/[\\/?]/).filter(Boolean).pop() || 'source.xlsx'
|
||||
const result = await saveUrlWithProgress(srcUrl, name, `src_${item.task_id}_${Date.now()}`)
|
||||
if (result.success) {
|
||||
ElMessage.success(`源文件已保存:${result.path || name}`)
|
||||
} else if (result.error && result.error !== '用户取消') {
|
||||
ElMessage.error(result.error)
|
||||
}
|
||||
}
|
||||
|
||||
async function reexportTask(item: VariantTaskListItem) {
|
||||
const bridge = getPywebviewApi()
|
||||
if (!bridge?.vc_export_task) {
|
||||
ElMessage.warning('当前环境不支持导出,请在桌面客户端中打开')
|
||||
return
|
||||
}
|
||||
const numeric = Number(item.status)
|
||||
if (numeric === 1) {
|
||||
try {
|
||||
await ElMessageBox.confirm('任务正在执行中,导出会终止任务,是否继续导出?', '重新导出', { type: 'warning' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
}
|
||||
try {
|
||||
const startRes = await bridge.vc_export_task(item.task_id)
|
||||
if (!startRes?.success || !startRes.file_id) {
|
||||
throw new Error(startRes?.error || '发起导出失败')
|
||||
}
|
||||
ElMessage.info('已发起导出,正在轮询结果…')
|
||||
const result = await pollExport(bridge, startRes.file_id)
|
||||
if (result && Number(result.status) === 2 && result.file_url) {
|
||||
const name = result.file_name || result.file_url.split(/[\\/?]/).filter(Boolean).pop() || 'export.xlsx'
|
||||
const dlRes = await saveUrlWithProgress(result.file_url, name, `exp_${startRes.file_id}`)
|
||||
if (dlRes.success) {
|
||||
ElMessage.success(`导出文件已保存:${dlRes.path || name}`)
|
||||
} else if (dlRes.error && dlRes.error !== '用户取消') {
|
||||
ElMessage.error(dlRes.error)
|
||||
}
|
||||
void loadTasks(currentPage.value || 1)
|
||||
} else if (result && Number(result.status) === 3) {
|
||||
ElMessage.error(result.error || '后台导出失败')
|
||||
} else {
|
||||
ElMessage.warning('导出超时或状态未知,请稍后重试')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '导出失败')
|
||||
}
|
||||
}
|
||||
|
||||
function pollExport(bridge: NonNullable<ReturnType<typeof getPywebviewApi>>, fileId: string) {
|
||||
return new Promise<{ status?: number | string; file_url?: string; file_name?: string; error?: string } | null>((resolve) => {
|
||||
let count = 0
|
||||
const maxAttempts = 60
|
||||
const tick = async () => {
|
||||
count += 1
|
||||
try {
|
||||
const res = await bridge.vc_query_export!(fileId)
|
||||
if (res?.success) {
|
||||
const st = Number(res.status)
|
||||
if (st === 2 || st === 3) {
|
||||
resolve(res)
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* 网络异常继续轮询 */
|
||||
}
|
||||
if (count >= maxAttempts) {
|
||||
resolve(null)
|
||||
return
|
||||
}
|
||||
autoTimer = window.setTimeout(tick, 2000)
|
||||
}
|
||||
void tick()
|
||||
})
|
||||
}
|
||||
|
||||
function showDetail(item: VariantTaskListItem) {
|
||||
detailItem.value = { ...item, task_data: item.task_data ? { ...item.task_data } : undefined }
|
||||
detailVisible.value = true
|
||||
}
|
||||
|
||||
function scheduleAutoRefresh() {
|
||||
if (autoTimer) {
|
||||
window.clearTimeout(autoTimer)
|
||||
autoTimer = null
|
||||
}
|
||||
autoTimer = window.setTimeout(async () => {
|
||||
await loadTasks(currentPage.value || 1)
|
||||
if (currentTasks.value.some((item) => isBusy(item.status))) {
|
||||
scheduleAutoRefresh()
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
void loadTasks(1)
|
||||
refreshTimer = window.setInterval(() => {
|
||||
void loadTasks(currentPage.value || 1)
|
||||
}, 60 * 1000)
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
if (autoTimer) {
|
||||
window.clearTimeout(autoTimer)
|
||||
autoTimer = null
|
||||
}
|
||||
if (refreshTimer) {
|
||||
window.clearInterval(refreshTimer)
|
||||
refreshTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
startPolling()
|
||||
scheduleAutoRefresh()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
cleanup()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-page { min-height: 100vh; background: #151a25; }
|
||||
.main-content { display: flex; height: calc(100vh - 56px); min-height: calc(100vh - 56px); }
|
||||
.left-panel { width: 420px; background: #1c2333; padding: 20px; overflow-y: auto; border-right: 1px solid #2e3a52; }
|
||||
.right-panel { flex: 1; min-width: 0; background: #151a25; display: flex; flex-direction: column; }
|
||||
.section-title { font-size: 13px; color: #a0acbe; margin: 14px 0 10px; }
|
||||
.hint, .loading-msg, .files, .country-hint { color: #5e6878; font-size: 12px; line-height: 1.5; }
|
||||
.hint { margin-bottom: 8px; }
|
||||
.input-tabs { display: flex; gap: 6px; margin-bottom: 12px; }
|
||||
.input-tab { flex: 1; padding: 9px 0; border: 1px solid #3e4a62; border-radius: 8px; background: #2e3a52; color: #a0acbe; font-size: 13px; cursor: pointer; }
|
||||
.input-tab.active { background: rgba(64, 158, 255, 0.15); color: #409eff; border-color: #409eff; }
|
||||
.input-panel { margin-bottom: 4px; }
|
||||
.asin-textarea { width: 100%; box-sizing: border-box; min-height: 150px; padding: 10px; background: #151a25; color: #c8d2e2; border: 1px solid #3e4a62; border-radius: 8px; font-size: 13px; line-height: 1.6; resize: vertical; outline: none; }
|
||||
.asin-textarea:focus { border-color: #409eff; }
|
||||
.asin-count { margin-top: 8px; color: #5e6878; font-size: 12px; }
|
||||
.asin-count b { color: #c8d2e2; }
|
||||
.btns, .run-row { display: flex; gap: 10px; flex-wrap: wrap; }
|
||||
.opt-btn { padding: 8px 14px; color: #c8d2e2; background: #2e3a52; border: 1px solid #3e4a62; border-radius: 7px; cursor: pointer; font-size: 12px; }
|
||||
.opt-btn:hover:not(:disabled) { color: #3498db; border-color: #3498db; }
|
||||
.opt-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.selected-files { margin-top: 10px; color: #a0acbe; font-size: 12px; word-break: break-all; }
|
||||
.selected-files span { display: block; margin: 3px 0; }
|
||||
.country-group { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
|
||||
.country-chip { display: inline-flex; align-items: center; gap: 7px; padding: 9px 11px; border: 1px solid #333; border-radius: 8px; background: #2e3a52; color: #c8d2e2; font-size: 12px; cursor: pointer; user-select: none; }
|
||||
.country-chip.selected { border-color: #409eff; background: rgba(64, 158, 255, 0.12); color: #e6f2ff; }
|
||||
.country-chip input { width: 15px; height: 15px; accent-color: #409eff; cursor: pointer; }
|
||||
.country-hint { margin: 10px 0 4px; }
|
||||
.country-hint.error { color: #e6a23c; }
|
||||
.country-hint b { color: #c8d2e2; }
|
||||
.bridge-tip { margin: 12px 0; padding: 9px 12px; border-radius: 8px; background: rgba(230, 162, 60, 0.1); color: #e6a23c; font-size: 11px; line-height: 1.6; }
|
||||
.btn-run { padding: 10px 18px; color: #f5f8fc; background: #27ae60; border: none; border-radius: 8px; font-weight: 600; cursor: pointer; }
|
||||
.btn-run:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.loading-msg { margin-top: 10px; }
|
||||
.panel-header { display: flex; justify-content: space-between; align-items: center; padding: 14px 20px; border-bottom: 1px solid #2e3a52; color: #f5f8fc; font-size: 15px; font-weight: 600; }
|
||||
.panel-actions { display: flex; gap: 8px; }
|
||||
.btn-refresh { padding: 6px 12px; color: #c8d2e2; background: #2e3a52; border: 1px solid #3e4a62; border-radius: 6px; cursor: pointer; font-size: 12px; }
|
||||
.task-list-wrap { flex: 1; padding: 16px 20px; overflow: auto; }
|
||||
.empty-tasks { color: #5e6878; font-size: 13px; padding: 28px; text-align: center; }
|
||||
.task-list { list-style: none; margin: 0; padding: 0; }
|
||||
.task-item { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; padding: 12px 14px; margin-bottom: 8px; border: 1px solid #2e3a52; border-radius: 8px; background: #1c2333; }
|
||||
.left { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
|
||||
.id { color: #f5f8fc; font-size: 13px; font-weight: 600; }
|
||||
.task-right { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
|
||||
.status { padding: 4px 10px; border-radius: 6px; font-size: 12px; white-space: nowrap; }
|
||||
.status.pending { background: rgba(149, 165, 166, 0.18); color: #a0acbe; }
|
||||
.status.running { background: rgba(52, 152, 219, 0.18); color: #3498db; }
|
||||
.status.success { background: rgba(46, 204, 113, 0.18); color: #2ecc71; }
|
||||
.status.failed { background: rgba(231, 76, 60, 0.18); color: #ff6b6b; }
|
||||
.status.cancelled { background: rgba(149, 165, 166, 0.18); color: #a0acbe; }
|
||||
.act-btn { padding: 4px 9px; border-radius: 6px; font-size: 12px; background: #2e3a52; color: #c8d2e2; border: 1px solid #3e4a62; cursor: pointer; }
|
||||
.act-btn.ok { background: rgba(52, 152, 219, 0.18); color: #69b6ff; border-color: transparent; }
|
||||
.act-btn.warn { background: rgba(230, 162, 60, 0.14); color: #e6a23c; border-color: transparent; }
|
||||
.pagination { display: flex; justify-content: space-between; align-items: center; padding: 10px 0 4px; }
|
||||
.page-info { color: #5e6878; font-size: 12px; }
|
||||
.page-btns { display: flex; gap: 8px; }
|
||||
.page-btn { padding: 5px 12px; border-radius: 6px; background: #2e3a52; color: #c8d2e2; border: 1px solid #3e4a62; cursor: pointer; font-size: 12px; }
|
||||
.page-btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
||||
.detail-body { max-height: 60vh; overflow: auto; }
|
||||
.kv-row { display: flex; gap: 12px; padding: 6px 0; font-size: 13px; }
|
||||
.kv-key { flex-shrink: 0; width: 110px; color: #5e6878; }
|
||||
.kv-val { color: #c8d2e2; word-break: break-all; }
|
||||
.kv-val.link { color: #69b6ff; }
|
||||
.kv-section { margin: 10px 0 4px; font-size: 12px; color: #a0acbe; border-bottom: 1px solid #333; padding-bottom: 4px; }
|
||||
@media (max-width: 1100px) {
|
||||
.main-content { flex-direction: column; height: auto; }
|
||||
.left-panel { width: 100%; border-right: none; border-bottom: 1px solid #2e3a52; }
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-shell module-page">
|
||||
<BrandTopBar active="withdraw" />
|
||||
<AmazonToolPageShell tool-id="cash">
|
||||
|
||||
<div class="main-content">
|
||||
<aside class="left-panel">
|
||||
@@ -325,13 +325,14 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</AmazonToolPageShell>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import BrandTopBar from "@/pages/brand/components/BrandTopBar.vue";
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import {
|
||||
addWithdrawCandidate,
|
||||
createWithdrawTask,
|
||||
@@ -1315,61 +1316,61 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-page { min-height: 100vh; background: #1a1a1a; }
|
||||
.module-page { min-height: 100vh; background: #151a25; }
|
||||
.main-content { display: flex; min-height: calc(100vh - 56px); height: calc(100vh - 56px); }
|
||||
.left-panel { width: 400px; background: #1e1e1e; padding: 20px; overflow-y: auto; border-right: 1px solid #2a2a2a; }
|
||||
.right-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; background: #1a1a1a; }
|
||||
.section-title { font-size: 13px; color: #bbb; margin-bottom: 10px; }
|
||||
.input-zone { border: 1px dashed #3a3a3a; border-radius: 10px; padding: 16px; background: #252525; margin-bottom: 20px; }
|
||||
.hint { color: #888; font-size: 12px; margin-bottom: 12px; line-height: 1.5; text-align: left; }
|
||||
.left-panel { width: 400px; background: #1c2333; padding: 20px; overflow-y: auto; border-right: 1px solid #2e3a52; }
|
||||
.right-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; background: #151a25; }
|
||||
.section-title { font-size: 13px; color: #a0acbe; margin-bottom: 10px; }
|
||||
.input-zone { border: 1px dashed #3e4a62; border-radius: 10px; padding: 16px; background: #2e3a52; margin-bottom: 20px; }
|
||||
.hint { color: #5e6878; font-size: 12px; margin-bottom: 12px; line-height: 1.5; text-align: left; }
|
||||
.input-row { display: flex; gap: 10px; align-items: center; }
|
||||
.input-row :deep(.el-input) { flex: 1; }
|
||||
.filter-row { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
|
||||
.filter-label { color: #aaa; font-size: 12px; white-space: nowrap; }
|
||||
.filter-label { color: #a0acbe; font-size: 12px; white-space: nowrap; }
|
||||
.reserved-input { width: 180px; }
|
||||
.opt-btn { padding: 8px 12px; font-size: 12px; color: #ccc; background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 6px; cursor: pointer; white-space: nowrap; }
|
||||
.opt-btn { padding: 8px 12px; font-size: 12px; color: #c8d2e2; background: #2e3a52; border: 1px solid #3e4a62; border-radius: 6px; cursor: pointer; white-space: nowrap; }
|
||||
.opt-btn:hover:not(:disabled) { color: #3498db; border-color: #3498db; }
|
||||
.opt-btn:disabled { opacity: 0.6; cursor: not-allowed; }
|
||||
.empty-candidates { color: #666; font-size: 13px; padding: 16px; border: 1px dashed #333; border-radius: 8px; margin-bottom: 16px; }
|
||||
.candidate-table-scroll { max-height: 320px; overflow: auto; margin-bottom: 18px; border-radius: 8px; border: 1px solid #2a2a2a; }
|
||||
.candidate-table { --el-table-bg-color: #252525; --el-table-tr-bg-color: #252525; --el-table-header-bg-color: #2a2a2a; --el-table-text-color: #ccc; --el-table-border-color: #333; }
|
||||
.empty-candidates { color: #5e6878; font-size: 13px; padding: 16px; border: 1px dashed #333; border-radius: 8px; margin-bottom: 16px; }
|
||||
.candidate-table-scroll { max-height: 320px; overflow: auto; margin-bottom: 18px; border-radius: 8px; border: 1px solid #2e3a52; }
|
||||
.candidate-table { --el-table-bg-color: #2e3a52; --el-table-tr-bg-color: #2e3a52; --el-table-header-bg-color: #2e3a52; --el-table-text-color: #c8d2e2; --el-table-border-color: #333; }
|
||||
.link-danger { background: none; border: none; color: #e74c3c; cursor: pointer; font-size: 12px; padding: 0; }
|
||||
.link-danger:hover { text-decoration: underline; }
|
||||
.run-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
|
||||
.btn-run { padding: 10px 18px; font-size: 14px; font-weight: 600; color: #fff; background: #3498db; border: none; border-radius: 8px; cursor: pointer; }
|
||||
.btn-run { padding: 10px 18px; font-size: 14px; font-weight: 600; color: #f5f8fc; background: #3498db; border: none; border-radius: 8px; cursor: pointer; }
|
||||
.btn-run:hover:not(:disabled) { background: #2980b9; }
|
||||
.btn-run:disabled { opacity: 0.55; cursor: not-allowed; }
|
||||
.btn-queue { background: #27ae60; }
|
||||
.btn-queue:hover:not(:disabled) { background: #219a52; }
|
||||
.loading-msg { margin-top: 12px; font-size: 12px; color: #777; line-height: 1.5; }
|
||||
.queue-debug-card { margin-top: 18px; padding: 14px; border: 1px solid #2a2a2a; border-radius: 10px; background: #202020; }
|
||||
.loading-msg { margin-top: 12px; font-size: 12px; color: #5e6878; line-height: 1.5; }
|
||||
.queue-debug-card { margin-top: 18px; padding: 14px; border: 1px solid #2e3a52; border-radius: 10px; background: #222b3d; }
|
||||
.queue-debug-title { margin-bottom: 8px; }
|
||||
.queue-debug-line { color: #b8c1cc; 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: #141414; 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: #e0e0e0; border-bottom: 1px solid #2a2a2a; }
|
||||
.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 #2a2a2a; border-radius: 10px; background: #1e1e1e; }
|
||||
.summary-card strong { display: block; margin-top: 8px; font-size: 22px; color: #eaf4ff; }
|
||||
.summary-label { font-size: 12px; color: #8d8d8d; }
|
||||
.subsection-title { font-size: 13px; color: #999; margin: 8px 0 10px; }
|
||||
.empty-tasks { color: #666; font-size: 13px; padding: 16px; text-align: center; }
|
||||
.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-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: #2a2a2a; --el-table-text-color: #ccc; --el-table-border-color: #333; }
|
||||
.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 #2a2a2a; border-radius: 10px; background: #1e1e1e; min-height: 220px; margin-top: 8px; }
|
||||
.result-list-header { padding: 12px 16px; border-bottom: 1px solid #2a2a2a; font-size: 14px; color: #ddd; }
|
||||
.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: #888; margin-bottom: 8px; }
|
||||
.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 #2a2a2a; border-radius: 8px; margin-bottom: 8px; background: #222; }
|
||||
.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: #e0e0e0; font-size: 13px; }
|
||||
.files { font-size: 12px; color: #888; }
|
||||
.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; }
|
||||
@@ -1381,7 +1382,7 @@ onUnmounted(() => {
|
||||
.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 #2a2a2a; }
|
||||
.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