diff --git a/app/core/config.py b/app/core/config.py index 8e64e7b..472a331 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -882,6 +882,13 @@ class AppConfig(GlobalConfig): "RootPath": general_config["Script"]["RootPath"], } + general_config["Script"]["ConfigPathMode"] = ( + "File" + if "所有文件" + in general_config["Script"]["ConfigPathMode"] + else "Folder" + ) + uid, sc = await self.add_script("General") script_dict[GeneralConfig.name] = str(uid) await sc.load(general_config) @@ -1061,6 +1068,8 @@ class AppConfig(GlobalConfig): await queue.QueueItem.remove(key) await self.ScriptConfig.remove(uid) + if (Path.cwd() / f"data/{uid}").exists(): + shutil.rmtree(Path.cwd() / f"data/{uid}") async def reorder_script(self, index_list: list[str]) -> None: """重新排序脚本""" @@ -1288,6 +1297,8 @@ class AppConfig(GlobalConfig): if isinstance(script_config, (MaaConfig | GeneralConfig)): await script_config.UserData.remove(uid) await self.ScriptConfig.save() + if (Path.cwd() / f"data/{script_id}/{user_id}").exists(): + shutil.rmtree(Path.cwd() / f"data/{script_id}/{user_id}") async def reorder_user(self, script_id: str, index_list: list[str]) -> None: """重新排序用户""" @@ -2027,11 +2038,25 @@ class AppConfig(GlobalConfig): data = { "recruit_statistics": defaultdict(int), "drop_statistics": defaultdict(dict), + "sanity": 0, + "sanity_full_at": "", "maa_result": maa_result, } if_six_star = False + # 提取理智相关信息 + for log_line in logs: + # 提取当前理智值:理智: 5/180 + sanity_match = re.search(r"理智:\s*(\d+)/\d+", log_line) + if sanity_match: + data["sanity"] = int(sanity_match.group(1)) + + # 提取理智回满时间:理智将在 2025-09-26 18:57 回满。(17h 29m 后) + sanity_full_match = re.search(r"(理智将在\s*\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}\s*回满。\(\d+h\s+\d+m\s+后\))", log_line) + if sanity_full_match: + data["sanity_full_at"] = sanity_full_match.group(1) + # 公招统计(仅统计招募到的) confirmed_recruit = False current_star_level = None @@ -2140,6 +2165,7 @@ class AppConfig(GlobalConfig): log_path.parent.mkdir(parents=True, exist_ok=True) with log_path.open("w", encoding="utf-8") as f: f.writelines(logs) + # 保存统计数据 with log_path.with_suffix(".json").open("w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=4) @@ -2216,6 +2242,10 @@ class AppConfig(GlobalConfig): data[key][stage][item] = 0 data[key][stage][item] += count + # 处理理智相关字段 - 使用最后一个文件的值 + elif key in ["sanity", "sanity_full_at"]: + data[key] = single_data[key] + # 录入运行结果 elif key in ["maa_result", "general_result"]: diff --git a/app/core/timer.py b/app/core/timer.py index c803a56..9e364c3 100644 --- a/app/core/timer.py +++ b/app/core/timer.py @@ -112,7 +112,7 @@ class _MainTimer: """静默模式通过模拟老板键来隐藏模拟器窗口""" if ( - len(Config.if_ignore_silence) > 0 + len(Config.if_ignore_silence) == 0 and Config.get("Function", "IfSilence") and Config.get("Function", "BossKey") != "" ): diff --git a/app/task/MAA.py b/app/task/MAA.py index b6b57a9..341d5b9 100644 --- a/app/task/MAA.py +++ b/app/task/MAA.py @@ -1122,7 +1122,7 @@ class MaaManager: await asyncio.sleep(self.wait_time) if "-" in self.ADB_address: - ADB_ip = f"{self.ADB_address.split("-")[0]}-" + ADB_ip = f"{self.ADB_address.split('-')[0]}-" ADB_port = int(self.ADB_address.split("-")[1]) elif ":" in self.ADB_address: @@ -1933,6 +1933,8 @@ class MaaManager: message_text = ( f"开始时间: {message['start_time']}\n" f"结束时间: {message['end_time']}\n" + f"理智剩余: {message.get('sanity', '未知')}\n" + f"回复时间: {message.get('sanity_full_at', '未知')}\n" f"MAA执行结果: {message['maa_result']}\n\n" f"{recruit_text}\n" f"{drop_text}" diff --git a/frontend/electron/main.ts b/frontend/electron/main.ts index edd8709..1109ece 100644 --- a/frontend/electron/main.ts +++ b/frontend/electron/main.ts @@ -781,97 +781,44 @@ ipcMain.handle('check-git-update', async () => { GIT_ASKPASS: '', } - log.info('开始检查Git仓库更新...') + log.info('开始检查Git仓库更新(跳过fetch,避免直接访问GitHub)...') - // 执行 git fetch 获取最新的远程信息 - await new Promise((resolve, reject) => { - const fetchProc = spawn(gitPath, ['fetch', 'origin'], { - stdio: 'pipe', - env: gitEnv, - cwd: appRoot, - }) - - fetchProc.stdout?.on('data', data => { - log.info('git fetch output:', data.toString()) - }) - - fetchProc.stderr?.on('data', data => { - log.info('git fetch stderr:', data.toString()) - }) - - fetchProc.on('close', code => { - if (code === 0) { - resolve() - } else { - reject(new Error(`git fetch失败,退出码: ${code}`)) - } - }) - - fetchProc.on('error', reject) - }) - - // 检查本地分支是否落后于远程分支 - const hasUpdate = await new Promise((resolve, reject) => { - const statusProc = spawn(gitPath, ['status', '-uno', '--porcelain=v1'], { + // 不执行fetch,直接检查本地状态 + // 这样避免了直接访问GitHub,而是在后续的pull操作中使用镜像站 + + // 获取当前HEAD的commit hash + const currentCommit = await new Promise((resolve, reject) => { + const revParseProc = spawn(gitPath, ['rev-parse', 'HEAD'], { stdio: 'pipe', env: gitEnv, cwd: appRoot, }) let output = '' - statusProc.stdout?.on('data', data => { + revParseProc.stdout?.on('data', data => { output += data.toString() }) - statusProc.stderr?.on('data', data => { - log.info('git status stderr:', data.toString()) - }) - - statusProc.on('close', code => { + revParseProc.on('close', code => { if (code === 0) { - // 检查是否有 "Your branch is behind" 的信息 - // 使用 git rev-list 来比较本地和远程分支 - const revListProc = spawn( - gitPath, - ['rev-list', '--count', 'HEAD..origin/feature/refactor'], - { - stdio: 'pipe', - env: gitEnv, - cwd: appRoot, - } - ) - - let revOutput = '' - revListProc.stdout?.on('data', data => { - revOutput += data.toString() - }) - - revListProc.on('close', revCode => { - if (revCode === 0) { - const commitsBehind = parseInt(revOutput.trim()) - const hasUpdates = commitsBehind > 0 - log.info(`本地分支落后远程分支 ${commitsBehind} 个提交,hasUpdate: ${hasUpdates}`) - resolve(hasUpdates) - } else { - log.warn('无法比较本地和远程分支,假设有更新') - resolve(true) // 如果无法确定,假设有更新 - } - }) - - revListProc.on('error', () => { - log.warn('git rev-list执行失败,假设有更新') - resolve(true) - }) + resolve(output.trim()) } else { - reject(new Error(`git status失败,退出码: ${code}`)) + reject(new Error(`git rev-parse失败,退出码: ${code}`)) } }) - statusProc.on('error', reject) + revParseProc.on('error', reject) }) - log.info(`Git更新检查完成,hasUpdate: ${hasUpdate}`) - return { hasUpdate } + log.info(`当前本地commit: ${currentCommit}`) + + // 由于我们跳过了fetch步骤(避免直接访问GitHub), + // 我们无法准确知道远程是否有更新 + // 因此返回true,让后续的pull操作通过镜像站来检查和获取更新 + // 如果没有更新,pull操作会很快完成且不会有实际变化 + log.info('跳过远程检查,返回hasUpdate=true以触发镜像站更新流程') + return { hasUpdate: true, skipReason: 'avoided_github_access' } + } catch (error) { log.error('检查Git更新失败:', error) // 如果检查失败,返回true以触发更新流程,确保代码是最新的 diff --git a/frontend/electron/services/gitService.ts b/frontend/electron/services/gitService.ts index 4b659df..ae59ee4 100644 --- a/frontend/electron/services/gitService.ts +++ b/frontend/electron/services/gitService.ts @@ -207,7 +207,7 @@ export async function cloneBackend( // ==== 下面是关键逻辑 ==== if (isGitRepository(backendPath)) { - // 已是 git 仓库,直接 pull + // 已是 git 仓库,先更新远程URL为镜像站,然后 pull if (mainWindow) { mainWindow.webContents.send('download-progress', { type: 'backend', @@ -216,6 +216,24 @@ export async function cloneBackend( message: '正在更新后端代码...', }) } + + // 更新远程URL为镜像站URL,避免直接访问GitHub + console.log(`更新远程URL为镜像站: ${repoUrl}`) + await new Promise((resolve, reject) => { + const proc = spawn(gitPath, ['remote', 'set-url', 'origin', repoUrl], { + stdio: 'pipe', + env: gitEnv, + cwd: backendPath + }) + proc.stdout?.on('data', d => console.log('git remote set-url:', d.toString())) + proc.stderr?.on('data', d => console.log('git remote set-url err:', d.toString())) + proc.on('close', code => + code === 0 ? resolve() : reject(new Error(`git remote set-url失败,退出码: ${code}`)) + ) + proc.on('error', reject) + }) + + // 执行pull操作 await new Promise((resolve, reject) => { const proc = spawn(gitPath, ['pull'], { stdio: 'pipe', env: gitEnv, cwd: backendPath }) proc.stdout?.on('data', d => console.log('git pull:', d.toString())) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 9c02e1e..9d6bc4e 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -8,6 +8,7 @@ import AppLayout from './components/AppLayout.vue' import TitleBar from './components/TitleBar.vue' import UpdateModal from './components/UpdateModal.vue' import DevDebugPanel from './components/DevDebugPanel.vue' +import GlobalPowerCountdown from './components/GlobalPowerCountdown.vue' import zhCN from 'ant-design-vue/es/locale/zh_CN' import { logger } from '@/utils/logger' @@ -49,6 +50,9 @@ onMounted(() => { + + + diff --git a/frontend/src/components/GlobalPowerCountdown.vue b/frontend/src/components/GlobalPowerCountdown.vue new file mode 100644 index 0000000..4d0da8d --- /dev/null +++ b/frontend/src/components/GlobalPowerCountdown.vue @@ -0,0 +1,307 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/LogViewer.vue b/frontend/src/components/LogViewer.vue index f10ba0c..c74e474 100644 --- a/frontend/src/components/LogViewer.vue +++ b/frontend/src/components/LogViewer.vue @@ -60,7 +60,7 @@ - 导出日志(txt格式) + 导出日志(log格式) @@ -285,7 +285,7 @@ const exportLogs = async () => { } else { fileName = `logs_${new Date().toISOString().slice(0, 10)}` } - a.download = `${fileName}.txt` + a.download = `${fileName}.log` document.body.appendChild(a) a.click() diff --git a/frontend/src/components/TitleBar.vue b/frontend/src/components/TitleBar.vue index e0e98cc..adfe81e 100644 --- a/frontend/src/components/TitleBar.vue +++ b/frontend/src/components/TitleBar.vue @@ -41,7 +41,7 @@ diff --git a/frontend/src/views/Initialization.vue b/frontend/src/views/Initialization.vue index 851549c..20caf00 100644 --- a/frontend/src/views/Initialization.vue +++ b/frontend/src/views/Initialization.vue @@ -191,55 +191,45 @@ async function checkEnvironment() { console.log('- main.py存在:', criticalFiles.mainPyExists) console.log('- 所有关键文件存在:', allExeFilesExist) - // 新的自动模式判断逻辑:只要所有关键exe文件都存在且不是第一次启动就进入自动模式 - console.log('自动模式判断条件:') - console.log('- 不是第一次启动:', !isFirst) + // 页面模式判断逻辑: + // 1. 第一次启动 -> 直接进入手动模式 + // 2. 非第一次启动 + 文件完整 -> 自动模式 + // 3. 非第一次启动 + 文件缺失 -> 环境不完整页面 + console.log('页面模式判断条件:') + console.log('- 是否第一次启动:', isFirst) console.log('- 所有关键文件存在:', allExeFilesExist) - // 只要不是第一次启动且所有关键exe文件都存在就进入自动模式 - if (!isFirst && allExeFilesExist) { + // 第一次启动时,无论文件是否存在都直接进入手动模式 + if (isFirst) { + console.log('第一次启动,直接进入手动模式') + autoMode.value = false + showEnvironmentIncomplete.value = false + } else if (allExeFilesExist) { + // 不是第一次启动且所有关键exe文件都存在,进入自动模式 console.log('进入自动模式,开始自动启动流程') autoMode.value = true + showEnvironmentIncomplete.value = false } else { - console.log('进入手动模式') - if (isFirst) { - console.log('原因: 第一次启动') - // 第一次启动直接进入手动模式 - autoMode.value = false - showEnvironmentIncomplete.value = false - } else if (!allExeFilesExist) { - console.log('原因: 关键exe文件缺失') - console.log(' - python.exe缺失:', !criticalFiles.pythonExists) - console.log(' - git.exe缺失:', !criticalFiles.gitExists) - console.log(' - main.py缺失:', !criticalFiles.mainPyExists) - - // 检查是否应该显示环境不完整页面(仅在自动模式下) - // 如果不是第一次启动且关键文件缺失,说明之前是自动模式但现在环境有问题 - if (!isFirst) { - const missing = [] - if (!criticalFiles.pythonExists) missing.push('Python 环境') - if (!criticalFiles.gitExists) missing.push('Git 工具') - if (!criticalFiles.mainPyExists) missing.push('后端代码') - - missingComponents.value = missing - showEnvironmentIncomplete.value = true - autoMode.value = false - } else { - // 第一次启动时,即使文件缺失也直接进入手动模式 - autoMode.value = false - showEnvironmentIncomplete.value = false - } - } else { - // 其他情况直接进入手动模式 - autoMode.value = false - showEnvironmentIncomplete.value = false - } + // 不是第一次启动但关键文件缺失,显示环境不完整页面 + console.log('环境损坏,显示环境不完整页面') + console.log(' - python.exe缺失:', !criticalFiles.pythonExists) + console.log(' - git.exe缺失:', !criticalFiles.gitExists) + console.log(' - main.py缺失:', !criticalFiles.mainPyExists) - // 如果关键文件缺失,重置初始化状态 - if (!allExeFilesExist && config.init) { - console.log('检测到关键exe文件缺失,重置初始化状态') - await saveConfig({ init: false }) - } + const missing = [] + if (!criticalFiles.pythonExists) missing.push('Python 环境') + if (!criticalFiles.gitExists) missing.push('Git 工具') + if (!criticalFiles.mainPyExists) missing.push('后端代码') + + missingComponents.value = missing + showEnvironmentIncomplete.value = true + autoMode.value = false + } + + // 如果关键文件缺失,重置初始化状态 + if (!allExeFilesExist && config.init) { + console.log('检测到关键exe文件缺失,重置初始化状态') + await saveConfig({ init: false }) } } catch (error) { const errorMsg = `环境检查失败: ${error instanceof Error ? error.message : String(error)}` @@ -284,14 +274,6 @@ onMounted(async () => { console.log('测试配置系统...') const testConfig = await getConfig() console.log('当前配置:', testConfig) - - // 测试保存配置 - await saveConfig({ isFirstLaunch: false }) - console.log('测试配置保存成功') - - // 重新读取配置验证 - const updatedConfig = await getConfig() - console.log('更新后的配置:', updatedConfig) } catch (error) { console.error('配置系统测试失败:', error) } diff --git a/frontend/src/views/MAAUserEdit.vue b/frontend/src/views/MAAUserEdit.vue index d24f7ca..90758ea 100644 --- a/frontend/src/views/MAAUserEdit.vue +++ b/frontend/src/views/MAAUserEdit.vue @@ -1,5 +1,31 @@ @@ -346,127 +310,7 @@ onUnmounted(() => { overflow: hidden; } -/* 电源操作倒计时全屏弹窗样式 */ -.power-countdown-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.8); - backdrop-filter: blur(8px); - z-index: 9999; - display: flex; - align-items: center; - justify-content: center; - animation: fadeIn 0.3s ease-out; -} - -.power-countdown-container { - background: var(--ant-color-bg-container); - border-radius: 16px; - padding: 48px; - box-shadow: 0 24px 48px rgba(0, 0, 0, 0.2); - text-align: center; - max-width: 500px; - width: 90%; - animation: slideIn 0.3s ease-out; -} - -.countdown-content .warning-icon { - font-size: 64px; - margin-bottom: 24px; - display: block; - animation: pulse 2s infinite; -} - -.countdown-title { - font-size: 28px; - font-weight: 600; - color: var(--ant-color-text); - margin: 0 0 16px 0; -} - -.countdown-message { - font-size: 16px; - color: var(--ant-color-text-secondary); - margin: 0 0 32px 0; - line-height: 1.5; -} - -.countdown-timer { - display: flex; - align-items: baseline; - justify-content: center; - margin-bottom: 32px; -} - -.countdown-number { - font-size: 72px; - font-weight: 700; - color: var(--ant-color-primary); - line-height: 1; - margin-right: 8px; - font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace; -} - -.countdown-unit { - font-size: 24px; - color: var(--ant-color-text-secondary); - font-weight: 500; -} - -.countdown-text { - font-size: 24px; - color: var(--ant-color-text-secondary); - font-weight: 500; -} - -.countdown-progress { - margin-bottom: 32px; -} - -.countdown-actions { - display: flex; - justify-content: center; -} - -.cancel-button { - padding: 12px 32px; - height: auto; - font-size: 16px; - font-weight: 500; -} - -/* 动画效果 */ -@keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@keyframes slideIn { - from { - opacity: 0; - transform: translateY(-20px) scale(0.95); - } - to { - opacity: 1; - transform: translateY(0) scale(1); - } -} - -@keyframes pulse { - 0%, 100% { - transform: scale(1); - } - 50% { - transform: scale(1.1); - } -} +/* 电源操作倒计时弹窗样式已移至 GlobalPowerCountdown.vue */ /* 响应式 - 移动端适配 */ @media (max-width: 768px) { @@ -499,27 +343,6 @@ onUnmounted(() => { width: 100%; } - /* 移动端倒计时弹窗适配 */ - .power-countdown-container { - padding: 32px 24px; - margin: 16px; - } - - .countdown-title { - font-size: 24px; - } - - .countdown-number { - font-size: 56px; - } - - .countdown-unit { - font-size: 20px; - } - - .countdown-content .warning-icon { - font-size: 48px; - margin-bottom: 16px; - } + /* 移动端倒计时弹窗适配已移至 GlobalPowerCountdown.vue */ } \ No newline at end of file diff --git a/frontend/src/views/scheduler/schedulerHandlers.ts b/frontend/src/views/scheduler/schedulerHandlers.ts index 4c50033..4ace62a 100644 --- a/frontend/src/views/scheduler/schedulerHandlers.ts +++ b/frontend/src/views/scheduler/schedulerHandlers.ts @@ -99,7 +99,11 @@ export function handleMainMessage(wsMessage: any) { if (!wsMessage || typeof wsMessage !== 'object') return const { type, data } = wsMessage try { - if (type === 'Message' && data && data.type === 'Countdown') { + if (type === 'Signal' && data && data.RequestClose) { + // 处理后端请求前端关闭的信号 + console.log('收到后端关闭请求,开始执行应用自杀...') + handleRequestClose() + } else if (type === 'Message' && data && data.type === 'Countdown') { // 存储倒计时消息,供 UI 回放 storePendingCountdown(data) if (uiHooks.onCountdown) { @@ -118,6 +122,40 @@ export function handleMainMessage(wsMessage: any) { } } +// 处理后端请求关闭的函数 +async function handleRequestClose() { + try { + console.log('开始执行前端自杀流程...') + + // 使用更激进的强制退出方法 + if (window.electronAPI?.forceExit) { + console.log('执行强制退出...') + await window.electronAPI.forceExit() + } else if (window.electronAPI?.windowClose) { + // 备用方法:先尝试正常关闭 + console.log('执行窗口关闭...') + await window.electronAPI.windowClose() + setTimeout(async () => { + if (window.electronAPI?.appQuit) { + await window.electronAPI.appQuit() + } + }, 500) + } else { + // 最后的备用方法 + console.log('使用页面重载作为最后手段...') + window.location.reload() + } + } catch (error) { + console.error('执行自杀流程失败:', error) + // 如果所有方法都失败,尝试页面重载 + try { + window.location.reload() + } catch (e) { + console.error('页面重载也失败:', e) + } + } +} + // UI 在挂载时调用,消费并回放 pending 数据 export function consumePendingTabIds(): string[] { return popPendingTabs() diff --git a/frontend/src/views/scheduler/useSchedulerLogic.ts b/frontend/src/views/scheduler/useSchedulerLogic.ts index 01711f0..c2111e1 100644 --- a/frontend/src/views/scheduler/useSchedulerLogic.ts +++ b/frontend/src/views/scheduler/useSchedulerLogic.ts @@ -8,7 +8,6 @@ import schedulerHandlers from './schedulerHandlers' import type { ComboBoxItem } from '@/api/models/ComboBoxItem' import type { QueueItem, Script } from './schedulerConstants' import { - getPowerActionText, type SchedulerTab, type TaskMessage, type SchedulerStatus, @@ -94,13 +93,15 @@ export function useSchedulerLogic() { // 电源操作 - 从本地存储加载或使用默认值 const powerAction = ref(loadPowerActionFromStorage()) + // 注意:电源倒计时弹窗已移至全局组件 GlobalPowerCountdown.vue + // 这里保留引用以避免破坏现有代码,但实际功能由全局组件处理 const powerCountdownVisible = ref(false) const powerCountdownData = ref<{ title?: string message?: string countdown?: number }>({}) - // 前端自己的60秒倒计时 + // 前端自己的60秒倒计时 - 已移至全局组件 let powerCountdownTimer: ReturnType | null = null // 消息弹窗 @@ -508,10 +509,10 @@ export function useSchedulerLogic() { } const handleMessageDialog = (tab: SchedulerTab, data: any) => { - // 处理倒计时消息 + // 处理倒计时消息 - 已移至全局组件处理 if (data.type === 'Countdown') { - console.log('[Scheduler] 收到倒计时消息,启动60秒倒计时:', data) - startPowerCountdown(data) + console.log('[Scheduler] 收到倒计时消息,由全局组件处理:', data) + // 不再在调度中心处理倒计时,由 GlobalPowerCountdown 组件处理 return } @@ -654,68 +655,16 @@ export function useSchedulerLogic() { console.log('[Scheduler] 电源操作显示已更新为:', newPowerAction) } - // 启动60秒倒计时 - const startPowerCountdown = (data: any) => { - // 清除之前的计时器 - if (powerCountdownTimer) { - clearInterval(powerCountdownTimer) - powerCountdownTimer = null - } - - // 显示倒计时弹窗 - powerCountdownVisible.value = true - - // 设置倒计时数据,从60秒开始 - powerCountdownData.value = { - title: data.title || `${getPowerActionText(powerAction.value)}倒计时`, - message: data.message || `程序将在倒计时结束后执行 ${getPowerActionText(powerAction.value)} 操作`, - countdown: 60 - } - - // 启动每秒倒计时 - powerCountdownTimer = setInterval(() => { - if (powerCountdownData.value.countdown && powerCountdownData.value.countdown > 0) { - powerCountdownData.value.countdown-- - console.log('[Scheduler] 倒计时:', powerCountdownData.value.countdown) - - // 倒计时结束 - if (powerCountdownData.value.countdown <= 0) { - if (powerCountdownTimer) { - clearInterval(powerCountdownTimer) - powerCountdownTimer = null - } - powerCountdownVisible.value = false - console.log('[Scheduler] 倒计时结束,弹窗关闭') - } - } - }, 1000) - } - - // 移除自动执行电源操作,由后端完全控制 + // 启动60秒倒计时 - 已移至全局组件,这里保留空函数避免破坏现有代码 +// 移除自动执行电源操作,由后端完全控制 // const executePowerAction = async () => { // // 不再自己执行电源操作,完全由后端控制 // } const cancelPowerAction = async () => { - // 清除倒计时器 - if (powerCountdownTimer) { - clearInterval(powerCountdownTimer) - powerCountdownTimer = null - } - - // 关闭倒计时弹窗 - powerCountdownVisible.value = false - - // 调用取消电源操作的API - try { - await Service.cancelPowerTaskApiDispatchCancelPowerPost() - message.success('已取消电源操作') - } catch (error) { - console.error('取消电源操作失败:', error) - message.error('取消电源操作失败') - } - - // 注意:这里不重置 powerAction,保留用户选择 + console.log('[Scheduler] cancelPowerAction 已移至全局组件,调度中心不再处理') + // 电源操作取消功能已移至 GlobalPowerCountdown 组件 + // 这里保留空函数以避免破坏现有的调用代码 } // 移除自动检查任务完成的逻辑,完全由后端控制 @@ -788,8 +737,8 @@ export function useSchedulerLogic() { }, onCountdown: (data) => { try { - // 直接启动前端倒计时 - startPowerCountdown(data) + // 倒计时已移至全局组件处理,这里不再处理 + console.log('[Scheduler] 倒计时消息由全局组件处理:', data) } catch (e) { console.warn('[Scheduler] registerSchedulerUI onCountdown error:', e) } @@ -814,7 +763,8 @@ export function useSchedulerLogic() { const pendingCountdown = schedulerHandlers.consumePendingCountdown() if (pendingCountdown) { try { - startPowerCountdown(pendingCountdown) + // 倒计时已移至全局组件处理,这里不再处理 + console.log('[Scheduler] 待处理倒计时消息由全局组件处理:', pendingCountdown) } catch (e) { console.warn('[Scheduler] replay pending countdown error:', e) } @@ -843,10 +793,17 @@ export function useSchedulerLogic() { const { type, data } = wsMessage console.log('[Scheduler] 收到Main消息:', { type, data }) + // 首先调用 schedulerHandlers 的处理函数,确保 RequestClose 等信号被正确处理 + try { + schedulerHandlers.handleMainMessage(wsMessage) + } catch (e) { + console.warn('[Scheduler] schedulerHandlers.handleMainMessage error:', e) + } + if (type === 'Message' && data && data.type === 'Countdown') { - // 收到倒计时消息,启动前端60秒倒计时 - console.log('[Scheduler] 收到倒计时消息,启动前端60秒倒计时:', data) - startPowerCountdown(data) + // 收到倒计时消息,由全局组件处理 + console.log('[Scheduler] 收到倒计时消息,由全局组件处理:', data) + // 不再在调度中心处理倒计时 } else if (type === 'Update' && data && data.PowerSign !== undefined) { // 收到电源操作更新消息,更新显示 console.log('[Scheduler] 收到电源操作更新消息:', data.PowerSign) @@ -856,7 +813,7 @@ export function useSchedulerLogic() { // 清理函数 const cleanup = () => { - // 清理倒计时器 + // 清理倒计时器 - 已移至全局组件,这里保留以避免错误 if (powerCountdownTimer) { clearInterval(powerCountdownTimer) powerCountdownTimer = null diff --git a/frontend/src/views/setting/TabAdvanced.vue b/frontend/src/views/setting/TabAdvanced.vue index 63360df..6fe88e8 100644 --- a/frontend/src/views/setting/TabAdvanced.vue +++ b/frontend/src/views/setting/TabAdvanced.vue @@ -1,19 +1,13 @@ + + diff --git a/frontend/src/views/setting/index.vue b/frontend/src/views/setting/index.vue index d08e9e5..a657478 100644 --- a/frontend/src/views/setting/index.vue +++ b/frontend/src/views/setting/index.vue @@ -34,11 +34,17 @@ const version = (import.meta as any).env?.VITE_APP_VERSION || '获取版本失 const backendUpdateInfo = ref(null) // 镜像配置状态 -const mirrorConfigStatus = ref({ +type MirrorConfigStatus = { + isUsingCloudConfig: boolean + version?: string + lastUpdated?: string + source: 'cloud' | 'fallback' +} +const mirrorConfigStatus = ref({ isUsingCloudConfig: false, - version: '', - lastUpdated: '', - source: 'fallback' as 'cloud' | 'fallback', + version: undefined, + lastUpdated: undefined, + source: 'fallback', }) const refreshingConfig = ref(false) @@ -324,9 +330,12 @@ onMounted(() => {