Skip to content

Commit d33ed99

Browse files
committed
feat: 新增本局结束后自动停止挂机功能
1 parent 9850b24 commit d33ed99

File tree

4 files changed

+119
-5
lines changed

4 files changed

+119
-5
lines changed

out/main/index.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16375,8 +16375,14 @@ function registerToggleHotkey(accelerator) {
1637516375
}
1637616376
const success = globalHotkeyManager.register(accelerator, async () => {
1637716377
console.log(`🎮 [Main] 快捷键 ${accelerator} 被触发,切换挂机状态`);
16378-
hexService.isRunning ? await hexService.stop() : await hexService.start();
16379-
win?.webContents.send(IpcChannel.HEX_TOGGLE_TRIGGERED, hexService.isRunning);
16378+
const wasRunning = hexService.isRunning;
16379+
if (wasRunning) {
16380+
await hexService.stop();
16381+
} else {
16382+
await hexService.start();
16383+
}
16384+
const newState = !wasRunning;
16385+
win?.webContents.send(IpcChannel.HEX_TOGGLE_TRIGGERED, newState);
1638016386
});
1638116387
if (success) {
1638216388
currentToggleHotkey = accelerator;

src-backend/states/GameRunningState.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { gameStateManager } from "../services/GameStateManager";
2727
import { logger } from "../utils/Logger";
2828
import { sleep } from "../utils/HelperTools";
2929
import { inGameApi, InGameApiEndpoints } from "../lcu/InGameApi";
30-
import { showToast } from "../utils/ToastBridge";
30+
import { showToast, notifyStopAfterGameState, notifyHexRunningState } from "../utils/ToastBridge";
3131
import { hexService } from "../services/HexService";
3232

3333
/** abort 信号轮询间隔 (ms),作为事件监听的兜底 */
@@ -98,8 +98,19 @@ export class GameRunningState implements IState {
9898
} else if (isGameEnded) {
9999
// 游戏正常结束,检查是否设置了"本局结束后停止"
100100
if (hexService.stopAfterCurrentGame) {
101-
logger.info("[GameRunningState] 游戏结束,检测到【本局结束后停止】标志,流转到 EndState");
101+
logger.info("[GameRunningState] 游戏结束,检测到【本局结束后停止】标志,停止挂机");
102102
showToast.success("本局已结束,自动停止挂机", { position: 'top-center' });
103+
104+
// 通知前端重置"本局结束后停止"状态(因为是一次性功能,生效后自动取消)
105+
notifyStopAfterGameState(false);
106+
107+
// 通知前端挂机已停止(更新开关按钮状态)
108+
notifyHexRunningState(false);
109+
110+
// 调用 hexService.stop() 来正确停止服务
111+
// 这会触发 AbortSignal,主循环会进入 finally 块执行 EndState 清理
112+
await hexService.stop();
113+
103114
return new EndState();
104115
}
105116
// 否则返回大厅开始下一局

src-backend/utils/ToastBridge.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,31 @@ showToast.warning = (message: string, options?: Omit<ToastOptions, 'type'>) =>
6161

6262
showToast.error = (message: string, options?: Omit<ToastOptions, 'type'>) =>
6363
showToast(message, { ...options, type: 'error' });
64+
65+
/**
66+
* 通知前端更新"本局结束后停止"状态
67+
* @param newState 新的状态值
68+
*
69+
* @description 用于从后端状态机通知前端更新 UI 状态
70+
* 主要在 GameRunningState 中,当"本局结束后停止"功能生效时调用
71+
*/
72+
export function notifyStopAfterGameState(newState: boolean): void {
73+
const windows = BrowserWindow.getAllWindows();
74+
for (const win of windows) {
75+
win.webContents.send(IpcChannel.HEX_STOP_AFTER_GAME_TRIGGERED, newState);
76+
}
77+
}
78+
79+
/**
80+
* 通知前端挂机服务运行状态变化
81+
* @param isRunning 新的运行状态(true=运行中,false=已停止)
82+
*
83+
* @description 用于从后端通知前端更新挂机开关的 UI 状态
84+
* 主要在"本局结束后停止"功能生效、自动停止挂机时调用
85+
*/
86+
export function notifyHexRunningState(isRunning: boolean): void {
87+
const windows = BrowserWindow.getAllWindows();
88+
for (const win of windows) {
89+
win.webContents.send(IpcChannel.HEX_TOGGLE_TRIGGERED, isRunning);
90+
}
91+
}

src/components/pages/HomePage.tsx

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React, {useState, useEffect} from 'react';
2-
import styled from 'styled-components';
2+
import styled, { keyframes } from 'styled-components';
33
import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline';
44
import StopCircleOutlinedIcon from '@mui/icons-material/StopCircleOutlined';
55
import BlockIcon from '@mui/icons-material/Block';
66
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
7+
import TimerOffIcon from '@mui/icons-material/TimerOff';
78
import {ThemeType} from "../../styles/theme.ts";
89
import {LogPanel} from "../LogPanel.tsx";
910
import {toast} from "../toast/toast-core.ts";
@@ -245,6 +246,45 @@ const AdminWarningBanner = styled.div<{ theme: ThemeType }>`
245246
gap: 6px;
246247
`;
247248

249+
/** 横幅滑入动画 - 从上方滑入并淡入 */
250+
const slideInFromTop = keyframes`
251+
from {
252+
opacity: 0;
253+
transform: translateY(-10px);
254+
max-height: 0;
255+
padding: 0 12px;
256+
margin-bottom: 0;
257+
margin-top: 0;
258+
}
259+
to {
260+
opacity: 1;
261+
transform: translateY(0);
262+
max-height: 50px;
263+
padding: 6px 12px;
264+
margin-bottom: 12px;
265+
margin-top: -12px;
266+
}
267+
`;
268+
269+
/** "本局结束后停止"信息横幅 */
270+
const StopAfterGameBanner = styled.div<{ theme: ThemeType }>`
271+
background-color: ${props => props.theme.colors.primary}20;
272+
border: 1px solid ${props => props.theme.colors.primary}60;
273+
border-radius: ${props => props.theme.borderRadius};
274+
padding: 6px 12px;
275+
margin-bottom: 12px;
276+
margin-top: -12px;
277+
font-size: 1rem;
278+
color: ${props => props.theme.colors.primary};
279+
display: flex;
280+
align-items: center;
281+
gap: 6px;
282+
overflow: hidden;
283+
284+
/* 入场动画 */
285+
animation: ${slideInFromTop} 0.3s ease-out forwards;
286+
`;
287+
248288
// ============================================
249289
// 游戏模式切换样式
250290
// ============================================
@@ -811,6 +851,8 @@ export const HomePage = () => {
811851
const [logMode, setLogMode] = useState<LogMode>(LogMode.SIMPLE);
812852
// 新增:管理员权限状态(null 表示还在检测中)
813853
const [isElevated, setIsElevated] = useState<boolean | null>(null);
854+
// 新增:"本局结束后停止"状态
855+
const [stopAfterGame, setStopAfterGame] = useState(false);
814856

815857
/**
816858
* 获取召唤师信息的函数
@@ -927,6 +969,25 @@ export const HomePage = () => {
927969
toast.success('海克斯科技启动!');
928970
} else {
929971
toast.success('海克斯科技已关闭!');
972+
// 停止时清除"本局结束后停止"状态
973+
setStopAfterGame(false);
974+
}
975+
});
976+
977+
return () => cleanup();
978+
}, []);
979+
980+
// 监听快捷键触发的"本局结束后停止"切换事件
981+
useEffect(() => {
982+
const cleanup = window.hex.onStopAfterGameTriggered((newState: boolean) => {
983+
console.log('🎮 [HomePage] 收到"本局结束后停止"切换事件,新状态:', newState);
984+
setStopAfterGame(newState);
985+
986+
// 显示提示
987+
if (newState) {
988+
toast.info('对局结束后自动停止挂机');
989+
} else {
990+
toast.info('已取消对局结束后停止');
930991
}
931992
});
932993

@@ -1086,6 +1147,14 @@ export const HomePage = () => {
10861147
</LoadingPlaceholder>
10871148
)}
10881149
</SummonerSection>
1150+
1151+
{/* "本局结束后停止"状态提示 - 在召唤师区域下方显示 */}
1152+
{stopAfterGame && (
1153+
<StopAfterGameBanner>
1154+
<TimerOffIcon style={{ fontSize: '1rem' }} />
1155+
对局结束后自动停止挂机
1156+
</StopAfterGameBanner>
1157+
)}
10891158

10901159
{/* 控制区域 - Flexbox 水平排列 */}
10911160
<ControlRow>

0 commit comments

Comments
 (0)