1
This commit is contained in:
230
app/mixins/authMixin.js
Normal file
230
app/mixins/authMixin.js
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* 用户认证管理 Mixin
|
||||
*/
|
||||
import { getToken } from '../utils/api';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
phone: '',
|
||||
password: '',
|
||||
isLoggedIn: false,
|
||||
loginButtonText: '登录',
|
||||
userName: '',
|
||||
remainingDays: null,
|
||||
snCode: '',
|
||||
deviceId: '-',
|
||||
listenChannel: '-',
|
||||
userMenuInfo: {
|
||||
userName: '',
|
||||
snCode: ''
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async loadSavedConfig() {
|
||||
try {
|
||||
// 从 store 加载保存的手机号
|
||||
if (this.$store) {
|
||||
const savedPhone = this.$store.state.config.phone || this.$store.state.auth.phone;
|
||||
if (savedPhone) {
|
||||
this.phone = savedPhone;
|
||||
}
|
||||
}
|
||||
|
||||
// 注意:现在数据都在 store 中,通过持久化插件自动恢复
|
||||
// 如果需要从主进程同步数据,可以在这里调用,但通常不需要
|
||||
// 因为登录成功后已经通过 syncUserInfo 同步到主进程了
|
||||
} catch (error) {
|
||||
console.error('加载配置失败:', error);
|
||||
if (this.addLog) {
|
||||
this.addLog('error', `加载配置失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// checkActivationStatus 方法已移除
|
||||
// 现在登录状态由 Vuex Store 管理,通过持久化插件自动恢复
|
||||
// 不再需要从主进程获取状态
|
||||
|
||||
// 用户登录(只调用主进程接口,业务逻辑由主进程处理)
|
||||
async userLogin(password, rememberMe = true) {
|
||||
// 基本验证
|
||||
if (!this.phone) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', '请输入手机号');
|
||||
}
|
||||
return { success: false, error: '请输入手机号' };
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', '请输入密码');
|
||||
}
|
||||
return { success: false, error: '请输入密码' };
|
||||
}
|
||||
|
||||
if (!window.electronAPI) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', 'Electron API不可用');
|
||||
}
|
||||
return { success: false, error: 'Electron API不可用' };
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.addLog) {
|
||||
this.addLog('info', `正在使用手机号 ${this.phone} 登录...`);
|
||||
}
|
||||
|
||||
const result = await window.electronAPI.invoke('auth:login', {
|
||||
phone: this.phone,
|
||||
password: password
|
||||
});
|
||||
|
||||
if (result.success && result.data) {
|
||||
// 登录成功,通过 store 更新状态(业务逻辑由主进程处理)
|
||||
if (this.$store) {
|
||||
await this.$store.dispatch('auth/login', {
|
||||
phone: this.phone,
|
||||
password: password,
|
||||
deviceId: result.data.device_id || ''
|
||||
});
|
||||
|
||||
if (rememberMe) {
|
||||
this.$store.dispatch('config/setRememberMe', true);
|
||||
this.$store.dispatch('config/setPhone', this.phone);
|
||||
}
|
||||
}
|
||||
|
||||
// MQTT 连接由主进程自动处理,这里只检查状态
|
||||
if (this.checkMQTTStatus) {
|
||||
setTimeout(() => {
|
||||
this.checkMQTTStatus();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 开始获取任务状态
|
||||
if (this.startTaskStatusUpdate) {
|
||||
this.startTaskStatusUpdate();
|
||||
}
|
||||
|
||||
return { success: true, data: result.data };
|
||||
} else {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', `登录失败: ${result.error || '未知错误'}`);
|
||||
}
|
||||
return { success: false, error: result.error || '未知错误' };
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', `登录过程中发生错误: ${error.message}`);
|
||||
}
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
},
|
||||
|
||||
async tryAutoLogin() {
|
||||
try {
|
||||
if (!this.$store) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 从 store 检查是否有保存的登录信息
|
||||
const savedPhone = this.$store.state.config.phone || this.$store.state.auth.phone;
|
||||
const userLoggedOut = this.$store.state.config.userLoggedOut || this.$store.state.auth.userLoggedOut;
|
||||
|
||||
// 如果用户手动退出,不自动登录
|
||||
if (userLoggedOut) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!savedPhone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 检查 store 中是否有有效的登录信息(token 和用户信息)
|
||||
const token = getToken();
|
||||
const storeSnCode = this.$store ? this.$store.state.auth.snCode : '';
|
||||
const storeUserName = this.$store ? this.$store.state.auth.userName : '';
|
||||
|
||||
// 如果有 token 和用户信息,说明已登录(数据已通过持久化插件恢复)
|
||||
if (token && (storeSnCode || storeUserName)) {
|
||||
// 更新登录状态
|
||||
this.$store.commit('auth/SET_LOGGED_IN', true);
|
||||
this.$store.commit('auth/SET_LOGIN_BUTTON_TEXT', '注销登录');
|
||||
|
||||
if (this.addLog) {
|
||||
this.addLog('info', '自动登录成功');
|
||||
}
|
||||
|
||||
// 连接MQTT
|
||||
// MQTT 连接由主进程自动处理,这里只检查状态
|
||||
if (this.checkMQTTStatus) {
|
||||
setTimeout(() => {
|
||||
this.checkMQTTStatus();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
return true; // 自动登录成功
|
||||
}
|
||||
|
||||
return false; // 未登录
|
||||
} catch (error) {
|
||||
console.error('自动登录失败:', error);
|
||||
if (this.addLog) {
|
||||
this.addLog('error', `自动登录失败: ${error.message}`);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// 注销登录(只调用主进程接口,业务逻辑由主进程处理)
|
||||
async logoutDevice() {
|
||||
if (!window.electronAPI) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', 'Electron API不可用');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.addLog) {
|
||||
this.addLog('info', '正在注销登录...');
|
||||
}
|
||||
|
||||
await window.electronAPI.invoke('auth:logout');
|
||||
|
||||
// 停止任务状态更新
|
||||
if (this.stopTaskStatusUpdate) {
|
||||
this.stopTaskStatusUpdate();
|
||||
}
|
||||
|
||||
// 更新 store 状态
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('auth/logout');
|
||||
this.$store.dispatch('config/setUserLoggedOut', true);
|
||||
}
|
||||
|
||||
if (this.addLog) {
|
||||
this.addLog('success', '注销登录成功');
|
||||
}
|
||||
|
||||
// 触发跳转到登录页面
|
||||
if (this.$emit) {
|
||||
this.$emit('logout-success');
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', `注销登录异常: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
snCode(newVal) {
|
||||
this.userMenuInfo.snCode = newVal;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
178
app/mixins/eventListenerMixin.js
Normal file
178
app/mixins/eventListenerMixin.js
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* 事件监听器管理 Mixin
|
||||
*/
|
||||
import platformMixin from './platformMixin.js';
|
||||
|
||||
export default {
|
||||
mixins: [platformMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
// 存储已注册的事件监听器,用于清理
|
||||
_registeredEventListeners: []
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
setupEventListeners() {
|
||||
console.log('[事件监听] setupEventListeners 开始执行');
|
||||
|
||||
if (!window.electronAPI || !window.electronEvents) {
|
||||
console.error('[事件监听] Electron API不可用', {
|
||||
hasElectronAPI: !!window.electronAPI,
|
||||
hasElectronEvents: !!window.electronEvents
|
||||
});
|
||||
if (this.addLog) {
|
||||
this.addLog('error', 'Electron API不可用');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const electronEvents = window.electronEvents;
|
||||
console.log('[事件监听] electronEvents 对象:', electronEvents);
|
||||
|
||||
// 定义需要注册的事件监听器
|
||||
const listeners = [
|
||||
// MQTT 事件
|
||||
{ channel: 'mqtt:connected', handler: (data) => {
|
||||
console.log('[事件监听] 收到 mqtt:connected 事件,数据:', data);
|
||||
this.onMQTTConnected(data);
|
||||
}},
|
||||
{ channel: 'mqtt:disconnected', handler: (data) => {
|
||||
console.log('[事件监听] 收到 mqtt:disconnected 事件,数据:', data);
|
||||
this.onMQTTDisconnected(data);
|
||||
}},
|
||||
{ channel: 'mqtt:message', handler: (data) => {
|
||||
console.log('[事件监听] 收到 mqtt:message 事件');
|
||||
this.onMQTTMessage(data);
|
||||
}},
|
||||
{ channel: 'mqtt:status', handler: (data) => {
|
||||
console.log('[事件监听] 收到 mqtt:status 事件,数据:', data);
|
||||
this.onMQTTStatusChange(data);
|
||||
}},
|
||||
|
||||
// 指令执行结果
|
||||
{ channel: 'command:result', handler: (data) => {
|
||||
if (this.addLog) {
|
||||
if (data.success) {
|
||||
this.addLog('success', `指令执行成功 : ${JSON.stringify(data.data).length}字符`);
|
||||
} else {
|
||||
this.addLog('error', `指令执行失败: ${data.error}`);
|
||||
}
|
||||
}
|
||||
}},
|
||||
|
||||
// 系统信息
|
||||
{ channel: 'system:info', handler: (data) => this.updateSystemInfo(data) },
|
||||
{ channel: 'log:message', handler: (data) => {
|
||||
console.log('[事件监听] 收到 log:message 事件,数据:', data);
|
||||
if (this.addLog) {
|
||||
this.addLog(data.level, data.message);
|
||||
}
|
||||
}},
|
||||
|
||||
// 通知
|
||||
{ channel: 'notification', handler: (data) => {
|
||||
if (this.showNotification) {
|
||||
this.showNotification(data.title, data.body);
|
||||
}
|
||||
}},
|
||||
|
||||
// 更新相关
|
||||
{ channel: 'update:available', handler: (updateInfo) => this.onUpdateAvailable(updateInfo) },
|
||||
{ channel: 'update:progress', handler: (progressData) => this.onUpdateProgress(progressData) },
|
||||
{ channel: 'update:downloaded', handler: (data) => this.onUpdateDownloaded(data) },
|
||||
{ channel: 'update:error', handler: (errorData) => this.onUpdateError(errorData) },
|
||||
|
||||
// 设备工作状态
|
||||
{
|
||||
channel: 'device:work-status',
|
||||
handler: (workStatus) => {
|
||||
this.onDeviceWorkStatus(workStatus);
|
||||
}
|
||||
},
|
||||
|
||||
// 平台登录状态
|
||||
{ channel: 'platform:login-status-updated', handler: (data) => {
|
||||
// 优先使用组件中定义的方法
|
||||
if (this.onPlatformLoginStatusUpdated) {
|
||||
this.onPlatformLoginStatusUpdated(data);
|
||||
} else {
|
||||
console.warn('[事件监听] 无法更新平台登录状态:组件未定义 onPlatformLoginStatusUpdated 且 store 不可用');
|
||||
}
|
||||
}}
|
||||
];
|
||||
|
||||
// 注册所有事件监听器
|
||||
listeners.forEach(({ channel, handler }) => {
|
||||
electronEvents.on(channel, handler);
|
||||
// 保存到清理列表
|
||||
this._registeredEventListeners.push({ channel, handler });
|
||||
});
|
||||
|
||||
console.log('[事件监听] 所有事件监听器已设置完成');
|
||||
// 优先使用 store 的 dispatch 方法添加日志
|
||||
if (this.$store && this.$store.dispatch) {
|
||||
this.$store.dispatch('log/addLog', { level: 'info', message: '事件监听器设置完成' });
|
||||
console.log('[事件监听] 已通过 store.dispatch 添加日志');
|
||||
} else if (this.addLog) {
|
||||
this.addLog('info', '事件监听器设置完成');
|
||||
console.log('[事件监听] 已通过 addLog 方法添加日志');
|
||||
} else {
|
||||
console.log('[事件监听] 日志系统暂不可用,将在组件完全初始化后记录');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 清理所有已注册的事件监听器
|
||||
*/
|
||||
cleanupEventListeners() {
|
||||
console.log('[事件监听] 开始清理事件监听器');
|
||||
|
||||
if (!window.electronEvents || !this._registeredEventListeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 移除所有已注册的监听器
|
||||
this._registeredEventListeners.forEach(({ channel, handler }) => {
|
||||
try {
|
||||
window.electronEvents.off(channel, handler);
|
||||
} catch (error) {
|
||||
console.warn(`[事件监听] 移除监听器失败: ${channel}`, error);
|
||||
}
|
||||
});
|
||||
|
||||
// 清空列表
|
||||
this._registeredEventListeners = [];
|
||||
console.log('[事件监听] 事件监听器清理完成');
|
||||
},
|
||||
|
||||
showNotification(title, body) {
|
||||
if ('Notification' in window) {
|
||||
if (Notification.permission === 'granted') {
|
||||
new Notification(title, {
|
||||
body: body,
|
||||
icon: '/assets/icon.png'
|
||||
});
|
||||
} else if (Notification.permission !== 'denied') {
|
||||
Notification.requestPermission().then(permission => {
|
||||
if (permission === 'granted') {
|
||||
new Notification(title, {
|
||||
body: body,
|
||||
icon: '/assets/icon.png'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.addLog) {
|
||||
this.addLog('info', `[通知] ${title}: ${body}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
48
app/mixins/logMixin.js
Normal file
48
app/mixins/logMixin.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 日志管理 Mixin
|
||||
* 直接使用 store 中的 log 状态
|
||||
*/
|
||||
export default {
|
||||
methods: {
|
||||
/**
|
||||
* 添加日志
|
||||
* @param {string} level - 日志级别: 'info', 'success', 'warn', 'error'
|
||||
* @param {string} message - 日志消息
|
||||
*/
|
||||
addLog(level, message) {
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('log/addLog', { level, message });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 清空日志
|
||||
*/
|
||||
clearLogs() {
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('log/clearLogs');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 导出日志
|
||||
*/
|
||||
exportLogs() {
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('log/exportLogs');
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* 获取日志条目(从 store 获取)
|
||||
*/
|
||||
logEntries() {
|
||||
if (this.$store) {
|
||||
return this.$store.getters['log/logEntries'] || [];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
97
app/mixins/mqttMixin.js
Normal file
97
app/mixins/mqttMixin.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* MQTT 管理 Mixin
|
||||
*/
|
||||
export default {
|
||||
computed: {
|
||||
isConnected() {
|
||||
return this.$store ? this.$store.state.mqtt.isConnected : false;
|
||||
},
|
||||
mqttStatus() {
|
||||
return this.$store ? this.$store.state.mqtt.mqttStatus : '未连接';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// MQTT 连接已由主进程自动处理,客户端不再需要手动连接
|
||||
// 只保留状态查询方法
|
||||
async checkMQTTStatus() {
|
||||
try {
|
||||
|
||||
if (!window.electronAPI) {
|
||||
return;
|
||||
}
|
||||
|
||||
const statusResult = await window.electronAPI.invoke('mqtt:status');
|
||||
if (statusResult && typeof statusResult.isConnected !== 'undefined') {
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('mqtt/setConnected', statusResult.isConnected);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('查询MQTT状态失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
async disconnectMQTT() {
|
||||
try {
|
||||
if (!window.electronAPI) {
|
||||
return;
|
||||
}
|
||||
|
||||
await window.electronAPI.invoke('mqtt:disconnect');
|
||||
if (this.addLog) {
|
||||
this.addLog('info', '服务断开连接指令已发送');
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', `断开服务连接异常: ${error.message}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onMQTTConnected(data) {
|
||||
console.log('[MQTT] onMQTTConnected 被调用,数据:', data);
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('mqtt/setConnected', true);
|
||||
console.log('[MQTT] 状态已更新为已连接');
|
||||
}
|
||||
if (this.addLog) {
|
||||
this.addLog('success', 'MQTT 服务已连接');
|
||||
}
|
||||
},
|
||||
|
||||
onMQTTDisconnected(data) {
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('mqtt/setConnected', false);
|
||||
}
|
||||
if (this.addLog) {
|
||||
this.addLog('warn', `服务连接断开: ${data.reason || '未知原因'}`);
|
||||
}
|
||||
},
|
||||
|
||||
onMQTTMessage(data) {
|
||||
const action = data.payload?.action || 'unknown';
|
||||
if (this.addLog) {
|
||||
this.addLog('info', `收到远程指令: ${action}`);
|
||||
}
|
||||
},
|
||||
|
||||
onMQTTStatusChange(data) {
|
||||
console.log('[MQTT] onMQTTStatusChange 被调用,数据:', data);
|
||||
// 根据状态数据更新连接状态
|
||||
if (data && typeof data.isConnected !== 'undefined') {
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('mqtt/setConnected', data.isConnected);
|
||||
console.log('[MQTT] 通过 isConnected 更新状态:', data.isConnected);
|
||||
}
|
||||
} else if (data && data.status) {
|
||||
// 如果数据中有 status 字段,根据状态字符串判断
|
||||
const isConnected = data.status === 'connected' || data.status === '已连接';
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('mqtt/setConnected', isConnected);
|
||||
console.log('[MQTT] 通过 status 更新状态:', isConnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
65
app/mixins/platformMixin.js
Normal file
65
app/mixins/platformMixin.js
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 平台信息管理 Mixin
|
||||
*/
|
||||
export default {
|
||||
computed: {
|
||||
currentPlatform() {
|
||||
return this.$store ? this.$store.state.platform.currentPlatform : '-';
|
||||
},
|
||||
platformLoginStatus() {
|
||||
return this.$store ? this.$store.state.platform.platformLoginStatus : '-';
|
||||
},
|
||||
platformLoginStatusColor() {
|
||||
return this.$store ? this.$store.state.platform.platformLoginStatusColor : '#FF9800';
|
||||
},
|
||||
isPlatformLoggedIn() {
|
||||
return this.$store ? this.$store.state.platform.isPlatformLoggedIn : false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 接收主进程推送的平台登录状态(不做处理,直接更新 store)
|
||||
onPlatformLoginStatusUpdated(data) {
|
||||
console.log('[PlatformMixin] 收到平台登录状态更新:', data);
|
||||
if (this.$store && data) {
|
||||
// 直接使用主进程提供的格式化数据
|
||||
if (data.platform !== undefined) {
|
||||
this.$store.dispatch('platform/updatePlatform', data.platform);
|
||||
}
|
||||
// 修复:确保正确处理 isLoggedIn 状态
|
||||
const isLoggedIn = data.isLoggedIn !== undefined ? data.isLoggedIn : false;
|
||||
this.$store.dispatch('platform/updatePlatformLoginStatus', {
|
||||
status: data.status || (isLoggedIn ? '已登录' : '未登录'),
|
||||
color: data.color || (isLoggedIn ? '#4CAF50' : '#FF9800'),
|
||||
isLoggedIn: isLoggedIn
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 主动检查平台登录状态
|
||||
* 调用主进程接口检查状态,主进程会自动更新并通知前端
|
||||
*/
|
||||
async checkPlatformLoginStatus() {
|
||||
if (!window.electronAPI || !window.electronAPI.invoke) {
|
||||
console.warn('[PlatformMixin] electronAPI 不可用,无法检查平台登录状态');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await window.electronAPI.invoke('auth:platform-login-status');
|
||||
if (result && result.success) {
|
||||
// 主进程已经通过 platform:login-status-updated 事件通知前端了
|
||||
// 这里不需要再次更新 store,因为事件处理已经更新了
|
||||
console.log('[PlatformMixin] 平台登录状态检查完成:', {
|
||||
platform: result.platformType,
|
||||
isLoggedIn: result.isLoggedIn
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[PlatformMixin] 检查平台登录状态失败:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
220
app/mixins/qrCodeMixin.js
Normal file
220
app/mixins/qrCodeMixin.js
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* 二维码管理 Mixin
|
||||
* 二维码刷新逻辑在渲染层处理,主进程只负责执行获取二维码的操作
|
||||
*/
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
qrCodeAutoRefreshInterval: null, // 二维码自动刷新定时器
|
||||
qrCodeCountdownInterval: null, // 倒计时定时器
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
qrCodeUrl() {
|
||||
return this.$store ? this.$store.state.qrCode.qrCodeUrl : null;
|
||||
},
|
||||
qrCodeCountdown() {
|
||||
return this.$store ? this.$store.state.qrCode.qrCodeCountdown : 0;
|
||||
},
|
||||
qrCodeCountdownActive() {
|
||||
return this.$store ? this.$store.state.qrCode.qrCodeCountdownActive : false;
|
||||
},
|
||||
qrCodeExpired() {
|
||||
return this.$store ? this.$store.state.qrCode.qrCodeExpired : false;
|
||||
},
|
||||
qrCodeRefreshCount() {
|
||||
return this.$store ? this.$store.state.qrCode.qrCodeRefreshCount : 0;
|
||||
},
|
||||
isPlatformLoggedIn() {
|
||||
return this.$store ? this.$store.state.platform.isPlatformLoggedIn : false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 获取二维码
|
||||
*/
|
||||
async getQrCode() {
|
||||
try {
|
||||
if (!window.electronAPI || !window.electronAPI.invoke) {
|
||||
console.error('[二维码] electronAPI 不可用');
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await window.electronAPI.invoke('command:execute', {
|
||||
platform: 'boss',
|
||||
action: 'get_login_qr_code',
|
||||
data: { type: 'app' },
|
||||
source: 'renderer'
|
||||
});
|
||||
|
||||
if (result.success && result.data) {
|
||||
const qrCodeUrl = result.data.qrCodeUrl || result.data.qr_code_url || result.data.oos_url || result.data.imageData;
|
||||
const qrCodeOosUrl = result.data.qrCodeOosUrl || result.data.oos_url || null;
|
||||
|
||||
if (qrCodeUrl && this.$store) {
|
||||
this.$store.dispatch('qrCode/setQrCode', { url: qrCodeUrl, oosUrl: qrCodeOosUrl });
|
||||
if (this.addLog) {
|
||||
this.addLog('success', '二维码已获取');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
console.error('[二维码] 获取失败:', result.error || '未知错误');
|
||||
if (this.addLog) {
|
||||
this.addLog('error', `获取二维码失败: ${result.error || '未知错误'}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[二维码] 获取失败:', error);
|
||||
if (this.addLog) {
|
||||
this.addLog('error', `获取二维码失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* 启动二维码自动刷新
|
||||
*/
|
||||
startQrCodeAutoRefresh() {
|
||||
// 如果定时器已存在,不重复启动
|
||||
if (this.qrCodeAutoRefreshInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果平台已登录,不启动自动刷新
|
||||
if (this.isPlatformLoggedIn) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QR_CODE_REFRESH_INTERVAL = 25000; // 25秒
|
||||
const MAX_REFRESH_COUNT = 3; // 最大刷新次数
|
||||
|
||||
// 重置刷新次数
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('qrCode/setQrCodeCountdown', {
|
||||
countdown: 25,
|
||||
isActive: true,
|
||||
refreshCount: 0,
|
||||
isExpired: false
|
||||
});
|
||||
}
|
||||
|
||||
// 立即获取一次二维码
|
||||
this.getQrCode();
|
||||
|
||||
// 启动定时器,每25秒刷新一次
|
||||
this.qrCodeAutoRefreshInterval = setInterval(async () => {
|
||||
// 检查平台登录状态
|
||||
if (this.isPlatformLoggedIn) {
|
||||
this.stopQrCodeAutoRefresh();
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查刷新次数
|
||||
const refreshCount = this.qrCodeRefreshCount;
|
||||
if (refreshCount >= MAX_REFRESH_COUNT) {
|
||||
// 已达到最大次数
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('qrCode/setQrCodeCountdown', {
|
||||
countdown: 0,
|
||||
isActive: false,
|
||||
refreshCount: refreshCount,
|
||||
isExpired: true
|
||||
});
|
||||
}
|
||||
this.stopQrCodeAutoRefresh();
|
||||
return;
|
||||
}
|
||||
|
||||
// 刷新二维码
|
||||
const success = await this.getQrCode();
|
||||
if (success) {
|
||||
// 更新刷新次数
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('qrCode/setQrCodeCountdown', {
|
||||
countdown: 25,
|
||||
isActive: true,
|
||||
refreshCount: refreshCount + 1,
|
||||
isExpired: false
|
||||
});
|
||||
}
|
||||
}
|
||||
}, QR_CODE_REFRESH_INTERVAL);
|
||||
|
||||
// 启动倒计时
|
||||
this.startQrCodeCountdown();
|
||||
},
|
||||
|
||||
/**
|
||||
* 停止二维码自动刷新
|
||||
*/
|
||||
stopQrCodeAutoRefresh() {
|
||||
if (this.qrCodeAutoRefreshInterval) {
|
||||
clearInterval(this.qrCodeAutoRefreshInterval);
|
||||
this.qrCodeAutoRefreshInterval = null;
|
||||
}
|
||||
this.stopQrCodeCountdown();
|
||||
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('qrCode/setQrCodeCountdown', {
|
||||
countdown: 0,
|
||||
isActive: false,
|
||||
refreshCount: this.qrCodeRefreshCount,
|
||||
isExpired: this.qrCodeExpired
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 启动二维码倒计时
|
||||
*/
|
||||
startQrCodeCountdown() {
|
||||
// 如果倒计时定时器已存在,不重复启动
|
||||
if (this.qrCodeCountdownInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 每秒更新一次倒计时
|
||||
this.qrCodeCountdownInterval = setInterval(() => {
|
||||
if (this.qrCodeCountdownActive && this.qrCodeCountdown > 0) {
|
||||
const newCountdown = this.qrCodeCountdown - 1;
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('qrCode/setQrCodeCountdown', {
|
||||
countdown: newCountdown,
|
||||
isActive: true,
|
||||
refreshCount: this.qrCodeRefreshCount,
|
||||
isExpired: false
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 倒计时结束
|
||||
this.stopQrCodeCountdown();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
/**
|
||||
* 停止二维码倒计时
|
||||
*/
|
||||
stopQrCodeCountdown() {
|
||||
if (this.qrCodeCountdownInterval) {
|
||||
clearInterval(this.qrCodeCountdownInterval);
|
||||
this.qrCodeCountdownInterval = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 监听平台登录状态,如果已登录则停止刷新
|
||||
isPlatformLoggedIn(newVal) {
|
||||
if (newVal) {
|
||||
this.stopQrCodeAutoRefresh();
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
// 组件销毁时清理定时器
|
||||
this.stopQrCodeAutoRefresh();
|
||||
}
|
||||
};
|
||||
|
||||
48
app/mixins/systemInfoMixin.js
Normal file
48
app/mixins/systemInfoMixin.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 系统信息管理 Mixin
|
||||
*/
|
||||
export default {
|
||||
computed: {
|
||||
uptime() {
|
||||
return this.$store ? this.$store.state.system.uptime : '0分钟';
|
||||
},
|
||||
cpuUsage() {
|
||||
return this.$store ? this.$store.state.system.cpuUsage : '0%';
|
||||
},
|
||||
memUsage() {
|
||||
return this.$store ? this.$store.state.system.memUsage : '0MB';
|
||||
},
|
||||
deviceId() {
|
||||
return this.$store ? this.$store.state.system.deviceId : '-';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 更新运行时间(仅计算,不请求)
|
||||
startSystemInfoUpdate() {
|
||||
const updateUptime = () => {
|
||||
if (this.$store && this.startTime) {
|
||||
const elapsed = Math.floor((Date.now() - this.startTime) / 1000 / 60);
|
||||
this.$store.dispatch('system/updateUptime', `${elapsed}分钟`);
|
||||
}
|
||||
};
|
||||
updateUptime();
|
||||
setInterval(updateUptime, 5000);
|
||||
},
|
||||
|
||||
// 接收主进程推送的系统信息(不做处理,直接更新 store)
|
||||
updateSystemInfo(data) {
|
||||
if (this.$store && data) {
|
||||
if (data.cpu !== undefined) {
|
||||
this.$store.dispatch('system/updateCpuUsage', data.cpu);
|
||||
}
|
||||
if (data.memory !== undefined) {
|
||||
this.$store.dispatch('system/updateMemUsage', data.memory);
|
||||
}
|
||||
if (data.deviceId !== undefined) {
|
||||
this.$store.dispatch('system/updateDeviceId', data.deviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
61
app/mixins/taskMixin.js
Normal file
61
app/mixins/taskMixin.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 设备工作状态管理 Mixin
|
||||
*/
|
||||
export default {
|
||||
computed: {
|
||||
displayText() {
|
||||
return this.$store ? this.$store.state.task.displayText : null;
|
||||
},
|
||||
nextExecuteTimeText() {
|
||||
return this.$store ? this.$store.state.task.nextExecuteTimeText : null;
|
||||
},
|
||||
currentActivity() {
|
||||
return this.$store ? this.$store.state.task.currentActivity : null;
|
||||
},
|
||||
pendingQueue() {
|
||||
return this.$store ? this.$store.state.task.pendingQueue : null;
|
||||
},
|
||||
deviceStatus() {
|
||||
return this.$store ? this.$store.state.task.deviceStatus : null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
startTaskStatusUpdate() {
|
||||
// 设备工作状态已通过 MQTT 实时推送,不再使用接口轮询
|
||||
// 设备工作状态通过 device_work_status_${snCode} 主题推送,由 mqttService 处理并发送到渲染进程
|
||||
// 通过 onDeviceWorkStatus 方法接收并更新状态
|
||||
console.log('[TaskMixin] 设备工作状态更新已启动,使用 MQTT 实时推送');
|
||||
},
|
||||
|
||||
stopTaskStatusUpdate() {
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('task/clearDeviceWorkStatus');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理设备工作状态通知
|
||||
* 服务端已格式化好显示文本,客户端直接使用,不做复杂处理
|
||||
*/
|
||||
onDeviceWorkStatus(workStatus) {
|
||||
if (!workStatus || !this.$store) {
|
||||
console.warn('[Renderer] 收到设备工作状态但数据无效:', workStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 直接更新设备工作状态到 store(服务端已处理好所有显示逻辑)
|
||||
try {
|
||||
this.$store.dispatch('task/updateDeviceWorkStatus', workStatus);
|
||||
|
||||
// 验证更新是否成功
|
||||
this.$nextTick(() => {
|
||||
const state = this.$store.state.task;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Renderer] 更新设备工作状态失败:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
138
app/mixins/updateMixin.js
Normal file
138
app/mixins/updateMixin.js
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 更新管理 Mixin
|
||||
*/
|
||||
export default {
|
||||
computed: {
|
||||
updateDialogVisible() {
|
||||
return this.$store ? this.$store.state.update.updateDialogVisible : false;
|
||||
},
|
||||
updateInfo() {
|
||||
return this.$store ? this.$store.state.update.updateInfo : null;
|
||||
},
|
||||
updateProgress() {
|
||||
return this.$store ? this.$store.state.update.updateProgress : 0;
|
||||
},
|
||||
isDownloading() {
|
||||
return this.$store ? this.$store.state.update.isDownloading : false;
|
||||
},
|
||||
downloadState() {
|
||||
return this.$store ? this.$store.state.update.downloadState : {
|
||||
progress: 0,
|
||||
downloadedBytes: 0,
|
||||
totalBytes: 0
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 接收主进程推送的更新信息(不做处理,直接更新 store)
|
||||
onUpdateAvailable(updateInfo) {
|
||||
if (!updateInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('update/setUpdateInfo', updateInfo);
|
||||
this.$store.dispatch('update/showUpdateDialog');
|
||||
}
|
||||
|
||||
if (this.addLog) {
|
||||
this.addLog('info', `发现新版本: ${updateInfo.version || '未知'}`);
|
||||
}
|
||||
},
|
||||
|
||||
// 接收主进程推送的更新进度(不做处理,直接更新 store)
|
||||
onUpdateProgress(progressData) {
|
||||
if (this.$store && progressData) {
|
||||
this.$store.dispatch('update/setDownloadState', {
|
||||
progress: progressData.progress || 0,
|
||||
downloadedBytes: progressData.downloadedBytes || 0,
|
||||
totalBytes: progressData.totalBytes || 0
|
||||
});
|
||||
this.$store.dispatch('update/setUpdateProgress', progressData.progress || 0);
|
||||
this.$store.dispatch('update/setDownloading', true);
|
||||
}
|
||||
},
|
||||
|
||||
// 接收主进程推送的下载完成通知(不做处理,直接更新 store)
|
||||
onUpdateDownloaded(data) {
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('update/setDownloading', false);
|
||||
this.$store.dispatch('update/setUpdateProgress', 100);
|
||||
}
|
||||
if (this.addLog) {
|
||||
this.addLog('success', '更新包下载完成');
|
||||
}
|
||||
if (this.showNotification) {
|
||||
this.showNotification('更新下载完成', '更新包已下载完成,是否立即安装?');
|
||||
}
|
||||
},
|
||||
|
||||
// 接收主进程推送的更新错误(不做处理,直接显示)
|
||||
onUpdateError(errorData) {
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('update/setDownloading', false);
|
||||
}
|
||||
const errorMsg = errorData?.error || '更新失败';
|
||||
if (this.addLog) {
|
||||
this.addLog('error', `更新错误: ${errorMsg}`);
|
||||
}
|
||||
if (this.showNotification) {
|
||||
this.showNotification('更新失败', errorMsg);
|
||||
}
|
||||
},
|
||||
|
||||
closeUpdateDialog() {
|
||||
if (this.$store) {
|
||||
this.$store.dispatch('update/hideUpdateDialog');
|
||||
}
|
||||
},
|
||||
|
||||
// 下载更新(调用主进程接口,不做业务逻辑处理)
|
||||
async startDownload() {
|
||||
const updateInfoData = this.updateInfo;
|
||||
if (!updateInfoData || !updateInfoData.downloadUrl) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', '更新信息不存在');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window.electronAPI) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', 'Electron API不可用');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await window.electronAPI.invoke('update:download', updateInfoData.downloadUrl);
|
||||
} catch (error) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', `下载更新失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 安装更新(调用主进程接口,不做业务逻辑处理)
|
||||
async installUpdate() {
|
||||
if (!window.electronAPI) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', 'Electron API不可用');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await window.electronAPI.invoke('update:install');
|
||||
setTimeout(() => {
|
||||
this.closeUpdateDialog();
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
if (this.addLog) {
|
||||
this.addLog('error', `安装更新失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user