feat(amazon):优化商标筛查重试机制和进度显示- 添加防抖控制,避免频繁点击重试按钮
- 优化重试逻辑,增加时间间隔限制和状态检查 - 移除表格上方冗余的进度条显示 - 更新取消状态下的提示文案和操作引导- 修复品牌统计数据显示逻辑,确保准确性- 调整用户界面元素间距和样式细节 - 完善后端接口调用,支持信号中断和错误处理 -优化SSE连接管理,防止连接泄漏 - 改进任务取消机制,提升用户体验 - 更新用户信息展示,增加注册时间显示
This commit is contained in:
@@ -229,8 +229,6 @@ function startSpringBoot() {
|
||||
const dataDir = getDataDirectoryPath();
|
||||
const logDir = getLogDirectoryPath();
|
||||
const logbackConfigPath = getLogbackConfigPath();
|
||||
console.log('[Spring Boot] JAR路径:', jarPath);
|
||||
console.log('[Spring Boot] Java路径:', javaPath);
|
||||
if (!existsSync(jarPath)) {
|
||||
dialog.showErrorBox('启动失败', `JAR 文件不存在:\n${jarPath}`);
|
||||
app.quit();
|
||||
@@ -475,9 +473,14 @@ app.whenReady().then(() => {
|
||||
splashWindow.once('ready-to-show', () => splashWindow?.show());
|
||||
}
|
||||
|
||||
// 已手动启动后端
|
||||
setTimeout(() => {
|
||||
startSpringBoot();
|
||||
}, 200);
|
||||
|
||||
// setTimeout(() => {
|
||||
// openAppIfNotOpened();
|
||||
// }, 200);
|
||||
|
||||
app.on('activate', () => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
@@ -856,15 +859,7 @@ ipcMain.handle('dev-skip-backend', () => {
|
||||
return { success: false, error: '仅开发模式可用' };
|
||||
});
|
||||
|
||||
// 开发模式:手动启动后端
|
||||
ipcMain.handle('dev-start-backend', () => {
|
||||
if (isDev) {
|
||||
console.log('[开发模式] 前端请求启动后端');
|
||||
startSpringBoot();
|
||||
return { success: true };
|
||||
}
|
||||
return { success: false, error: '仅开发模式可用' };
|
||||
});
|
||||
|
||||
|
||||
// 窗口控制 API
|
||||
ipcMain.handle('window-minimize', () => {
|
||||
|
||||
@@ -51,6 +51,7 @@ const showAuthDialog = ref(false)
|
||||
const showRegDialog = ref(false)
|
||||
const zhCnLocale = zhCn
|
||||
const currentUsername = ref('')
|
||||
const registerTime = ref('')
|
||||
const showDeviceDialog = ref(false)
|
||||
const deviceLoading = ref(false)
|
||||
const devices = ref<DeviceItem[]>([])
|
||||
@@ -220,6 +221,7 @@ async function handleLoginSuccess(data: {
|
||||
vipExpireTime.value = data.expireTime ? new Date(data.expireTime) : null
|
||||
accountType.value = data.accountType || 'trial'
|
||||
deviceTrialExpired.value = data.deviceTrialExpired || false
|
||||
registerTime.value = data.registerTime || ''
|
||||
|
||||
const deviceId = await getOrCreateDeviceId()
|
||||
await deviceApi.register({
|
||||
@@ -249,6 +251,7 @@ async function clearLocalAuth() {
|
||||
removeToken()
|
||||
isAuthenticated.value = false
|
||||
currentUsername.value = ''
|
||||
registerTime.value = ''
|
||||
userPermissions.value = ''
|
||||
vipExpireTime.value = null
|
||||
deviceTrialExpired.value = false
|
||||
@@ -319,6 +322,7 @@ async function checkAuth() {
|
||||
userPermissions.value = res.data.permissions || ''
|
||||
deviceTrialExpired.value = res.data.deviceTrialExpired || false
|
||||
accountType.value = res.data.accountType || 'trial'
|
||||
registerTime.value = res.data.registerTime || ''
|
||||
|
||||
if (res.data.expireTime) {
|
||||
vipExpireTime.value = new Date(res.data.expireTime)
|
||||
@@ -640,15 +644,8 @@ onUnmounted(() => {
|
||||
<div class="user-avatar-section" @click="handleUserClick">
|
||||
<div class="avatar-wrapper">
|
||||
<img
|
||||
v-if="isAuthenticated"
|
||||
src="/image/img_v3_02qd_052605f0-4be3-44db-9691-35ee5ff6201g.jpg"
|
||||
alt="用户头像"
|
||||
class="user-avatar-img"
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
src="/image/user.png"
|
||||
alt="默认头像"
|
||||
alt="用户头像"
|
||||
class="user-avatar-img"
|
||||
/>
|
||||
</div>
|
||||
@@ -657,7 +654,7 @@ onUnmounted(() => {
|
||||
<span class="user-name">{{ isAuthenticated ? currentUsername : '登录/注册' }}</span>
|
||||
<span v-if="isAuthenticated && vipStatus.isVip" class="vip-badge">VIP {{ vipStatus.daysLeft }}天</span>
|
||||
</div>
|
||||
<div class="user-action">{{ isAuthenticated ? '18659156151' : '登录账号体验完整功能' }}</div>
|
||||
<div class="user-action">{{ isAuthenticated ? ` ${registerTime ? registerTime.replace('T', ' ').substring(0, 16) : '未知'}` : '登录账号体验完整功能' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -903,7 +900,6 @@ onUnmounted(() => {
|
||||
|
||||
/* 主Logo */
|
||||
.main-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px 0;
|
||||
@@ -929,8 +925,8 @@ onUnmounted(() => {
|
||||
|
||||
.avatar-wrapper {
|
||||
flex-shrink: 0;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
@@ -958,7 +954,7 @@ onUnmounted(() => {
|
||||
.user-name-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -968,15 +964,17 @@ onUnmounted(() => {
|
||||
color: #303133;
|
||||
white-space: nowrap;
|
||||
line-height: 1.2;
|
||||
flex-shrink: 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.vip-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0px 5px;
|
||||
height: 16px;
|
||||
padding: 0px 3px;
|
||||
height: 14px;
|
||||
background: #BAE0FF;
|
||||
border: 1px solid rgba(22, 119, 255, 0.05);
|
||||
border-radius: 8px;
|
||||
@@ -1021,7 +1019,8 @@ onUnmounted(() => {
|
||||
.brand-logo {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
object-fit: cover;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.menu {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export type HttpMethod = 'GET' | 'POST' | 'DELETE';
|
||||
const RUOYI_BASE = 'http://8.138.23.49:8085';
|
||||
// const RUOYI_BASE = 'http://192.168.1.89:8085';
|
||||
// const RUOYI_BASE = 'http://192.168.1.89:8085';
|
||||
export const CONFIG = {
|
||||
CLIENT_BASE: 'http://localhost:8081',
|
||||
RUOYI_BASE,
|
||||
|
||||
@@ -2,28 +2,28 @@ import { http } from './http'
|
||||
|
||||
export const markApi = {
|
||||
// 新建任务(调用 erp_client_sb)
|
||||
newTask(file: File) {
|
||||
newTask(file: File, signal?: AbortSignal) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
return http.upload<{ code: number, data: any, msg: string }>('/api/trademark/newTask', formData)
|
||||
return http.upload<{ code: number, data: any, msg: string }>('/api/trademark/newTask', formData, signal)
|
||||
},
|
||||
|
||||
// 获取任务列表及筛选数据(调用 erp_client_sb)
|
||||
getTask() {
|
||||
return http.post<{
|
||||
code: number,
|
||||
data: {
|
||||
original: any,
|
||||
getTask(signal?: AbortSignal) {
|
||||
return http.post<{
|
||||
code: number,
|
||||
data: {
|
||||
original: any,
|
||||
filtered: Record<string, any>[], // 完整的行数据(Map格式)
|
||||
headers: string[] // 表头
|
||||
},
|
||||
msg: string
|
||||
}>('/api/trademark/task')
|
||||
},
|
||||
msg: string
|
||||
}>('/api/trademark/task', undefined, signal)
|
||||
},
|
||||
|
||||
// 品牌商标筛查
|
||||
brandCheck(brands: string[], taskId?: string) {
|
||||
return http.post<{ code: number, data: { total: number, checked: number, registered: number, unregistered: number, failed: number, data: any[], duration: string }, msg: string }>('/api/trademark/brandCheck', { brands, taskId })
|
||||
brandCheck(brands: string[], taskId?: string, signal?: AbortSignal) {
|
||||
return http.post<{ code: number, data: { total: number, checked: number, registered: number, unregistered: number, failed: number, data: any[], duration: string }, msg: string }>('/api/trademark/brandCheck', { brands, taskId }, signal)
|
||||
},
|
||||
|
||||
// 查询品牌筛查进度
|
||||
|
||||
@@ -107,10 +107,28 @@ function handleCancelTask() {
|
||||
}
|
||||
}
|
||||
|
||||
const isRetrying = ref(false)
|
||||
let lastRetryTime = 0
|
||||
|
||||
function handleRetryTask() {
|
||||
const now = Date.now()
|
||||
if (now - lastRetryTime < 3000 || isRetrying.value || loading.value || trademarkPanelRef.value?.trademarkLoading) {
|
||||
return
|
||||
}
|
||||
|
||||
lastRetryTime = now
|
||||
isRetrying.value = true
|
||||
|
||||
if (trademarkPanelRef.value && typeof trademarkPanelRef.value.startTrademarkQuery === 'function') {
|
||||
trademarkPanelRef.value.startTrademarkQuery()
|
||||
}
|
||||
|
||||
const checkInterval = setInterval(() => {
|
||||
if (trademarkPanelRef.value?.trademarkLoading || Date.now() - now > 2000) {
|
||||
clearInterval(checkInterval)
|
||||
isRetrying.value = false
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
|
||||
function openSubscribeDialog() {
|
||||
@@ -176,19 +194,6 @@ function handleExportData() {
|
||||
<!-- 数据显示区域 -->
|
||||
<div class="table-container">
|
||||
<div class="table-section">
|
||||
<!-- 表格上方进度条 -->
|
||||
<div v-if="progressVisible || (currentTab === 'trademark' && progressPercentage > 0)" class="progress-head">
|
||||
<div class="progress-section">
|
||||
<div class="progress-box">
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: progressPercentage + '%' }"></div>
|
||||
</div>
|
||||
<div class="progress-text">{{ progressPercentage }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper">
|
||||
<!-- 跟卖精灵内容 -->
|
||||
@@ -276,15 +281,14 @@ function handleExportData() {
|
||||
</svg>
|
||||
</div>
|
||||
<div class="banner-content">
|
||||
<div class="banner-title">已取消查询</div>
|
||||
<div class="banner-desc">您已取消本次查询任务</div>
|
||||
<div class="banner-title">筛查已取消</div>
|
||||
<div class="banner-desc">点击右侧“重试”按钮可继续,或导入新的选品表格重新筛查。</div>
|
||||
</div>
|
||||
<div class="banner-actions">
|
||||
<el-button size="default" @click="handleNewTask">新建任务</el-button>
|
||||
<el-button type="primary" size="default" @click="handleRetryTask">重新筛查</el-button>
|
||||
<el-button type="primary" size="default" @click="handleRetryTask" :disabled="loading || isRetrying">重新筛查</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 完成/失败状态横幅 -->
|
||||
<div v-if="trademarkPanelRef?.queryStatus === 'done' || trademarkPanelRef?.queryStatus === 'error' || trademarkPanelRef?.queryStatus === 'networkError'" class="status-banner done-banner">
|
||||
<div class="banner-icon">
|
||||
@@ -305,7 +309,7 @@ function handleExportData() {
|
||||
<div class="banner-actions">
|
||||
<el-button size="default" @click="handleNewTask">新建任务</el-button>
|
||||
<el-button v-if="trademarkPanelRef.queryStatus === 'done'" type="primary" size="default" @click="handleExportData">导出数据</el-button>
|
||||
<el-button v-else type="primary" size="default" @click="handleRetryTask">重新筛查</el-button>
|
||||
<el-button v-else type="primary" size="default" @click="handleRetryTask" :disabled="loading || isRetrying">重新筛查</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -429,11 +433,11 @@ function handleExportData() {
|
||||
</div>
|
||||
<div class="task-stat highlight">
|
||||
<span class="stat-label">未注册</span>
|
||||
<span class="stat-value">{{ trademarkPanelRef?.isBrandTaskRealData ? trademarkPanelRef.taskProgress.brand.completed : '-' }}</span>
|
||||
<span class="stat-value">{{ trademarkPanelRef?.brandStatsDisplay?.unregistered || '-' }}</span>
|
||||
</div>
|
||||
<div class="task-stat">
|
||||
<span class="stat-label">已注册</span>
|
||||
<span class="stat-value">{{ trademarkPanelRef?.isBrandTaskRealData ? (trademarkPanelRef.taskProgress.brand.total - trademarkPanelRef.taskProgress.brand.completed) : '-' }}</span>
|
||||
<span class="stat-value">{{ trademarkPanelRef?.brandStatsDisplay?.registered || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -702,13 +706,6 @@ function handleExportData() {
|
||||
.text:focus { border-color: #409EFF; }
|
||||
.text:disabled { background: #f5f7fa; color: #c0c4cc; }
|
||||
.action-buttons { display: flex; gap: 10px; flex-wrap: wrap; }
|
||||
.progress-section { margin: 0px 12px 0px 12px; }
|
||||
.progress-head { margin-bottom: 8px; }
|
||||
.progress-box { padding: 4px 0; }
|
||||
.progress-container { display: flex; align-items: center; gap: 8px; }
|
||||
.progress-bar { flex: 1; height: 6px; background: #e3eeff; border-radius: 999px; overflow: hidden; }
|
||||
.progress-fill { height: 100%; background: #1677FF; border-radius: 999px; transition: width 0.3s ease; }
|
||||
.progress-text { font-size: 13px; color: #1677FF; font-weight: 500; min-width: 44px; text-align: right; }
|
||||
.current-status { font-size: 12px; color: #606266; padding-left: 2px; }
|
||||
.table-container {
|
||||
display: flex;
|
||||
@@ -970,6 +967,7 @@ function handleExportData() {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.done-banner .banner-icon {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ defineExpose({
|
||||
.step-header { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
|
||||
.title { font-size: 14px; font-weight: 600; color: #303133; text-align: left; }
|
||||
.desc { font-size: 12px; color: #909399; margin-bottom: 10px; text-align: left; line-height: 1.5; }
|
||||
.links { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
|
||||
.links { display: flex; align-items: center; gap: 2px; margin-bottom: 8px; }
|
||||
.link { color: #409EFF; cursor: pointer; font-size: 12px; }
|
||||
.sep { color: #dcdfe6; }
|
||||
.dropzone { border: 1px dashed #c0c4cc; border-radius: 6px; padding: 16px; text-align: center; cursor: pointer; background: #fafafa; }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user