diff --git a/electron-vue-template/src/renderer/App.vue b/electron-vue-template/src/renderer/App.vue index c256961..d2784fa 100644 --- a/electron-vue-template/src/renderer/App.vue +++ b/electron-vue-template/src/renderer/App.vue @@ -118,11 +118,34 @@ async function handleLoginSuccess(data: { token: string; permissions?: string }) currentUsername.value = username userPermissions.value = data?.permissions || '' await deviceApi.register({ username }) + + // 建立SSE连接 + SSEManager.connect() } catch (e) { // 设备注册失败不影响登录流程,静默处理 console.warn('设备注册失败:', e) } } +async function logout() { + try { + // 删除后端token缓存 - 复刻HTML版本逻辑 + await fetch('/api/cache/delete?key=token', { method: 'POST' }) + } catch (e) { + console.warn('删除后端token缓存失败:', e) + } + + // 清理前端状态 + try { localStorage.removeItem('token') } catch {} + isAuthenticated.value = false + currentUsername.value = '' + userPermissions.value = '' + showAuthDialog.value = true + showDeviceDialog.value = false + + // 关闭SSE连接 + SSEManager.disconnect() +} + async function handleUserClick() { if (!isAuthenticated.value) { showAuthDialog.value = true @@ -130,14 +153,7 @@ async function handleUserClick() { } try { await ElMessageBox.confirm('确认退出登录?', '提示', { type: 'warning', confirmButtonText: '退出', cancelButtonText: '取消' }) - const token = localStorage.getItem('token') || '' - try { await authApi.logout(token) } catch {} - try { localStorage.removeItem('token') } catch {} - isAuthenticated.value = false - currentUsername.value = '' - userPermissions.value = '' - showAuthDialog.value = true - showDeviceDialog.value = false + await logout() ElMessage.success('已退出登录') } catch {} } @@ -177,6 +193,9 @@ async function checkAuth() { if (u) currentUsername.value = u } userPermissions.value = response.permissions || '' + + // 认证成功后建立SSE连接 + SSEManager.connect() return } } catch { @@ -204,6 +223,67 @@ function getUsernameFromToken(token?: string) { return payload.username || '' } +// SSE管理器 - 简化封装 +const SSEManager = { + connection: null as EventSource | null, + + async connect() { + if (this.connection) return + + const token = localStorage.getItem('token') + const clientId = getClientIdFromToken(token) + if (!token || !clientId) return + + try { + // 简化配置获取,失败时使用默认配置 + let sseUrl = 'http://192.168.1.89:8080/monitor/account/events' + try { + const resp = await fetch('/api/config/server') + if (resp.ok) { + const config = await resp.json() + sseUrl = config.sseUrl || sseUrl + } + } catch {} + + const src = new EventSource(`${sseUrl}?clientId=${clientId}&token=${token}`) + this.connection = src + + src.onmessage = (e) => this.handleMessage(e) + src.onerror = () => this.handleError() + } catch (e) { + console.warn('SSE连接失败:', e.message) + } + }, + + handleMessage(e: MessageEvent) { + try { + const payload = JSON.parse(e.data) + switch (payload.type) { + case 'DEVICE_REMOVED': + case 'FORCE_LOGOUT': + logout() + ElMessage.warning('会话已失效,请重新登录') + break + case 'PERMISSIONS_UPDATED': + checkAuth() + break + } + } catch {} + }, + + handleError() { + this.disconnect() + setTimeout(() => this.connect(), 3000) + }, + + disconnect() { + if (this.connection) { + try { this.connection.close() } catch {} + this.connection = null + } + } +} + async function openDeviceManager() { if (!isAuthenticated.value) { showAuthDialog.value = true @@ -241,12 +321,13 @@ async function confirmRemoveDevice(row: DeviceItem & { isCurrent?: boolean }) { await deviceApi.remove({ deviceId: row.deviceId }) devices.value = devices.value.filter(d => d.deviceId !== row.deviceId) deviceQuota.value.used = Math.max(0, (deviceQuota.value.used || 0) - 1) - if (row.isCurrent) { - // 当前设备被移除,清理登录状态 - isAuthenticated.value = false - showAuthDialog.value = true - try { localStorage.removeItem('token') } catch {} + + // 如果是本机设备被移除,执行logout - 复刻HTML版本逻辑 + const clientId = getClientIdFromToken() + if (row.deviceId === clientId) { + await logout() } + ElMessage.success('已移除设备') } catch (e) { /* 用户取消或失败 */ diff --git a/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue b/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue index 5dd44f6..e072b6c 100644 --- a/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue +++ b/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue @@ -329,7 +329,7 @@ onMounted(async () => { -
+
加载中...
@@ -366,8 +366,8 @@ onMounted(async () => { .progress-section { margin: 15px 0 10px 0; } .progress-box { padding: 8px 0; } .progress-container { display: flex; align-items: center; position: relative; padding-right: 50px; margin-bottom: 8px; } -.progress-bar { flex: 1; height: 6px; background: #ebeef5; border-radius: 3px; overflow: hidden; } -.progress-fill { height: 100%; background: linear-gradient(90deg, #409EFF, #66b1ff); border-radius: 3px; transition: width 0.3s ease; } +.progress-bar { flex: 1; height: 3px; background: #ebeef5; border-radius: 2px; overflow: hidden; } +.progress-fill { height: 100%; background: linear-gradient(90deg, #409EFF, #66b1ff); border-radius: 2px; transition: width 0.3s ease; } .progress-text { position: absolute; right: 0; font-size: 13px; color: #409EFF; font-weight: 500; } .current-status { font-size: 12px; color: #606266; padding-left: 2px; } .table-container { display: flex; flex-direction: column; flex: 1; min-height: 400px; overflow: hidden; } diff --git a/electron-vue-template/src/renderer/components/rakuten/RakutenDashboard.vue b/electron-vue-template/src/renderer/components/rakuten/RakutenDashboard.vue index b6a5a60..9b4431d 100644 --- a/electron-vue-template/src/renderer/components/rakuten/RakutenDashboard.vue +++ b/electron-vue-template/src/renderer/components/rakuten/RakutenDashboard.vue @@ -369,7 +369,7 @@ onMounted(loadLatest)
-
+
加载中...
@@ -454,20 +454,8 @@ onMounted(loadLatest) margin-bottom: 8px; } -.progress-bar { - flex: 1; - height: 6px; - background: #ebeef5; - border-radius: 3px; - overflow: hidden; -} - -.progress-fill { - height: 100%; - background: linear-gradient(90deg, #409EFF, #66b1ff); - border-radius: 3px; - transition: width 0.3s ease; -} +.progress-bar { flex: 1; height: 3px; background: #ebeef5; border-radius: 2px; overflow: hidden; } +.progress-fill { height: 100%; background: linear-gradient(90deg, #409EFF, #66b1ff); border-radius: 2px; transition: width 0.3s ease; } .progress-text { position: absolute; diff --git a/electron-vue-template/src/renderer/components/zebra/ZebraDashboard.vue b/electron-vue-template/src/renderer/components/zebra/ZebraDashboard.vue index c86deae..37fc943 100644 --- a/electron-vue-template/src/renderer/components/zebra/ZebraDashboard.vue +++ b/electron-vue-template/src/renderer/components/zebra/ZebraDashboard.vue @@ -174,20 +174,7 @@ onMounted(async () => {
- -
-
-
-
-
-
-
{{ progressPercentage }}%
-
-
- {{ progressPercentage >= 100 ? '完成' : `获取中... (${allOrderData.length}/${fetchTotalItems})` }} -
-
-
+ @@ -256,15 +243,19 @@ onMounted(async () => { - +
加载中...
- +
+
+
+
{{ progressPercentage }}%
+