279 lines
8.3 KiB
Vue
279 lines
8.3 KiB
Vue
<template>
|
||
<div class="container">
|
||
<header class="header">
|
||
<div class="header-left">
|
||
<h1>Boss - 远程监听服务 <span style="font-size: 0.7em; opacity: 0.8;">v{{ currentVersion }}</span></h1>
|
||
<div class="status-indicator">
|
||
<span :class="statusDotClass"></span>
|
||
<span>{{ isConnected ? '已连接' : '未连接' }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="header-right">
|
||
<!-- 登录页面不显示 UserMenu -->
|
||
<UserMenu v-if="showSidebar" />
|
||
</div>
|
||
</header>
|
||
|
||
<div class="main-content">
|
||
<!-- 左侧菜单 - 登录页面不显示 -->
|
||
<Sidebar v-if="showSidebar" />
|
||
|
||
<!-- 内容区域 - 使用 router-view 切换页面 -->
|
||
<div class="content-area" :class="{ 'full-width': !showSidebar }">
|
||
<router-view />
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- 更新弹窗 - 使用 store 管理状态 -->
|
||
<UpdateDialog />
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import Sidebar from './components/Sidebar.vue';
|
||
import UpdateDialog from './components/UpdateDialog.vue';
|
||
import UserMenu from './components/UserMenu.vue';
|
||
|
||
// 导入 Vuex helpers
|
||
import { mapState } from 'vuex';
|
||
|
||
// 导入 Mixins
|
||
import logMixin from './mixins/logMixin.js';
|
||
import authMixin from './mixins/authMixin.js';
|
||
import mqttMixin from './mixins/mqttMixin.js';
|
||
import taskMixin from './mixins/taskMixin.js';
|
||
import systemInfoMixin from './mixins/systemInfoMixin.js';
|
||
import platformMixin from './mixins/platformMixin.js';
|
||
import qrCodeMixin from './mixins/qrCodeMixin.js';
|
||
import updateMixin from './mixins/updateMixin.js';
|
||
import eventListenerMixin from './mixins/eventListenerMixin.js';
|
||
|
||
// Vue 应用主组件逻辑
|
||
export default {
|
||
name: 'App',
|
||
mixins: [
|
||
logMixin,
|
||
authMixin,
|
||
mqttMixin,
|
||
taskMixin,
|
||
systemInfoMixin,
|
||
platformMixin,
|
||
qrCodeMixin,
|
||
updateMixin,
|
||
eventListenerMixin
|
||
],
|
||
components: {
|
||
Sidebar,
|
||
UpdateDialog,
|
||
UserMenu
|
||
},
|
||
data() {
|
||
return {
|
||
// 应用启动时间(用于计算运行时间)
|
||
startTime: Date.now(),
|
||
|
||
// 应用加载状态
|
||
isLoading: true,
|
||
|
||
// 浏览器窗口状态
|
||
browserWindowVisible: false
|
||
};
|
||
},
|
||
|
||
mounted() {
|
||
this.init();
|
||
},
|
||
|
||
// beforeDestroy 已在 taskMixin 中定义
|
||
|
||
watch: {
|
||
// 监听登录状态变化
|
||
isLoggedIn(newVal, oldVal) {
|
||
// 未登录时自动跳转到登录页面
|
||
if (!newVal && this.$route.name !== 'Login') {
|
||
this.$router.push('/login');
|
||
}
|
||
|
||
// 登录状态从 false 变为 true 时,检查 MQTT 状态(连接由主进程自动处理)
|
||
if (newVal && !oldVal) {
|
||
// 延迟一下,确保 store 中的 snCode 已经更新
|
||
this.$nextTick(() => {
|
||
setTimeout(() => {
|
||
// MQTT 连接由主进程自动处理,这里只检查状态
|
||
if (this.checkMQTTStatus) {
|
||
this.checkMQTTStatus();
|
||
}
|
||
// 开始获取任务状态
|
||
if (this.startTaskStatusUpdate) {
|
||
this.startTaskStatusUpdate();
|
||
}
|
||
}, 500);
|
||
});
|
||
}
|
||
}
|
||
},
|
||
|
||
methods: {
|
||
async init() {
|
||
try {
|
||
// 先隐藏加载屏幕,避免一直显示
|
||
this.hideLoadingScreen();
|
||
|
||
if (window.electronAPI && window.electronAPI.invoke) {
|
||
const result = await window.electronAPI.invoke('system:get-version');
|
||
if (result && result.success && result.version) {
|
||
this.currentVersion = result.version;
|
||
this.addLog('info', `当前版本: v${this.currentVersion}`);
|
||
} else if (window.appInfo && window.appInfo.version) {
|
||
this.currentVersion = window.appInfo.version;
|
||
}
|
||
} else if (window.appInfo && window.appInfo.version) {
|
||
this.currentVersion = window.appInfo.version;
|
||
}
|
||
} catch (error) {
|
||
console.error('获取版本号失败:', error);
|
||
if (window.appInfo && window.appInfo.version) {
|
||
this.$store.commit('app/SET_VERSION', window.appInfo.version);
|
||
}
|
||
}
|
||
|
||
this.setupEventListeners();
|
||
await this.loadSavedConfig();
|
||
|
||
// 加载投递配置到 store
|
||
if (this.$store && this.$store.dispatch) {
|
||
await this.$store.dispatch('delivery/loadDeliveryConfig');
|
||
}
|
||
|
||
this.startSystemInfoUpdate();
|
||
|
||
// 尝试自动登录
|
||
const autoLoginSuccess = await this.tryAutoLogin();
|
||
|
||
// 如果已登录,开始获取任务状态
|
||
if (this.$store.state.auth.isLoggedIn) {
|
||
this.startTaskStatusUpdate();
|
||
|
||
// 检查 MQTT 连接状态
|
||
this.checkMQTTStatus();
|
||
} else if (!autoLoginSuccess) {
|
||
// 未登录时,自动跳转到登录页面
|
||
this.$router.push('/login');
|
||
}
|
||
},
|
||
|
||
hideLoadingScreen() {
|
||
setTimeout(() => {
|
||
const loadingScreen = document.getElementById('loading-screen');
|
||
const app = document.getElementById('app');
|
||
|
||
if (loadingScreen) {
|
||
loadingScreen.classList.add('hidden');
|
||
setTimeout(() => {
|
||
if (loadingScreen.parentNode) {
|
||
loadingScreen.remove();
|
||
}
|
||
}, 500);
|
||
}
|
||
|
||
if (app) {
|
||
app.style.display = 'block';
|
||
}
|
||
|
||
this.isLoading = false;
|
||
}, 500);
|
||
},
|
||
|
||
// setupEventListeners 已在 eventListenerMixin 中定义
|
||
// loadSavedConfig, checkActivationStatus, userLogin, tryAutoLogin 已在 authMixin 中定义
|
||
// startTaskStatusUpdate, updateCurrentTask, onTaskStatusUpdate 已在 taskMixin 中定义
|
||
|
||
// connectMQTT, disconnectMQTT, onMQTTConnected, onMQTTDisconnected, onMQTTMessage, onMQTTStatusChange 已在 mqttMixin 中定义
|
||
// handleMenuChange, handleUpdateDeliveryConfig, handleLoginFromPage, handleLoginFromDialog 已不需要,使用路由和组件内部处理
|
||
|
||
/**
|
||
* 检查 MQTT 连接状态,如果未连接则尝试重新连接
|
||
*/
|
||
async checkMQTTStatus() {
|
||
try {
|
||
if (!window.electronAPI || !window.electronAPI.invoke) {
|
||
console.warn('[App] electronAPI 不可用');
|
||
return;
|
||
}
|
||
|
||
// 获取当前 MQTT 状态
|
||
const status = await window.electronAPI.invoke('mqtt:status');
|
||
console.log('[App] MQTT 状态查询结果:', status);
|
||
|
||
if (status && status.isConnected) {
|
||
// 如果已经连接,更新状态
|
||
if (this.$store) {
|
||
this.$store.dispatch('mqtt/setConnected', true);
|
||
console.log('[App] MQTT 状态已更新为已连接');
|
||
}
|
||
} else {
|
||
// 如果未连接,尝试重新连接(需要 snCode)
|
||
const snCode = this.$store?.state?.auth?.snCode;
|
||
if (snCode) {
|
||
console.log('[App] MQTT 未连接,尝试重新连接...');
|
||
try {
|
||
await window.electronAPI.invoke('mqtt:connect', snCode);
|
||
console.log('[App] MQTT 重新连接成功');
|
||
if (this.$store) {
|
||
this.$store.dispatch('mqtt/setConnected', true);
|
||
}
|
||
} catch (error) {
|
||
console.warn('[App] MQTT 重新连接失败:', error);
|
||
if (this.$store) {
|
||
this.$store.dispatch('mqtt/setConnected', false);
|
||
}
|
||
}
|
||
} else {
|
||
console.warn('[App] 无法重新连接 MQTT: 缺少 snCode');
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('[App] 检查 MQTT 状态失败:', error);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
},
|
||
|
||
computed: {
|
||
...mapState('app', ['currentVersion']),
|
||
...mapState('mqtt', ['isConnected']),
|
||
...mapState('auth', ['isLoggedIn', 'userName', 'snCode', 'deviceId', 'remainingDays', 'phone']),
|
||
// 根据路由 meta 决定是否显示侧边栏,默认为 true
|
||
showSidebar() {
|
||
return this.$route.meta.showSidebar !== false;
|
||
},
|
||
statusDotClass() {
|
||
return {
|
||
'status-dot': true,
|
||
'connected': this.isConnected
|
||
};
|
||
}
|
||
// logEntries 已在 logMixin 中定义
|
||
}
|
||
|
||
// snCode watch 已在 authMixin 中定义
|
||
};
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
// 登录页面时,内容区域不需要背景和padding
|
||
.content-area.full-width {
|
||
// 当侧边栏隐藏时,登录页面应该有特殊样式
|
||
&.login-page {
|
||
padding: 0;
|
||
background: transparent;
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
}
|
||
}
|
||
</style>
|
||
|