feat(electron):优化商标筛查面板与资源加载逻辑
- 将多个 v-if 条件渲染改为 v-show,提升组件切换性能 - 优化商标任务完成状态判断逻辑,确保准确显示采集完成图标- 调整任务统计数据显示条件,支持零数据展示- 更新 API 配置地址,切换至本地开发环境地址 - 降低 Spring Boot 线程池与数据库连接池配置,适应小规模并发- 禁用 devtools 热部署与 Swagger 接口文档,优化生产环境性能 - 配置 RestTemplate 使用 HttpClient 连接池,增强 HTTP 请求稳定性 - 改进静态资源拷贝脚本,确保 icon 与 image 文件夹正确复制 - 更新 electron-builder 配置,优化资源打包路径与应用图标 - 修改 HTTP 路由规则,明确区分客户端与管理端接口路径- 注册文件协议拦截器,解决生产环境下 icon/image 资源加载问题 - 调整商标 API 接口路径,指向 erp_client_sb服务 -重构 MarkController 控制器,专注 Token 管理功能 - 优化线程池参数,适配低并发业务场景- 强化商标筛查流程控制,完善任务取消与异常处理机制 - 新增方舟精选任务管理接口,实现 Excel 下载与数据解析功能
This commit is contained in:
@@ -4,10 +4,9 @@ import {join, dirname, basename, extname} from 'path';
|
||||
import {spawn, ChildProcess} from 'child_process';
|
||||
import * as https from 'https';
|
||||
import * as http from 'http';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { createTray, destroyTray } from './tray';
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
|
||||
let springProcess: ChildProcess | null = null;
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
let splashWindow: BrowserWindow | null = null;
|
||||
@@ -74,7 +73,7 @@ function getJarFilePath(): string {
|
||||
}
|
||||
|
||||
const getSplashPath = () => getResourcePath('../../public/splash.html', 'public/splash.html');
|
||||
const getIconPath = () => getResourcePath('../../public/icon/icon1.png', 'public/icon/icon1.png', '../renderer/icon/icon1.png');
|
||||
const getIconPath = () => getResourcePath('../../public/icon/icon1.png', 'public/icon/icon1.png');
|
||||
const getLogbackConfigPath = () => getResourcePath('../../public/config/logback.xml', 'public/config/logback.xml');
|
||||
|
||||
function getDataDirectoryPath(): string {
|
||||
@@ -211,7 +210,7 @@ function startSpringBoot() {
|
||||
}
|
||||
}
|
||||
|
||||
// startSpringBoot();
|
||||
startSpringBoot();
|
||||
function stopSpringBoot() {
|
||||
if (!springProcess) return;
|
||||
try {
|
||||
@@ -305,14 +304,21 @@ if (!gotTheLock) {
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
// 注册文件协议拦截器,将 /icon/ 和 /image/ 请求重定向到 public 目录
|
||||
if (!isDev) {
|
||||
protocol.interceptFileProtocol('file', (request, callback) => {
|
||||
let url = request.url.substring(8); // 移除 'file:///'
|
||||
// 使用 fileURLToPath 正确解码 URL,处理空格和特殊字符
|
||||
let filePath: string;
|
||||
try {
|
||||
filePath = fileURLToPath(request.url);
|
||||
} catch (e) {
|
||||
// 如果解码失败,回退到原来的方法
|
||||
filePath = decodeURIComponent(request.url.substring(8));
|
||||
}
|
||||
|
||||
// 检查是否是 icon 或 image 资源请求
|
||||
if (url.includes('/icon/') || url.includes('/image/')) {
|
||||
const match = url.match(/\/(icon|image)\/([^?#]+)/);
|
||||
if (filePath.includes('/icon/') || filePath.includes('\\icon\\') ||
|
||||
filePath.includes('/image/') || filePath.includes('\\image\\')) {
|
||||
const match = filePath.match(/[/\\](icon|image)[/\\]([^?#]+)/);
|
||||
if (match) {
|
||||
const [, type, filename] = match;
|
||||
const publicPath = join(process.resourcesPath, 'app.asar.unpacked', 'public', type, filename);
|
||||
@@ -323,7 +329,7 @@ app.whenReady().then(() => {
|
||||
}
|
||||
}
|
||||
|
||||
callback({ path: url });
|
||||
callback({ path: filePath });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -370,9 +376,9 @@ app.whenReady().then(() => {
|
||||
}
|
||||
}
|
||||
//666
|
||||
setTimeout(() => {
|
||||
openAppIfNotOpened();
|
||||
}, 100);
|
||||
// setTimeout(() => {
|
||||
// openAppIfNotOpened();
|
||||
// }, 100);
|
||||
|
||||
app.on('activate', () => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
|
||||
@@ -9,9 +9,7 @@ function getIconPath(): string {
|
||||
if (isDev) {
|
||||
return join(__dirname, '../../public/icon/icon1.png')
|
||||
}
|
||||
const bundledPath = join(process.resourcesPath, 'app.asar.unpacked', 'public/icon/icon1.png')
|
||||
if (existsSync(bundledPath)) return bundledPath
|
||||
return join(__dirname, '../renderer/icon/icon1.png')
|
||||
return join(process.resourcesPath, 'app.asar.unpacked', 'public/icon/icon1.png')
|
||||
}
|
||||
|
||||
export function createTray(mainWindow: BrowserWindow | null) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
export type HttpMethod = 'GET' | 'POST' | 'DELETE';
|
||||
const RUOYI_BASE = 'http://8.138.23.49:8085';
|
||||
// const RUOYI_BASE = 'http://192.168.1.89:8085';
|
||||
export const CONFIG = {
|
||||
CLIENT_BASE: 'http://localhost:8081',
|
||||
//RUOYI_BASE: 'http://8.138.23.49:8085',
|
||||
RUOYI_BASE: 'http://192.168.1.89:8085',
|
||||
SSE_URL: 'http://192.168.1.89:8085/monitor/account/events'
|
||||
RUOYI_BASE,
|
||||
SSE_URL: `${RUOYI_BASE}/monitor/account/events`
|
||||
} as const;
|
||||
|
||||
function resolveBase(path: string): string {
|
||||
// 路由到 ruoyi-admin (8085):仅系统管理和监控相关
|
||||
if (path.startsWith('/monitor/') || path.startsWith('/system/') || path.startsWith('/tool/banma') || path.startsWith('/tool/genmai')) {
|
||||
|
||||
9
electron-vue-template/src/renderer/auto-imports.d.ts
vendored
Normal file
9
electron-vue-template/src/renderer/auto-imports.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
export {}
|
||||
declare global {
|
||||
|
||||
}
|
||||
31
electron-vue-template/src/renderer/components.d.ts
vendored
Normal file
31
electron-vue-template/src/renderer/components.d.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElImage: typeof import('element-plus/es')['ElImage']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
}
|
||||
}
|
||||
@@ -249,7 +249,7 @@ function handleExportData() {
|
||||
</div>
|
||||
<div class="banner-content">
|
||||
<div class="banner-title">数据筛查中...</div>
|
||||
<div class="banner-desc">为确保结果准确,请耐心等待,保持后台运行...</div>
|
||||
<div class="banner-desc">为避免进程中断,请勿退出应用,保持后台运行...</div>
|
||||
</div>
|
||||
<div class="banner-actions">
|
||||
<el-button size="default" @click="handleCancelTask">取消</el-button>
|
||||
@@ -278,7 +278,7 @@ function handleExportData() {
|
||||
</div>
|
||||
<div class="banner-content">
|
||||
<div class="banner-title">{{ trademarkPanelRef.queryStatus === 'done' ? '筛查已完成' : '数据筛查失败' }}</div>
|
||||
<div class="banner-desc">{{ trademarkPanelRef.queryStatus === 'done' ? '点击"导出数据"按钮,可导出为 Excel 表格文件。' : (trademarkPanelRef.errorMessage || '请稍后重试') }}</div>
|
||||
<div class="banner-desc">{{ trademarkPanelRef.queryStatus === 'done' ? '点击右侧“导出数据”按钮,可导出为 Excel 表格文档。如过滤结果为 0,请排查选品表格内容是否正确;产品所属地区与商标查询地区是否不一致,如不一致可能导致筛查结果偏差或错误。' : (trademarkPanelRef.errorMessage || '请稍后重试') }}</div>
|
||||
</div>
|
||||
<div class="banner-actions">
|
||||
<el-button size="default" @click="handleNewTask">新建任务</el-button>
|
||||
@@ -328,7 +328,7 @@ function handleExportData() {
|
||||
<div class="task-title-row">
|
||||
<div class="task-info">
|
||||
<div class="task-name">{{ trademarkPanelRef?.taskProgress?.product?.label || '未注册/TM商标筛查' }}</div>
|
||||
<div class="task-desc">{{ trademarkPanelRef?.taskProgress?.product?.desc || '筛查未注册商标或TM标的产品' }}<span v-if="(trademarkPanelRef?.taskProgress?.product?.total || 0) > 0"> (已完成)</span></div>
|
||||
<div class="task-desc">{{ trademarkPanelRef?.taskProgress?.product?.desc || '筛查未注册商标或TM标的产品' }}<span v-if="trademarkPanelRef?.isProductTaskRealData && (trademarkPanelRef?.taskProgress?.product?.current || 0) >= trademarkPanelRef.taskProgress.product.total"> (已完成)</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-progress-wrapper">
|
||||
@@ -340,15 +340,15 @@ function handleExportData() {
|
||||
<div class="task-stats">
|
||||
<div class="task-stat">
|
||||
<span class="stat-label">查询数量</span>
|
||||
<span class="stat-value">{{ (trademarkPanelRef?.taskProgress?.product?.total || 0) > 0 ? trademarkPanelRef.taskProgress.product.total : '-' }}</span>
|
||||
<span class="stat-value">{{ trademarkPanelRef?.isProductTaskRealData ? trademarkPanelRef.taskProgress.product.total : '-' }}</span>
|
||||
</div>
|
||||
<div class="task-stat highlight">
|
||||
<span class="stat-label">未注册/TM标</span>
|
||||
<span class="stat-value">{{ (trademarkPanelRef?.taskProgress?.product?.total || 0) > 0 && (trademarkPanelRef?.taskProgress?.product?.current || 0) >= trademarkPanelRef.taskProgress.product.total ? (trademarkPanelRef?.taskProgress?.product?.completed || 0) : '-' }}</span>
|
||||
<span class="stat-value">{{ trademarkPanelRef?.isProductTaskRealData ? trademarkPanelRef.taskProgress.product.completed : '-' }}</span>
|
||||
</div>
|
||||
<div class="task-stat">
|
||||
<span class="stat-label">已过滤</span>
|
||||
<span class="stat-value">{{ (trademarkPanelRef?.taskProgress?.product?.total || 0) > 0 && (trademarkPanelRef?.taskProgress?.product?.current || 0) >= trademarkPanelRef.taskProgress.product.total ? ((trademarkPanelRef?.taskProgress?.product?.total || 0) - (trademarkPanelRef?.taskProgress?.product?.completed || 0)) : '-' }}</span>
|
||||
<span class="stat-value">{{ trademarkPanelRef?.isProductTaskRealData ? (trademarkPanelRef.taskProgress.product.total - trademarkPanelRef.taskProgress.product.completed) : '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -357,7 +357,7 @@ function handleExportData() {
|
||||
<div class="task-title-row">
|
||||
<div class="task-info">
|
||||
<div class="task-name">{{ trademarkPanelRef?.taskProgress?.brand?.label || '品牌商标筛查' }}</div>
|
||||
<div class="task-desc">{{ trademarkPanelRef?.taskProgress?.brand?.desc || '筛查未注册商标的品牌' }}<span v-if="(trademarkPanelRef?.taskProgress?.brand?.total || 0) > 0 && (trademarkPanelRef?.taskProgress?.brand?.current || 0) >= trademarkPanelRef.taskProgress.brand.total"> (已完成)</span></div>
|
||||
<div class="task-desc">{{ trademarkPanelRef?.taskProgress?.brand?.desc || '筛查未注册商标的品牌' }}<span v-if="trademarkPanelRef?.isBrandTaskRealData && (trademarkPanelRef?.taskProgress?.brand?.current || 0) >= trademarkPanelRef.taskProgress.brand.total"> (已完成)</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-progress-wrapper">
|
||||
@@ -369,15 +369,15 @@ function handleExportData() {
|
||||
<div class="task-stats">
|
||||
<div class="task-stat">
|
||||
<span class="stat-label">查询数量</span>
|
||||
<span class="stat-value">{{ (trademarkPanelRef?.taskProgress?.brand?.total || 0) === 0 ? '-' : trademarkPanelRef.taskProgress.brand.total }}</span>
|
||||
<span class="stat-value">{{ trademarkPanelRef?.isBrandTaskRealData ? trademarkPanelRef.taskProgress.brand.total : '-' }}</span>
|
||||
</div>
|
||||
<div class="task-stat highlight">
|
||||
<span class="stat-label">未注册</span>
|
||||
<span class="stat-value">{{ ((trademarkPanelRef?.taskProgress?.brand?.current || 0) >= (trademarkPanelRef?.taskProgress?.brand?.total || 1)) ? (trademarkPanelRef?.taskProgress?.brand?.completed || 0) : '-' }}</span>
|
||||
<span class="stat-value">{{ trademarkPanelRef?.isBrandTaskRealData ? trademarkPanelRef.taskProgress.brand.completed : '-' }}</span>
|
||||
</div>
|
||||
<div class="task-stat">
|
||||
<span class="stat-label">已注册</span>
|
||||
<span class="stat-value">{{ ((trademarkPanelRef?.taskProgress?.brand?.current || 0) >= (trademarkPanelRef?.taskProgress?.brand?.total || 1)) ? ((trademarkPanelRef?.taskProgress?.brand?.total || 0) - (trademarkPanelRef?.taskProgress?.brand?.completed || 0)) : '-' }}</span>
|
||||
<span class="stat-value">{{ trademarkPanelRef?.isBrandTaskRealData ? (trademarkPanelRef.taskProgress.brand.total - trademarkPanelRef.taskProgress.brand.completed) : '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ElMessage } from 'element-plus'
|
||||
import { handlePlatformFileExport } from '../../utils/settings'
|
||||
import { getUsernameFromToken } from '../../utils/token'
|
||||
import { markApi } from '../../api/mark'
|
||||
import { useFileDrop } from '../../composables/useFileDrop'
|
||||
const TrialExpiredDialog = defineAsyncComponent(() => import('../common/TrialExpiredDialog.vue'))
|
||||
|
||||
const refreshVipStatus = inject<() => Promise<boolean>>('refreshVipStatus')
|
||||
@@ -31,6 +32,10 @@ const totalSteps = ref(0)
|
||||
let brandProgressTimer: any = null
|
||||
const brandTaskId = ref('')
|
||||
|
||||
// 真实数据标记(区分临时进度值和真实统计数据)
|
||||
const isProductTaskRealData = ref(false)
|
||||
const isBrandTaskRealData = ref(false)
|
||||
|
||||
// 三个任务的进度数据
|
||||
const taskProgress = ref({
|
||||
product: { total: 0, current: 0, completed: 0, label: '产品商标筛查', desc: '筛查未注册商标或TM标的商品' },
|
||||
@@ -107,6 +112,49 @@ function showMessage(message: string, type: 'success' | 'warning' | 'error' | 'i
|
||||
ElMessage({ message, type })
|
||||
}
|
||||
|
||||
// 拖拽上传
|
||||
async function processTrademarkFile(file: File) {
|
||||
uploadLoading.value = true
|
||||
|
||||
try {
|
||||
// 根据选中的查询类型确定需要的表头
|
||||
const requiredHeaders: string[] = []
|
||||
if (queryTypes.value.includes('product')) {
|
||||
requiredHeaders.push('商品主图')
|
||||
}
|
||||
if (queryTypes.value.includes('brand')) {
|
||||
requiredHeaders.push('品牌')
|
||||
}
|
||||
|
||||
// 验证表头
|
||||
if (requiredHeaders.length > 0) {
|
||||
const validateResult = await markApi.validateHeaders(file, requiredHeaders)
|
||||
if (validateResult.code !== 200 && validateResult.code !== 0) {
|
||||
showMessage(validateResult.msg || '表头验证失败', 'error')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
trademarkFileName.value = file.name
|
||||
trademarkFile.value = file
|
||||
queryStatus.value = 'idle'
|
||||
trademarkData.value = []
|
||||
trademarkFullData.value = []
|
||||
trademarkHeaders.value = []
|
||||
emit('updateData', [])
|
||||
} catch (error: any) {
|
||||
showMessage('表头验证失败: ' + error.message, 'error')
|
||||
} finally {
|
||||
uploadLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const { dragActive, onDragEnter, onDragOver, onDragLeave, onDrop } = useFileDrop({
|
||||
accept: /\.xlsx?$/i,
|
||||
onFile: processTrademarkFile,
|
||||
onError: (msg) => showMessage(msg, 'warning')
|
||||
})
|
||||
|
||||
function removeTrademarkFile() {
|
||||
trademarkFileName.value = ''
|
||||
trademarkFile.value = null
|
||||
@@ -196,41 +244,8 @@ async function handleTrademarkUpload(e: Event) {
|
||||
return
|
||||
}
|
||||
|
||||
uploadLoading.value = true
|
||||
|
||||
try {
|
||||
// 根据选中的查询类型确定需要的表头
|
||||
const requiredHeaders: string[] = []
|
||||
if (queryTypes.value.includes('product')) {
|
||||
requiredHeaders.push('商品主图')
|
||||
}
|
||||
if (queryTypes.value.includes('brand')) {
|
||||
requiredHeaders.push('品牌')
|
||||
}
|
||||
|
||||
// 验证表头
|
||||
if (requiredHeaders.length > 0) {
|
||||
const validateResult = await markApi.validateHeaders(file, requiredHeaders)
|
||||
if (validateResult.code !== 200 && validateResult.code !== 0) {
|
||||
showMessage(validateResult.msg || '表头验证失败', 'error')
|
||||
input.value = ''
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
trademarkFileName.value = file.name
|
||||
trademarkFile.value = file
|
||||
queryStatus.value = 'idle'
|
||||
trademarkData.value = []
|
||||
trademarkFullData.value = []
|
||||
trademarkHeaders.value = []
|
||||
emit('updateData', [])
|
||||
} catch (error: any) {
|
||||
showMessage('表头验证失败: ' + error.message, 'error')
|
||||
} finally {
|
||||
uploadLoading.value = false
|
||||
input.value = ''
|
||||
}
|
||||
await processTrademarkFile(file)
|
||||
input.value = ''
|
||||
}
|
||||
|
||||
async function startTrademarkQuery() {
|
||||
@@ -268,6 +283,10 @@ async function startTrademarkQuery() {
|
||||
taskProgress.value.platform.current = 0
|
||||
taskProgress.value.platform.completed = 0
|
||||
|
||||
// 重置真实数据标记
|
||||
isProductTaskRealData.value = false
|
||||
isBrandTaskRealData.value = false
|
||||
|
||||
// 通知父组件更新数据
|
||||
emit('updateData', [])
|
||||
|
||||
@@ -297,7 +316,7 @@ async function startTrademarkQuery() {
|
||||
// 轮询检查任务状态
|
||||
const pollTask = async () => {
|
||||
const maxWaitTime = 60000
|
||||
const pollInterval = 3000
|
||||
const pollInterval = 2000
|
||||
const startTime = Date.now()
|
||||
|
||||
let taskResult: any = null
|
||||
@@ -307,7 +326,16 @@ async function startTrademarkQuery() {
|
||||
return null
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, pollInterval))
|
||||
// 分段等待,每500ms检查一次取消状态
|
||||
const waitSegments = pollInterval / 500
|
||||
for (let i = 0; i < waitSegments; i++) {
|
||||
if (!trademarkLoading.value) {
|
||||
clearInterval(progressTimer)
|
||||
return null
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
}
|
||||
|
||||
if (!trademarkLoading.value) {
|
||||
clearInterval(progressTimer)
|
||||
return null
|
||||
@@ -321,8 +349,14 @@ async function startTrademarkQuery() {
|
||||
return taskResult
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// 继续等待
|
||||
} catch (err: any) {
|
||||
const errorMsg = err.message || ''
|
||||
if (errorMsg.includes('表格没有数据') || errorMsg.includes('没有数据') ||
|
||||
errorMsg.includes('任务处理失败') || errorMsg.includes('任务失败')) {
|
||||
clearInterval(progressTimer)
|
||||
throw err
|
||||
}
|
||||
// 其他错误(网络错误等)继续等待
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +367,12 @@ async function startTrademarkQuery() {
|
||||
try {
|
||||
productResult = await pollTask()
|
||||
|
||||
if (!productResult || (productResult.code !== 200 && productResult.code !== 0)) {
|
||||
// 如果返回null,说明用户取消了,直接返回
|
||||
if (!productResult) {
|
||||
return
|
||||
}
|
||||
|
||||
if (productResult.code !== 200 && productResult.code !== 0) {
|
||||
throw new Error('获取任务超时或失败,请重试')
|
||||
}
|
||||
|
||||
@@ -345,6 +384,7 @@ async function startTrademarkQuery() {
|
||||
taskData.total = productResult.data.original?.total || 0
|
||||
taskData.current = taskData.total
|
||||
taskData.completed = productResult.data.filtered.length
|
||||
isProductTaskRealData.value = true
|
||||
} finally {
|
||||
clearInterval(progressTimer)
|
||||
}
|
||||
@@ -440,6 +480,7 @@ async function startTrademarkQuery() {
|
||||
brandData.total = brandResult.data.checked || brandResult.data.total || brandData.total
|
||||
brandData.current = brandData.total
|
||||
brandData.completed = brandResult.data.unregistered || 0
|
||||
isBrandTaskRealData.value = true
|
||||
|
||||
// 提取未注册品牌列表
|
||||
const unregisteredBrands = brandResult.data.data.map((item: any) => item.brand).filter(Boolean)
|
||||
@@ -455,6 +496,7 @@ async function startTrademarkQuery() {
|
||||
|
||||
// 更新统计:显示过滤出的实际行数(而不是品牌数)
|
||||
brandData.completed = filterResult.data.filteredRows.length
|
||||
isBrandTaskRealData.value = true
|
||||
|
||||
// 将品牌筛查结果作为展示数据
|
||||
const brandItems = filterResult.data.filteredRows.map((row: any) => ({
|
||||
@@ -484,44 +526,61 @@ async function startTrademarkQuery() {
|
||||
}
|
||||
|
||||
// 只要流程正常完成,就设置为done状态(不再依赖trademarkLoading)
|
||||
queryStatus.value = 'done'
|
||||
emit('updateData', trademarkData.value)
|
||||
|
||||
let summaryMsg = '筛查完成'
|
||||
if (needProductCheck) summaryMsg += `,产品:${taskProgress.value.product.completed}/${taskProgress.value.product.total}`
|
||||
if (needBrandCheck && brandList.length > 0) summaryMsg += `,品牌:${taskProgress.value.brand.completed}/${taskProgress.value.brand.total}`
|
||||
showMessage(summaryMsg, 'success')
|
||||
|
||||
// 保存会话
|
||||
await saveSession()
|
||||
queryStatus.value = 'done'
|
||||
emit('updateData', trademarkData.value)
|
||||
|
||||
let summaryMsg = '筛查完成'
|
||||
if (needProductCheck) summaryMsg += `,产品:${taskProgress.value.product.completed}/${taskProgress.value.product.total}`
|
||||
if (needBrandCheck && brandList.length > 0) summaryMsg += `,品牌:${taskProgress.value.brand.completed}/${taskProgress.value.brand.total}`
|
||||
showMessage(summaryMsg, 'success')
|
||||
|
||||
// 保存会话
|
||||
await saveSession()
|
||||
} catch (error: any) {
|
||||
const hasProductData = taskProgress.value.product.total > 0
|
||||
const hasProductData = isProductTaskRealData.value && taskProgress.value.product.total > 0
|
||||
const hasBrandData = isBrandTaskRealData.value && taskProgress.value.brand.total > 0
|
||||
|
||||
// 优化错误信息 - 只显示友好提示
|
||||
let msg = error.message || ''
|
||||
if (msg.includes('网络') || msg.includes('network')) {
|
||||
let friendlyMsg = ''
|
||||
|
||||
if (msg.includes('网络') || msg.includes('network') || msg.includes('Network')) {
|
||||
queryStatus.value = 'networkError'
|
||||
errorMessage.value = '网络不可用,请检查你的网络或代理设置'
|
||||
} else if (msg.includes('超时') || msg.includes('timeout')) {
|
||||
friendlyMsg = '网络连接失败,请检查网络或代理设置'
|
||||
} else if (msg.includes('超时') || msg.includes('timeout') || msg.includes('Timeout')) {
|
||||
queryStatus.value = 'error'
|
||||
errorMessage.value = '数据库维护中,请稍后重试'
|
||||
friendlyMsg = '查询超时,请稍后重试'
|
||||
} else if (msg.includes('403') || msg.includes('风控')) {
|
||||
queryStatus.value = 'error'
|
||||
errorMessage.value = '网站风控限制,请稍后重试'
|
||||
friendlyMsg = '访问受限,请稍后重试'
|
||||
} else if (msg.includes('表格没有数据') || msg.includes('没有数据')) {
|
||||
queryStatus.value = 'error'
|
||||
friendlyMsg = '表格没有有效数据,请检查文件内容'
|
||||
} else if (msg.includes('创建任务失败') || msg.includes('提取品牌')) {
|
||||
queryStatus.value = 'error'
|
||||
friendlyMsg = '数据处理失败,请检查文件格式是否正确'
|
||||
} else {
|
||||
queryStatus.value = 'error'
|
||||
errorMessage.value = '数据库维护中,请稍后重试'
|
||||
friendlyMsg = '查询失败,请稍后重试'
|
||||
}
|
||||
|
||||
// 仅在第1步失败时清空数据
|
||||
if (!hasProductData) {
|
||||
// 根据失败阶段确定错误提示
|
||||
if (hasProductData && !hasBrandData && needBrandCheck) {
|
||||
// 产品筛查完成,品牌筛查失败
|
||||
errorMessage.value = `品牌筛查失败:${friendlyMsg}。产品筛查结果已保留`
|
||||
// 保留产品数据
|
||||
emit('updateData', trademarkData.value)
|
||||
} else if (!hasProductData) {
|
||||
errorMessage.value = `产品筛查失败:${friendlyMsg}`
|
||||
trademarkData.value = []
|
||||
trademarkFullData.value = []
|
||||
trademarkHeaders.value = []
|
||||
emit('updateData', [])
|
||||
} else {
|
||||
errorMessage.value = friendlyMsg
|
||||
emit('updateData', trademarkData.value)
|
||||
}
|
||||
|
||||
showMessage(errorMessage.value, 'error')
|
||||
} finally {
|
||||
// 清除定时器
|
||||
@@ -646,6 +705,10 @@ function resetToIdle() {
|
||||
taskProgress.value.platform.current = 0
|
||||
taskProgress.value.platform.completed = 0
|
||||
|
||||
// 重置真实数据标记
|
||||
isProductTaskRealData.value = false
|
||||
isBrandTaskRealData.value = false
|
||||
|
||||
// 清空localStorage中的会话数据
|
||||
try {
|
||||
const username = getUsernameFromToken()
|
||||
@@ -666,7 +729,9 @@ defineExpose({
|
||||
resetToIdle,
|
||||
stopTrademarkQuery,
|
||||
startTrademarkQuery,
|
||||
exportTrademarkData
|
||||
exportTrademarkData,
|
||||
isProductTaskRealData,
|
||||
isBrandTaskRealData
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -679,7 +744,15 @@ defineExpose({
|
||||
<div class="step-card">
|
||||
<div class="step-header"><div class="title">导入Excel表格</div></div>
|
||||
<div class="desc">产品筛查:需导入卖家精灵选品表格,并勾选"导出主图";品牌筛查:Excel需包含"品牌"列</div>
|
||||
<div class="dropzone" :class="{ uploading: uploadLoading }" @click="!uploadLoading && openTrademarkUpload()">
|
||||
<div
|
||||
class="dropzone"
|
||||
:class="{ uploading: uploadLoading, 'drag-active': dragActive }"
|
||||
@click="!uploadLoading && openTrademarkUpload()"
|
||||
@dragenter="onDragEnter"
|
||||
@dragover="onDragOver"
|
||||
@dragleave="onDragLeave"
|
||||
@drop="onDrop"
|
||||
>
|
||||
<div v-if="!uploadLoading" class="dz-icon">📤</div>
|
||||
<div v-else class="dz-icon spinner">⟳</div>
|
||||
<div class="dz-text">{{ uploadLoading ? '正在验证表头...' : '点击或将文件拖拽到这里上传' }}</div>
|
||||
@@ -845,6 +918,7 @@ defineExpose({
|
||||
.dropzone:hover { background: #f6fbff; border-color: #409EFF; }
|
||||
.dropzone.uploading { cursor: not-allowed; opacity: 0.7; }
|
||||
.dropzone.uploading:hover { background: #fafafa; border-color: #c0c4cc; }
|
||||
.dropzone.drag-active { background: #e6f4ff; border-color: #1677FF; }
|
||||
.dz-icon { font-size: 20px; margin-bottom: 6px; color: #909399; }
|
||||
.dz-text { color: #303133; font-size: 13px; margin-bottom: 2px; }
|
||||
.dz-sub { color: #909399; font-size: 12px; }
|
||||
|
||||
Reference in New Issue
Block a user