This commit is contained in:
2025-09-30 17:16:11 +08:00
parent e650a7c7f3
commit 52ce0e1969
25 changed files with 689 additions and 989 deletions

View File

@@ -134,56 +134,43 @@ function handleMenuSelect(key: string) {
async function handleLoginSuccess(data: { token: string; permissions?: string }) {
isAuthenticated.value = true
showAuthDialog.value = false
showRegDialog.value = false // 确保注册对话框也关闭
showRegDialog.value = false
try {
// 保存token到本地数据库
await authApi.saveToken(data.token)
const username = getUsernameFromToken(data.token)
currentUsername.value = username
userPermissions.value = data?.permissions || ''
await deviceApi.register({username})
// 建立SSE连接
SSEManager.connect()
} catch (e: any) {
// 设备注册失败时回滚登录状态
isAuthenticated.value = false
showAuthDialog.value = true
await authApi.deleteTokenCache()
ElMessage({
message: e?.message || '设备注册失败,请重试',
type: 'error'
})
ElMessage.error(e?.message || '设备注册失败')
}
}
async function logout() {
// 主动设置设备离线
try {
const deviceId = await getClientIdFromToken()
if (deviceId) {
await deviceApi.offline({ deviceId })
}
if (deviceId) await deviceApi.offline({ deviceId })
} catch (error) {
console.warn('离线通知失败:', error)
}
const token = await authApi.getToken()
if (token) {
await authApi.logout(token)
}
try {
const tokenRes: any = await authApi.getToken()
const token = typeof tokenRes === 'string' ? tokenRes : tokenRes?.data
if (token) await authApi.logout(token)
} catch {}
await authApi.deleteTokenCache()
// 清理前端状态
isAuthenticated.value = false
currentUsername.value = ''
userPermissions.value = ''
showAuthDialog.value = true
showDeviceDialog.value = false
// 关闭SSE连接
SSEManager.disconnect()
}
@@ -199,12 +186,8 @@ async function handleUserClick() {
cancelButtonText: '取消'
})
await logout()
ElMessage({
message: '已退出登录',
type: 'success'
})
} catch {
}
ElMessage.success('已退出登录')
} catch {}
}
function showRegisterDialog() {
@@ -218,26 +201,23 @@ function backToLogin() {
}
async function checkAuth() {
const authRequiredMenus = ['rakuten', 'amazon', 'zebra', 'shopee']
try {
await authApi.sessionBootstrap().catch(() => undefined)
const token = await authApi.getToken()
const tokenRes: any = await authApi.getToken()
const token = typeof tokenRes === 'string' ? tokenRes : tokenRes?.data
if (token) {
const response = await authApi.verifyToken(token)
if (response?.success) {
isAuthenticated.value = true
currentUsername.value = getUsernameFromToken(token) || ''
SSEManager.connect()
return
}
await authApi.deleteTokenCache()
await authApi.verifyToken(token)
isAuthenticated.value = true
currentUsername.value = getUsernameFromToken(token) || ''
SSEManager.connect()
return
}
} catch {
// 忽略
await authApi.deleteTokenCache()
}
if (authRequiredMenus.includes(activeMenu.value)) {
if (['rakuten', 'amazon', 'zebra', 'shopee'].includes(activeMenu.value)) {
showAuthDialog.value = true
}
}
@@ -246,11 +226,11 @@ async function getClientIdFromToken(token?: string) {
try {
let t = token
if (!t) {
t = await authApi.getToken()
const tokenRes: any = await authApi.getToken()
t = typeof tokenRes === 'string' ? tokenRes : tokenRes?.data
}
if (!t) return ''
const payload = JSON.parse(atob(t.split('.')[1] || ''))
const payload = JSON.parse(atob(t.split('.')[1]))
return payload.clientId || ''
} catch {
return ''
@@ -259,7 +239,7 @@ async function getClientIdFromToken(token?: string) {
function getUsernameFromToken(token: string) {
try {
const payload = JSON.parse(atob(token.split('.')[1] || ''))
const payload = JSON.parse(atob(token.split('.')[1]))
return payload.username || ''
} catch {
return ''
@@ -273,21 +253,24 @@ const SSEManager = {
if (this.connection) return
try {
const token = await authApi.getToken()
if (!token) return
const tokenRes: any = await authApi.getToken()
const token = typeof tokenRes === 'string' ? tokenRes : tokenRes?.data
if (!token) {
console.warn('SSE连接失败: 没有有效的 token')
return
}
const clientId = await getClientIdFromToken(token)
if (!clientId) return
if (!clientId) {
console.warn('SSE连接失败: 无法从 token 获取 clientId')
return
}
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 {
}
if (resp.ok) sseUrl = (await resp.json()).sseUrl || sseUrl
} catch {}
const src = new EventSource(`${sseUrl}?clientId=${clientId}&token=${token}`)
this.connection = src
@@ -296,15 +279,13 @@ const SSEManager = {
src.onerror = () => this.handleError()
} catch (e: any) {
console.warn('SSE连接失败:', e?.message || e)
this.disconnect()
}
},
handleMessage(e: MessageEvent) {
try {
// 处理ping心跳
if (e.type === 'ping') {
return // ping消息自动保持连接无需处理
}
if (e.type === 'ping') return
console.log('SSE消息:', e.data)
const payload = JSON.parse(e.data)
@@ -314,17 +295,11 @@ const SSEManager = {
break
case 'DEVICE_REMOVED':
logout()
ElMessage({
message: '您的设备已被移除,请重新登录',
type: 'warning'
})
ElMessage.warning('您的设备已被移除,请重新登录')
break
case 'FORCE_LOGOUT':
logout()
ElMessage({
message: '会话已失效,请重新登录',
type: 'warning'
})
ElMessage.warning('会话已失效,请重新登录')
break
case 'PERMISSIONS_UPDATED':
checkAuth()
@@ -336,18 +311,16 @@ const SSEManager = {
},
handleError() {
this.disconnect()
setTimeout(() => this.connect(), 3000)
if (!this.connection) return
try { this.connection.close() } catch {}
this.connection = null
console.warn('SSE连接失败,已断开')
},
disconnect() {
if (this.connection) {
try {
this.connection.close()
} catch {
}
this.connection = null
}
if (!this.connection) return
try { this.connection.close() } catch {}
this.connection = null
},
}
@@ -366,26 +339,22 @@ function openSettings() {
async function fetchDeviceData() {
if (!currentUsername.value) {
ElMessage({
message: '未获取到用户名,请重新登录',
type: 'warning'
})
ElMessage.warning('未获取到用户名,请重新登录')
return
}
try {
deviceLoading.value = true
const [quota, list] = await Promise.all([
const [quotaRes, listRes] = await Promise.all([
deviceApi.getQuota(currentUsername.value),
deviceApi.list(currentUsername.value),
])
deviceQuota.value = quota || {limit: 0, used: 0}
]) as any[]
deviceQuota.value = quotaRes?.data || quotaRes || {limit: 0, used: 0}
const clientId = await getClientIdFromToken()
devices.value = (list || []).map(d => ({...d, isCurrent: d.deviceId === clientId})) as any
const list = listRes?.data || listRes || []
devices.value = list.map(d => ({...d, isCurrent: d.deviceId === clientId}))
} catch (e: any) {
ElMessage({
message: e?.message || '获取设备列表失败',
type: 'error'
})
ElMessage.error(e?.message || '获取设备列表失败')
} finally {
deviceLoading.value = false
}
@@ -403,21 +372,12 @@ async function confirmRemoveDevice(row: DeviceItem & { isCurrent?: boolean }) {
devices.value = devices.value.filter(d => d.deviceId !== row.deviceId)
deviceQuota.value.used = Math.max(0, (deviceQuota.value.used || 0) - 1)
// 如果是本机设备被移除执行logout
const clientId = await getClientIdFromToken()
if (row.deviceId === clientId) {
await logout()
}
if (row.deviceId === clientId) await logout()
ElMessage({
message: '已移除设备',
type: 'success'
})
ElMessage.success('已移除设备')
} catch (e: any) {
ElMessage({
message: '移除设备失败: ' + ((e as any)?.message || '未知错误'),
type: 'error'
})
if (e !== 'cancel') ElMessage.error('移除设备失败: ' + (e?.message || '未知错误'))
}
}