This commit is contained in:
2025-09-22 14:50:20 +08:00
parent a76d3a4337
commit d5fa814cd6
4 changed files with 110 additions and 49 deletions

View File

@@ -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) {
/* 用户取消或失败 */

View File

@@ -329,7 +329,7 @@ onMounted(async () => {
</div>
<!-- 表格加载遮罩 -->
<div v-if="tableLoading" class="table-loading">
<div v-if="tableLoading && paginatedData.length === 0" class="table-loading">
<div class="spinner"></div>
<div>加载中...</div>
</div>
@@ -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; }

View File

@@ -369,7 +369,7 @@ onMounted(loadLatest)
</div>
<!-- 表格加载遮罩 -->
<div v-if="tableLoading" class="table-loading">
<div v-if="tableLoading && paginatedData.length === 0" class="table-loading">
<div class="spinner"></div>
<div>加载中...</div>
</div>
@@ -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;

View File

@@ -174,20 +174,7 @@ onMounted(async () => {
</div>
</div>
<!-- 进度条显示 -->
<div class="progress-section" v-if="showProgress">
<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 class="current-status" v-if="fetchTotalItems > 0">
{{ progressPercentage >= 100 ? '完成' : `获取中... (${allOrderData.length}/${fetchTotalItems})` }}
</div>
</div>
</div>
<!-- 进度条显示移动到底部以免挤压表头 -->
</div>
<!-- 数据显示区域 -->
@@ -256,15 +243,19 @@ onMounted(async () => {
</div>
</div>
<!-- 表格加载遮罩 -->
<!-- 表格加载遮罩仅在无数据时显示 -->
<div v-if="loading && !allOrderData.length" class="table-loading">
<div class="spinner"></div>
<div>加载中...</div>
</div>
</div>
<!-- 分页器 -->
<!-- 底部区域进度条 + 分页器 -->
<div class="pagination-fixed">
<div v-if="showProgress" class="progress-bottom">
<div class="progress-bar"><div class="progress-fill" :style="{ width: progressPercentage + '%' }"></div></div>
<div class="progress-text">{{ progressPercentage }}%</div>
</div>
<el-pagination
background
:current-page="currentPage"
@@ -296,8 +287,8 @@ export default {
.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; }
@@ -326,6 +317,7 @@ export default {
.empty-icon { font-size: 48px; margin-bottom: 12px; opacity: 0.6; }
.empty-text { font-size: 14px; color: #909399; }
.empty-abs { position: absolute; left: 0; right: 0; top: 48px; bottom: 0; display: flex; align-items: center; justify-content: center; }
.progress-bottom { display: flex; align-items: center; gap: 8px; margin-right: auto; }
</style>