feat(dialog): 实现对话框主题与拖拽功能

This commit is contained in:
MoeSnowyFox
2025-10-02 02:30:05 +08:00
parent 0a20ee299d
commit 334c17c055
4 changed files with 868 additions and 265 deletions

View File

@@ -176,9 +176,7 @@ class MaaManager:
# 开始代理
for self.index, user in enumerate(self.user_list):
try:
self.cur_user_data = self.user_config[uuid.UUID(user["user_id"])]
if (self.script_config.get("Run", "ProxyTimesLimit") == 0) or (
@@ -225,11 +223,9 @@ class MaaManager:
if self.cur_user_data.get(
"Info", "IfSkland"
) and self.cur_user_data.get("Info", "SklandToken"):
if self.cur_user_data.get(
"Data", "LastSklandDate"
) != datetime.now().strftime("%Y-%m-%d"):
await Config.send_json(
WebSocketMessage(
id=self.ws_id,
@@ -243,7 +239,6 @@ class MaaManager:
)
for type, user_list in skland_result.items():
if type != "总计" and len(user_list) > 0:
logger.info(
f"用户: {user['user_id']} - 森空岛签到{type}: {''.join(user_list)}"
@@ -299,7 +294,6 @@ class MaaManager:
# 剿灭-日常模式循环
for mode in ["Annihilation", "Routine"]:
if self.run_book[mode]:
continue
@@ -361,7 +355,6 @@ class MaaManager:
# 解析任务构成
if mode == "Routine":
self.task_dict = {
"WakeUp": str(
self.cur_user_data.get("Task", "IfWakeUp")
@@ -386,7 +379,6 @@ class MaaManager:
}
elif mode == "Annihilation":
self.task_dict = {
"WakeUp": "True",
"Recruiting": "False",
@@ -404,7 +396,6 @@ class MaaManager:
# 尝试次数循环
for i in range(self.script_config.get("Run", "RunTimesLimit")):
if self.run_book[mode]:
break
@@ -566,7 +557,6 @@ class MaaManager:
# 处理MAA结果
if self.maa_result == "Success!":
# 标记任务完成
self.run_book[mode] = True
@@ -743,7 +733,6 @@ class MaaManager:
await self.result_record()
except Exception as e:
logger.exception(f"代理用户 {user['user_id']} 时出现异常: {e}")
user["status"] = "异常"
await Config.send_json(
@@ -756,7 +745,6 @@ class MaaManager:
# 人工排查模式
elif self.mode == "人工排查":
# 人工排查时, 屏蔽静默操作
logger.info("人工排查任务开始, 屏蔽静默操作")
Config.if_ignore_silence.append(self.script_id)
@@ -766,7 +754,6 @@ class MaaManager:
# 开始排查
for self.index, user in enumerate(self.user_list):
self.cur_user_data = self.user_config[uuid.UUID(user["user_id"])]
logger.info(f"开始排查用户: {user['user_id']}")
@@ -787,7 +774,6 @@ class MaaManager:
# 启动重试循环
while True:
# 配置MAA
await self.set_maa("人工排查")
@@ -849,7 +835,6 @@ class MaaManager:
break
# 登录失败, 询问是否结束循环
else:
uid = str(uuid.uuid4())
await Config.send_json(
WebSocketMessage(
@@ -860,6 +845,7 @@ class MaaManager:
"type": "Question",
"title": "操作提示",
"message": "MAA未能正确登录到PRTS, 是否重试?",
"options": ["", ""],
},
).model_dump()
)
@@ -869,7 +855,6 @@ class MaaManager:
# 登录成功, 录入人工排查情况
if self.run_book["SignIn"]:
uid = str(uuid.uuid4())
await Config.send_json(
WebSocketMessage(
@@ -880,6 +865,7 @@ class MaaManager:
"type": "Question",
"title": "操作提示",
"message": "请检查用户代理情况, 该用户是否正确完成代理任务?",
"options": ["", ""],
},
).model_dump()
)
@@ -899,7 +885,6 @@ class MaaManager:
# 设置MAA模式
elif self.mode == "设置脚本":
# 配置MAA
await self.set_maa(self.mode)
# 创建MAA任务
@@ -982,7 +967,6 @@ class MaaManager:
)
elif self.mode == "人工排查":
if self.run_book["SignIn"] and self.run_book["PassCheck"]:
logger.info(
f"用户 {self.user_list[self.index]['user_id']} 通过人工排查"
@@ -1018,7 +1002,6 @@ class MaaManager:
return self.check_result
if self.mode == "人工排查":
# 解除静默操作屏蔽
logger.info("人工排查任务结束, 解除静默操作屏蔽")
if self.script_id in Config.if_ignore_silence:
@@ -1029,9 +1012,7 @@ class MaaManager:
and hasattr(self, "index")
and self.user_list[self.index]["status"] == "运行"
):
if not self.maa_update_package:
self.maa_result = "用户手动中止任务"
# 保存运行日志以及统计信息
@@ -1071,12 +1052,10 @@ class MaaManager:
and hasattr(self, "index")
and self.user_list[self.index]["status"] == "运行"
):
await self.result_record()
# 导出结果
if self.mode in ["自动代理", "人工排查"]:
# 更新用户数据
await Config.ScriptConfig[self.script_id].UserData.load(
await self.user_config.toDict()
@@ -1207,7 +1186,6 @@ class MaaManager:
)
for port in self.port_range:
ADB_address = f"{ADB_ip}{ADB_port + port}"
# 尝试通过ADB连接到指定地址
@@ -1221,7 +1199,6 @@ class MaaManager:
)
if "connected" in connect_result.stdout:
# 检查连接状态
devices_result = subprocess.run(
[self.ADB_path, "devices"],
@@ -1232,7 +1209,6 @@ class MaaManager:
encoding="utf-8",
)
if ADB_address in devices_result.stdout:
logger.info(f"ADB实际地址: {ADB_address}")
# 断开连接
@@ -1250,9 +1226,9 @@ class MaaManager:
await System.kill_process(self.maa_exe_path)
with self.maa_set_path.open(mode="r", encoding="utf-8") as f:
data = json.load(f)
data["Configurations"]["Default"][
"Connect.Address"
] = self.ADB_address
data["Configurations"]["Default"]["Connect.Address"] = (
self.ADB_address
)
data["Configurations"]["Default"]["Start.EmulatorWaitSeconds"] = "0"
with self.maa_set_path.open(mode="w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
@@ -1279,7 +1255,6 @@ class MaaManager:
)
if self.mode == "自动代理":
# 获取最近一条日志的时间
latest_time = self.log_start_time
for _ in self.maa_logs[::-1]:
@@ -1302,7 +1277,6 @@ class MaaManager:
self.maa_result = "MAA未能正确登录PRTS"
elif "任务已全部完成!" in log:
if "完成任务: StartUp" in log or "完成任务: 开始唤醒" in log:
self.task_dict["WakeUp"] = "False"
if "完成任务: Recruit" in log or "完成任务: 自动公招" in log:
@@ -1383,7 +1357,6 @@ class MaaManager:
logger.info(f"开始配置MAA运行参数: {mode}")
if self.mode != "设置脚本" and mode != "Update":
if self.cur_user_data.get("Info", "Server") == "Bilibili":
self.agree_bilibili(True)
else:
@@ -1454,38 +1427,36 @@ class MaaManager:
# 自动代理配置
if self.mode == "自动代理" and mode in ["Annihilation", "Routine"]:
if (self.index == len(self.user_list) - 1) or (
self.user_config[
uuid.UUID(self.user_list[self.index + 1]["user_id"])
].get("Info", "Mode")
== "详细"
):
data["Configurations"]["Default"][
"MainFunction.PostActions"
] = "12" # 完成后退出MAA和模拟器
data["Configurations"]["Default"]["MainFunction.PostActions"] = (
"12" # 完成后退出MAA和模拟器
)
else:
data["Configurations"]["Default"]["MainFunction.PostActions"] = (
METHOD_BOOK[self.script_config.get("Run", "TaskTransitionMethod")]
) # 完成后行为
data["Configurations"]["Default"][
"Start.RunDirectly"
] = "True" # 启动MAA后直接运行
data["Configurations"]["Default"]["Start.RunDirectly"] = (
"True" # 启动MAA后直接运行
)
data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = str(
self.if_open_emulator
) # 启动MAA后自动开启模拟器
data["Global"][
"VersionUpdate.ScheduledUpdateCheck"
] = "False" # 定时检查更新
data["Global"][
"VersionUpdate.AutoDownloadUpdatePackage"
] = "True" # 自动下载更新包
data["Global"][
"VersionUpdate.AutoInstallUpdatePackage"
] = "False" # 自动安装更新包
data["Global"]["VersionUpdate.ScheduledUpdateCheck"] = (
"False" # 定时检查更新
)
data["Global"]["VersionUpdate.AutoDownloadUpdatePackage"] = (
"True" # 自动下载更新包
)
data["Global"]["VersionUpdate.AutoInstallUpdatePackage"] = (
"False" # 自动安装更新包
)
if Config.get("Function", "IfSilence"):
data["Global"]["Start.MinimizeDirectly"] = "True" # 启动MAA后直接最小化
@@ -1510,9 +1481,9 @@ class MaaManager:
)
# 按预设设定任务
data["Configurations"]["Default"][
"TaskQueue.WakeUp.IsChecked"
] = "True" # 开始唤醒
data["Configurations"]["Default"]["TaskQueue.WakeUp.IsChecked"] = (
"True" # 开始唤醒
)
data["Configurations"]["Default"]["TaskQueue.Recruiting.IsChecked"] = (
self.task_dict["Recruiting"]
) # 自动公招
@@ -1584,49 +1555,47 @@ class MaaManager:
) # 连战次数
if mode == "Annihilation":
data["Configurations"]["Default"][
"MainFunction.Stage1"
] = "Annihilation" # 主关卡
data["Configurations"]["Default"][
"MainFunction.Stage2"
] = "" # 备选关卡1
data["Configurations"]["Default"][
"MainFunction.Stage3"
] = "" # 备选关卡2
data["Configurations"]["Default"][
"Fight.RemainingSanityStage"
] = "" # 剩余理智关卡
data["Configurations"]["Default"][
"MainFunction.Series.Quantity"
] = "1" # 连战次数
data["Configurations"]["Default"]["MainFunction.Stage1"] = (
"Annihilation" # 主关卡
)
data["Configurations"]["Default"]["MainFunction.Stage2"] = (
"" # 备选关卡1
)
data["Configurations"]["Default"]["MainFunction.Stage3"] = (
"" # 备选关卡2
)
data["Configurations"]["Default"]["Fight.RemainingSanityStage"] = (
"" # 剩余理智关卡
)
data["Configurations"]["Default"]["MainFunction.Series.Quantity"] = (
"1" # 连战次数
)
data["Configurations"]["Default"][
"MainFunction.Annihilation.UseCustom"
] = "True" # 自定义剿灭关卡
data["Configurations"]["Default"]["MainFunction.Annihilation.Stage"] = (
self.cur_user_data.get("Info", "Annihilation")
) # 自定义剿灭关卡号
data["Configurations"]["Default"][
"Penguin.IsDrGrandet"
] = "False" # 博朗台模式
data["Configurations"]["Default"][
"GUI.CustomStageCode"
] = "True" # 手动输入关卡名
data["Configurations"]["Default"][
"GUI.UseAlternateStage"
] = "False" # 使用备选关卡
data["Configurations"]["Default"][
"Fight.UseRemainingSanityStage"
] = "False" # 使用剩余理智
data["Configurations"]["Default"][
"Fight.UseExpiringMedicine"
] = "True" # 无限吃48小时内过期的理智药
data["Configurations"]["Default"][
"GUI.HideSeries"
] = "False" # 隐藏连战次数
data["Configurations"]["Default"]["Penguin.IsDrGrandet"] = (
"False" # 博朗台模式
)
data["Configurations"]["Default"]["GUI.CustomStageCode"] = (
"True" # 手动输入关卡名
)
data["Configurations"]["Default"]["GUI.UseAlternateStage"] = (
"False" # 使用备选关卡
)
data["Configurations"]["Default"]["Fight.UseRemainingSanityStage"] = (
"False" # 使用剩余理智
)
data["Configurations"]["Default"]["Fight.UseExpiringMedicine"] = (
"True" # 无限吃48小时内过期的理智药
)
data["Configurations"]["Default"]["GUI.HideSeries"] = (
"False" # 隐藏连战次数
)
elif mode == "Routine":
data["Configurations"]["Default"]["MainFunction.Stage1"] = (
plan_data.get("Stage") if plan_data.get("Stage", "-") != "-" else ""
) # 主关卡
@@ -1650,35 +1619,32 @@ class MaaManager:
if plan_data.get("Stage_Remain", "-") != "-"
else ""
) # 剩余理智关卡
data["Configurations"]["Default"][
"GUI.UseAlternateStage"
] = "True" # 备选关卡
data["Configurations"]["Default"]["GUI.UseAlternateStage"] = (
"True" # 备选关卡
)
data["Configurations"]["Default"]["Fight.UseRemainingSanityStage"] = (
"True" if plan_data.get("Stage_Remain", "-") != "-" else "False"
) # 使用剩余理智
if self.cur_user_data.get("Info", "Mode") == "简洁":
data["Configurations"]["Default"][
"Penguin.IsDrGrandet"
] = "False" # 博朗台模式
data["Configurations"]["Default"][
"GUI.CustomStageCode"
] = "True" # 手动输入关卡名
data["Configurations"]["Default"][
"Fight.UseExpiringMedicine"
] = "True" # 无限吃48小时内过期的理智药
data["Configurations"]["Default"]["Penguin.IsDrGrandet"] = (
"False" # 博朗台模式
)
data["Configurations"]["Default"]["GUI.CustomStageCode"] = (
"True" # 手动输入关卡名
)
data["Configurations"]["Default"]["Fight.UseExpiringMedicine"] = (
"True" # 无限吃48小时内过期的理智药
)
# 自定义基建配置
if self.cur_user_data.get("Info", "InfrastMode") == "Custom":
if (
Path.cwd()
/ f"data/{self.script_id}/{self.user_list[self.index]['user_id']}/Infrastructure/infrastructure.json"
).exists():
data["Configurations"]["Default"][
"Infrast.InfrastMode"
] = "Custom" # 基建模式
data["Configurations"]["Default"]["Infrast.InfrastMode"] = (
"Custom" # 基建模式
)
data["Configurations"]["Default"][
"Infrast.CustomInfrastPlanIndex"
] = self.cur_user_data.get(
@@ -1718,7 +1684,6 @@ class MaaManager:
) # 基建模式
elif self.cur_user_data.get("Info", "Mode") == "详细":
# 基建模式
if (
data["Configurations"]["Default"]["Infrast.InfrastMode"]
@@ -1732,28 +1697,27 @@ class MaaManager:
# 人工排查配置
elif self.mode == "人工排查" and self.cur_user_data is not None:
data["Configurations"]["Default"][
"MainFunction.PostActions"
] = "8" # 完成后退出MAA
data["Configurations"]["Default"][
"Start.RunDirectly"
] = "True" # 启动MAA后直接运行
data["Configurations"]["Default"]["MainFunction.PostActions"] = (
"8" # 完成后退出MAA
)
data["Configurations"]["Default"]["Start.RunDirectly"] = (
"True" # 启动MAA后直接运行
)
data["Global"]["Start.MinimizeDirectly"] = "True" # 启动MAA后直接最小化
data["Global"]["GUI.UseTray"] = "True" # 显示托盘图标
data["Global"]["GUI.MinimizeToTray"] = "True" # 最小化时隐藏至托盘
data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = str(
self.if_open_emulator
) # 启动MAA后自动开启模拟器
data["Global"][
"VersionUpdate.ScheduledUpdateCheck"
] = "False" # 定时检查更新
data["Global"][
"VersionUpdate.AutoDownloadUpdatePackage"
] = "False" # 自动下载更新包
data["Global"][
"VersionUpdate.AutoInstallUpdatePackage"
] = "False" # 自动安装更新包
data["Global"]["VersionUpdate.ScheduledUpdateCheck"] = (
"False" # 定时检查更新
)
data["Global"]["VersionUpdate.AutoDownloadUpdatePackage"] = (
"False" # 自动下载更新包
)
data["Global"]["VersionUpdate.AutoInstallUpdatePackage"] = (
"False" # 自动安装更新包
)
# 客户端类型
data["Configurations"]["Default"]["Start.ClientType"] = (
@@ -1772,134 +1736,132 @@ class MaaManager:
self.cur_user_data.get("Info", "Id")
)
data["Configurations"]["Default"][
"TaskQueue.WakeUp.IsChecked"
] = "True" # 开始唤醒
data["Configurations"]["Default"][
"TaskQueue.Recruiting.IsChecked"
] = "False" # 自动公招
data["Configurations"]["Default"][
"TaskQueue.Base.IsChecked"
] = "False" # 基建换班
data["Configurations"]["Default"][
"TaskQueue.Combat.IsChecked"
] = "False" # 刷理智
data["Configurations"]["Default"][
"TaskQueue.Mission.IsChecked"
] = "False" # 领取奖励
data["Configurations"]["Default"][
"TaskQueue.Mall.IsChecked"
] = "False" # 获取信用及购物
data["Configurations"]["Default"][
"TaskQueue.AutoRoguelike.IsChecked"
] = "False" # 自动肉鸽
data["Configurations"]["Default"][
"TaskQueue.Reclamation.IsChecked"
] = "False" # 生息演算
data["Configurations"]["Default"]["TaskQueue.WakeUp.IsChecked"] = (
"True" # 开始唤醒
)
data["Configurations"]["Default"]["TaskQueue.Recruiting.IsChecked"] = (
"False" # 自动公招
)
data["Configurations"]["Default"]["TaskQueue.Base.IsChecked"] = (
"False" # 基建换班
)
data["Configurations"]["Default"]["TaskQueue.Combat.IsChecked"] = (
"False" # 刷理智
)
data["Configurations"]["Default"]["TaskQueue.Mission.IsChecked"] = (
"False" # 领取奖励
)
data["Configurations"]["Default"]["TaskQueue.Mall.IsChecked"] = (
"False" # 获取信用及购物
)
data["Configurations"]["Default"]["TaskQueue.AutoRoguelike.IsChecked"] = (
"False" # 自动肉鸽
)
data["Configurations"]["Default"]["TaskQueue.Reclamation.IsChecked"] = (
"False" # 生息演算
)
# 设置脚本配置
elif self.mode == "设置脚本":
data["Configurations"]["Default"][
"MainFunction.PostActions"
] = "0" # 完成后无动作
data["Configurations"]["Default"][
"Start.RunDirectly"
] = "False" # 启动MAA后直接运行
data["Configurations"]["Default"][
"Start.OpenEmulatorAfterLaunch"
] = "False" # 启动MAA后自动开启模拟器
data["Global"][
"VersionUpdate.ScheduledUpdateCheck"
] = "False" # 定时检查更新
data["Global"][
"VersionUpdate.AutoDownloadUpdatePackage"
] = "False" # 自动下载更新包
data["Global"][
"VersionUpdate.AutoInstallUpdatePackage"
] = "False" # 自动安装更新包
data["Configurations"]["Default"]["MainFunction.PostActions"] = (
"0" # 完成后无动作
)
data["Configurations"]["Default"]["Start.RunDirectly"] = (
"False" # 启动MAA后直接运行
)
data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = (
"False" # 启动MAA后自动开启模拟器
)
data["Global"]["VersionUpdate.ScheduledUpdateCheck"] = (
"False" # 定时检查更新
)
data["Global"]["VersionUpdate.AutoDownloadUpdatePackage"] = (
"False" # 自动下载更新包
)
data["Global"]["VersionUpdate.AutoInstallUpdatePackage"] = (
"False" # 自动安装更新包
)
if Config.get("Function", "IfSilence"):
data["Global"][
"Start.MinimizeDirectly"
] = "False" # 启动MAA后直接最小化
data["Global"]["Start.MinimizeDirectly"] = (
"False" # 启动MAA后直接最小化
)
data["Configurations"]["Default"][
"TaskQueue.WakeUp.IsChecked"
] = "False" # 开始唤醒
data["Configurations"]["Default"][
"TaskQueue.Recruiting.IsChecked"
] = "False" # 自动公招
data["Configurations"]["Default"][
"TaskQueue.Base.IsChecked"
] = "False" # 基建换班
data["Configurations"]["Default"][
"TaskQueue.Combat.IsChecked"
] = "False" # 刷理智
data["Configurations"]["Default"][
"TaskQueue.Mission.IsChecked"
] = "False" # 领取奖励
data["Configurations"]["Default"][
"TaskQueue.Mall.IsChecked"
] = "False" # 获取信用及购物
data["Configurations"]["Default"][
"TaskQueue.AutoRoguelike.IsChecked"
] = "False" # 自动肉鸽
data["Configurations"]["Default"][
"TaskQueue.Reclamation.IsChecked"
] = "False" # 生息演算
data["Configurations"]["Default"]["TaskQueue.WakeUp.IsChecked"] = (
"False" # 开始唤醒
)
data["Configurations"]["Default"]["TaskQueue.Recruiting.IsChecked"] = (
"False" # 自动公招
)
data["Configurations"]["Default"]["TaskQueue.Base.IsChecked"] = (
"False" # 基建换班
)
data["Configurations"]["Default"]["TaskQueue.Combat.IsChecked"] = (
"False" # 刷理智
)
data["Configurations"]["Default"]["TaskQueue.Mission.IsChecked"] = (
"False" # 领取奖励
)
data["Configurations"]["Default"]["TaskQueue.Mall.IsChecked"] = (
"False" # 获取信用及购物
)
data["Configurations"]["Default"]["TaskQueue.AutoRoguelike.IsChecked"] = (
"False" # 自动肉鸽
)
data["Configurations"]["Default"]["TaskQueue.Reclamation.IsChecked"] = (
"False" # 生息演算
)
elif mode == "Update":
data["Configurations"]["Default"][
"MainFunction.PostActions"
] = "0" # 完成后无动作
data["Configurations"]["Default"][
"Start.RunDirectly"
] = "False" # 启动MAA后直接运行
data["Configurations"]["Default"][
"Start.OpenEmulatorAfterLaunch"
] = "False" # 启动MAA后自动开启模拟器
data["Configurations"]["Default"]["MainFunction.PostActions"] = (
"0" # 完成后无动作
)
data["Configurations"]["Default"]["Start.RunDirectly"] = (
"False" # 启动MAA后直接运行
)
data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = (
"False" # 启动MAA后自动开启模拟器
)
data["Global"]["Start.MinimizeDirectly"] = "True" # 启动MAA后直接最小化
data["Global"]["GUI.UseTray"] = "True" # 显示托盘图标
data["Global"]["GUI.MinimizeToTray"] = "True" # 最小化时隐藏至托盘
data["Global"][
"VersionUpdate.package"
] = self.maa_update_package # 更新包路径
data["Global"]["VersionUpdate.package"] = (
self.maa_update_package
) # 更新包路径
data["Global"][
"VersionUpdate.ScheduledUpdateCheck"
] = "False" # 定时检查更新
data["Global"][
"VersionUpdate.AutoDownloadUpdatePackage"
] = "False" # 自动下载更新包
data["Global"][
"VersionUpdate.AutoInstallUpdatePackage"
] = "True" # 自动安装更新包
data["Configurations"]["Default"][
"TaskQueue.WakeUp.IsChecked"
] = "False" # 开始唤醒
data["Configurations"]["Default"][
"TaskQueue.Recruiting.IsChecked"
] = "False" # 自动公招
data["Configurations"]["Default"][
"TaskQueue.Base.IsChecked"
] = "False" # 基建换班
data["Configurations"]["Default"][
"TaskQueue.Combat.IsChecked"
] = "False" # 刷理智
data["Configurations"]["Default"][
"TaskQueue.Mission.IsChecked"
] = "False" # 领取奖励
data["Configurations"]["Default"][
"TaskQueue.Mall.IsChecked"
] = "False" # 获取信用及购物
data["Configurations"]["Default"][
"TaskQueue.AutoRoguelike.IsChecked"
] = "False" # 自动肉鸽
data["Configurations"]["Default"][
"TaskQueue.Reclamation.IsChecked"
] = "False" # 生息演算
data["Global"]["VersionUpdate.ScheduledUpdateCheck"] = (
"False" # 定时检查更新
)
data["Global"]["VersionUpdate.AutoDownloadUpdatePackage"] = (
"False" # 自动下载更新包
)
data["Global"]["VersionUpdate.AutoInstallUpdatePackage"] = (
"True" # 自动安装更新包
)
data["Configurations"]["Default"]["TaskQueue.WakeUp.IsChecked"] = (
"False" # 开始唤醒
)
data["Configurations"]["Default"]["TaskQueue.Recruiting.IsChecked"] = (
"False" # 自动公招
)
data["Configurations"]["Default"]["TaskQueue.Base.IsChecked"] = (
"False" # 基建换班
)
data["Configurations"]["Default"]["TaskQueue.Combat.IsChecked"] = (
"False" # 刷理智
)
data["Configurations"]["Default"]["TaskQueue.Mission.IsChecked"] = (
"False" # 领取奖励
)
data["Configurations"]["Default"]["TaskQueue.Mall.IsChecked"] = (
"False" # 获取信用及购物
)
data["Configurations"]["Default"]["TaskQueue.AutoRoguelike.IsChecked"] = (
"False" # 自动肉鸽
)
data["Configurations"]["Default"]["TaskQueue.Reclamation.IsChecked"] = (
"False" # 生息演算
)
# 启动模拟器仅生效一次
if self.mode != "设置脚本" and mode != "Update" and self.if_open_emulator:
@@ -1995,7 +1957,6 @@ class MaaManager:
)
elif mode == "统计信息":
# 生成文本通知内容
formatted = []
if "drop_statistics" in message:
@@ -2030,7 +1991,6 @@ class MaaManager:
# 发送全局通知
if Config.get("Notify", "IfSendStatistic"):
if Config.get("Notify", "IfSendMail"):
await Notify.send_mail(
"网页", title, message_html, Config.get("Notify", "ToAddress")
@@ -2053,7 +2013,6 @@ class MaaManager:
if self.cur_user_data.get("Notify", "Enabled") and self.cur_user_data.get(
"Notify", "IfSendStatistic"
):
# 发送邮件通知
if self.cur_user_data.get("Notify", "IfSendMail"):
await Notify.send_mail(
@@ -2078,7 +2037,6 @@ class MaaManager:
)
elif mode == "公招六星":
# 生成HTML通知内容
template = env.get_template("MAA_six_star.html")
@@ -2086,7 +2044,6 @@ class MaaManager:
# 发送全局通知
if Config.get("Notify", "IfSendSixStar"):
if Config.get("Notify", "IfSendMail"):
await Notify.send_mail(
"网页", title, message_html, Config.get("Notify", "ToAddress")
@@ -2107,7 +2064,6 @@ class MaaManager:
if self.cur_user_data.get("Notify", "Enabled") and self.cur_user_data.get(
"Notify", "IfSendSixStar"
):
# 发送邮件通知
if self.cur_user_data.get("Notify", "IfSendMail"):
await Notify.send_mail(

View File

@@ -1 +1,630 @@
.log-entry.error .log-message {
<template>
<div class="backend-launch-page">
<div class="section">
<h3 class="section-title">🚀 后端服务控制</h3>
<!-- 后端状态显示 -->
<div class="status-card" :class="{ running: isBackendRunning, stopped: !isBackendRunning }">
<div class="status-indicator">
<span class="status-dot" :class="{ active: isBackendRunning }"></span>
<span class="status-text">
{{ isBackendRunning ? '运行中' : '已停止' }}
</span>
</div>
<div v-if="backendPid" class="pid-info">
PID: {{ backendPid }}
</div>
</div>
<!-- 控制按钮 -->
<div class="action-buttons">
<button
@click="startBackend"
:disabled="isLoading || isBackendRunning"
class="action-btn start-btn"
>
<span v-if="isLoading" class="loading-spinner"></span>
<span v-else></span>
启动后端
</button>
<button
@click="stopBackend"
:disabled="isLoading || !isBackendRunning"
class="action-btn stop-btn"
>
<span v-if="isLoading" class="loading-spinner"></span>
<span v-else></span>
停止后端
</button>
<button
@click="refreshStatus"
:disabled="isLoading"
class="action-btn refresh-btn"
>
<span v-if="isLoading" class="loading-spinner"></span>
<span v-else>🔄</span>
刷新状态
</button>
</div>
<!-- 操作结果显示 -->
<div v-if="lastResult" class="result-card" :class="{ success: lastResult.success, error: !lastResult.success }">
<div class="result-title">
{{ lastResult.success ? '✅ 操作成功' : '❌ 操作失败' }}
</div>
<div v-if="lastResult.message" class="result-message">
{{ lastResult.message }}
</div>
<div v-if="lastResult.error" class="result-error">
错误: {{ lastResult.error }}
</div>
</div>
</div>
<!-- 进程信息 -->
<div class="section">
<h3 class="section-title">📊 进程信息</h3>
<div class="process-info">
<div class="info-row">
<span class="info-label">Python路径:</span>
<span class="info-value">{{ pythonPath || '未检测到' }}</span>
</div>
<div class="info-row">
<span class="info-label">主文件:</span>
<span class="info-value">{{ mainPyPath || '未检测到' }}</span>
</div>
<div class="info-row">
<span class="info-label">工作目录:</span>
<span class="info-value">{{ workingDir || '未知' }}</span>
</div>
</div>
<button @click="getProcessInfo" :disabled="isLoading" class="action-btn info-btn">
<span v-if="isLoading" class="loading-spinner"></span>
<span v-else>🔍</span>
获取进程信息
</button>
</div>
<!-- 快速操作 -->
<div class="section">
<h3 class="section-title"> 快速操作</h3>
<div class="quick-actions">
<button @click="restartBackend" :disabled="isLoading" class="action-btn restart-btn">
<span v-if="isLoading" class="loading-spinner"></span>
<span v-else>🔄</span>
重启后端
</button>
<button @click="forceKillProcesses" :disabled="isLoading" class="action-btn kill-btn">
<span v-if="isLoading" class="loading-spinner"></span>
<span v-else>💀</span>
强制结束所有进程
</button>
</div>
</div>
<!-- 日志区域 -->
<div class="section">
<h3 class="section-title">📝 操作日志</h3>
<div class="log-container">
<div v-if="logs.length === 0" class="no-logs">
暂无日志记录
</div>
<div v-else class="log-entries">
<div
v-for="(log, index) in logs"
:key="index"
class="log-entry"
:class="log.type"
>
<span class="log-time">{{ log.time }}</span>
<span class="log-message">{{ log.message }}</span>
</div>
</div>
</div>
<button @click="clearLogs" class="action-btn clear-btn">
🗑 清空日志
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
// 临时的类型断言确保能访问到完整的electronAPI
const electronAPI = (window as any).electronAPI
// 状态管理
const isBackendRunning = ref(false)
const isLoading = ref(false)
const backendPid = ref<number | null>(null)
const lastResult = ref<{ success: boolean; message?: string; error?: string } | null>(null)
// 进程信息
const pythonPath = ref<string>('')
const mainPyPath = ref<string>('')
const workingDir = ref<string>('')
// 日志管理
const logs = ref<Array<{ time: string; message: string; type: 'info' | 'success' | 'error' }>>([])
// 添加日志
const addLog = (message: string, type: 'info' | 'success' | 'error' = 'info') => {
const now = new Date()
const time = now.toLocaleTimeString()
logs.value.unshift({ time, message, type })
// 限制日志数量
if (logs.value.length > 50) {
logs.value = logs.value.slice(0, 50)
}
}
// 清空日志
const clearLogs = () => {
logs.value = []
addLog('日志已清空', 'info')
}
// 启动后端
const startBackend = async () => {
if (isLoading.value) return
isLoading.value = true
lastResult.value = null
addLog('正在启动后端服务...', 'info')
try {
const result = await electronAPI.startBackend()
if (result.success) {
lastResult.value = { success: true, message: '后端服务启动成功' }
addLog('✅ 后端服务启动成功', 'success')
await refreshStatus()
} else {
lastResult.value = { success: false, error: result.error }
addLog(`❌ 后端服务启动失败: ${result.error}`, 'error')
}
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
lastResult.value = { success: false, error: errorMsg }
addLog(`❌ 启动后端时出现异常: ${errorMsg}`, 'error')
} finally {
isLoading.value = false
}
}
// 停止后端
const stopBackend = async () => {
if (isLoading.value) return
isLoading.value = true
lastResult.value = null
addLog('正在停止后端服务...', 'info')
try {
// 检查stopBackend方法是否存在
if (electronAPI.stopBackend) {
const result = await electronAPI.stopBackend()
if (result.success) {
lastResult.value = { success: true, message: '后端服务已停止' }
addLog('✅ 后端服务已停止', 'success')
await refreshStatus()
} else {
lastResult.value = { success: false, error: result.error }
addLog(`❌ 停止后端服务失败: ${result.error}`, 'error')
}
} else {
// 如果没有stopBackend方法使用强制结束进程的方式
addLog(' 使用强制结束进程的方式停止后端', 'info')
await forceKillProcesses()
}
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
lastResult.value = { success: false, error: errorMsg }
addLog(`❌ 停止后端时出现异常: ${errorMsg}`, 'error')
} finally {
isLoading.value = false
}
}
// 重启后端
const restartBackend = async () => {
if (isLoading.value) return
addLog('正在重启后端服务...', 'info')
// 先停止
if (isBackendRunning.value) {
await stopBackend()
// 等待一秒确保完全停止
await new Promise(resolve => setTimeout(resolve, 1000))
}
// 再启动
await startBackend()
}
// 强制结束所有相关进程
const forceKillProcesses = async () => {
if (isLoading.value) return
isLoading.value = true
addLog('正在强制结束所有相关进程...', 'info')
try {
const result = await electronAPI.killAllProcesses()
if (result.success) {
lastResult.value = { success: true, message: '所有相关进程已强制结束' }
addLog('✅ 所有相关进程已强制结束', 'success')
await refreshStatus()
} else {
lastResult.value = { success: false, error: result.error }
addLog(`❌ 强制结束进程失败: ${result.error}`, 'error')
}
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
lastResult.value = { success: false, error: errorMsg }
addLog(`❌ 强制结束进程时出现异常: ${errorMsg}`, 'error')
} finally {
isLoading.value = false
}
}
// 刷新状态
const refreshStatus = async () => {
if (isLoading.value) return
isLoading.value = true
addLog('正在刷新后端状态...', 'info')
try {
// 获取相关进程信息
const processes = await electronAPI.getRelatedProcesses()
// 检查是否有Python进程在运行main.py
const backendProcess = processes.find((proc: any) =>
proc.command && proc.command.includes('main.py')
)
if (backendProcess) {
isBackendRunning.value = true
backendPid.value = backendProcess.pid
addLog(`✅ 检测到后端进程 (PID: ${backendProcess.pid})`, 'success')
} else {
isBackendRunning.value = false
backendPid.value = null
addLog(' 未检测到后端进程', 'info')
}
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
addLog(`❌ 刷新状态失败: ${errorMsg}`, 'error')
} finally {
isLoading.value = false
}
}
// 获取进程信息
const getProcessInfo = async () => {
if (isLoading.value) return
isLoading.value = true
addLog('正在获取进程信息...', 'info')
try {
// 这里可以调用一些API来获取Python路径等信息
// 暂时使用模拟数据
pythonPath.value = 'environment/python/python.exe'
mainPyPath.value = 'main.py'
workingDir.value = window.location.origin
addLog('✅ 进程信息获取完成', 'success')
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
addLog(`❌ 获取进程信息失败: ${errorMsg}`, 'error')
} finally {
isLoading.value = false
}
}
// 定时刷新状态
let statusInterval: NodeJS.Timeout | null = null
onMounted(() => {
addLog('📱 后端控制面板已加载', 'info')
// 初始化时获取状态
refreshStatus()
getProcessInfo()
// 每5秒自动刷新状态
statusInterval = setInterval(() => {
refreshStatus()
}, 5000)
})
onUnmounted(() => {
if (statusInterval) {
clearInterval(statusInterval)
}
})
</script>
<style scoped>
.backend-launch-page {
font-size: 11px;
line-height: 1.4;
}
.section {
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.section:last-child {
border-bottom: none;
margin-bottom: 0;
}
.section-title {
margin: 0 0 8px 0;
font-size: 12px;
font-weight: bold;
color: #4caf50;
}
/* 状态卡片 */
.status-card {
padding: 8px;
border-radius: 4px;
margin-bottom: 8px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.status-card.running {
background: rgba(76, 175, 80, 0.1);
border-color: rgba(76, 175, 80, 0.3);
}
.status-card.stopped {
background: rgba(244, 67, 54, 0.1);
border-color: rgba(244, 67, 54, 0.3);
}
.status-indicator {
display: flex;
align-items: center;
gap: 6px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #f44336;
animation: pulse 2s infinite;
}
.status-dot.active {
background: #4caf50;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.status-text {
font-weight: bold;
}
.pid-info {
margin-top: 4px;
font-size: 10px;
color: #888;
}
/* 按钮样式 */
.action-buttons {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 8px;
}
.quick-actions {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.action-btn {
padding: 6px 8px;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 4px;
background: rgba(255, 255, 255, 0.1);
color: #fff;
cursor: pointer;
font-size: 10px;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 4px;
}
.action-btn:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-1px);
}
.action-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.start-btn:hover:not(:disabled) {
background: rgba(76, 175, 80, 0.3);
border-color: rgba(76, 175, 80, 0.5);
}
.stop-btn:hover:not(:disabled) {
background: rgba(244, 67, 54, 0.3);
border-color: rgba(244, 67, 54, 0.5);
}
.restart-btn:hover:not(:disabled) {
background: rgba(255, 193, 7, 0.3);
border-color: rgba(255, 193, 7, 0.5);
}
.kill-btn:hover:not(:disabled) {
background: rgba(156, 39, 176, 0.3);
border-color: rgba(156, 39, 176, 0.5);
}
.loading-spinner {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* 结果卡片 */
.result-card {
padding: 8px;
border-radius: 4px;
margin-bottom: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.result-card.success {
background: rgba(76, 175, 80, 0.1);
border-color: rgba(76, 175, 80, 0.3);
}
.result-card.error {
background: rgba(244, 67, 54, 0.1);
border-color: rgba(244, 67, 54, 0.3);
}
.result-title {
font-weight: bold;
margin-bottom: 4px;
}
.result-message, .result-error {
font-size: 10px;
line-height: 1.3;
}
.result-error {
color: #ff6b6b;
}
/* 进程信息 */
.process-info {
margin-bottom: 8px;
}
.info-row {
display: flex;
margin-bottom: 4px;
font-size: 10px;
}
.info-label {
width: 60px;
color: #888;
flex-shrink: 0;
}
.info-value {
color: #fff;
word-break: break-all;
}
/* 日志区域 */
.log-container {
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 4px;
max-height: 120px;
overflow-y: auto;
margin-bottom: 8px;
}
.no-logs {
padding: 8px;
text-align: center;
color: #888;
font-size: 10px;
}
.log-entries {
padding: 4px;
}
.log-entry {
padding: 2px 4px;
font-size: 9px;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
display: flex;
gap: 6px;
}
.log-entry:last-child {
border-bottom: none;
}
.log-entry.success {
color: #4caf50;
}
.log-entry.error {
color: #f44336;
}
.log-entry.info {
color: #888;
}
.log-time {
color: #666;
font-size: 8px;
min-width: 60px;
}
.log-message {
flex: 1;
word-break: break-all;
}
.log-container::-webkit-scrollbar {
width: 3px;
}
.log-container::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
.log-container::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
}
</style>

View File

@@ -74,7 +74,7 @@ import { onMounted, onUnmounted, ref } from 'vue'
import { useWebSocket } from '@/composables/useWebSocket'
import { logger } from '@/utils/logger'
const { subscribe, unsubscribe, sendRaw, getConnectionInfo } = useWebSocket()
const { subscribe, unsubscribe, getConnectionInfo } = useWebSocket()
// 测试状态
const isTesting = ref(false)
@@ -165,27 +165,39 @@ const directTriggerModal = () => {
}, 1000)
}
// 发送WebSocket消息来模拟接收消息
// 直接调用弹窗API测试功能
const simulateMessage = (messageData: any) => {
logger.info('[调试工具] 发送模拟消息:', messageData)
// 检查连接状态
const connInfo = getConnectionInfo()
if (connInfo.status !== '已连接') {
logger.warn('[调试工具] WebSocket未连接无法发送消息')
lastResponse.value = '发送失败: WebSocket未连接'
return
}
logger.info('[调试工具] 直接测试弹窗功能:', messageData)
try {
// 使用sendRaw直接发送Message类型的消息
sendRaw('Message', messageData)
// 检查是否在Electron环境
if (typeof window !== 'undefined' && (window as any).electronAPI?.showQuestionDialog) {
// 直接调用Electron的弹窗API进行测试
(window as any).electronAPI.showQuestionDialog({
title: messageData.title || '测试标题',
message: messageData.message || '测试消息',
options: messageData.options || ['确定', '取消'],
messageId: messageData.message_id || 'test-' + Date.now()
}).then((result: boolean) => {
logger.info('[调试工具] 弹窗测试结果:', result)
const choice = result ? '确认' : '取消'
lastResponse.value = `用户选择: ${choice}`
addTestHistory('弹窗测试', choice)
}).catch((error: any) => {
logger.error('[调试工具] 弹窗测试失败:', error)
lastResponse.value = '弹窗测试失败: ' + (error?.message || '未知错误')
})
} else {
logger.warn('[调试工具] 不在Electron环境中或API不可用使用浏览器confirm作为备用')
const result = confirm(`${messageData.title || '测试'}\n\n${messageData.message || '这是测试消息'}`)
const choice = result ? '确认' : '取消'
lastResponse.value = `用户选择: ${choice} (浏览器备用)`
addTestHistory('浏览器备用测试', choice)
}
logger.info('[调试工具] 消息已发送到WebSocket')
lastResponse.value = '消息已发送,等待弹窗显示...'
} catch (error: any) {
logger.error('[调试工具] 发送消息失败:', error)
lastResponse.value = '发送失败: ' + (error?.message || '未知错误')
logger.error('[调试工具] 测试弹窗失败:', error)
lastResponse.value = '测试失败: ' + (error?.message || '未知错误')
}
}

View File

@@ -44,22 +44,28 @@ import RouteInfoPage from './RouteInfoPage.vue'
import EnvironmentPage from './EnvironmentPage.vue'
import QuickNavPage from './QuickNavPage.vue'
import MessageTestPage from './MessageTestPage.vue'
import BackendLaunchPage from './BackendLaunchPage.vue'
// 调试页面配置
const tabs = [
{ key: 'route', title: '路由', icon: '🛣️', component: RouteInfoPage },
{ key: 'env', title: '环境', icon: '⚙️', component: EnvironmentPage },
{ key: 'nav', title: '导航', icon: '🚀', component: QuickNavPage },
{ key: 'backend', title: '后端', icon: '🚀', component: BackendLaunchPage },
{ key: 'nav', title: '导航', icon: '🧭', component: QuickNavPage },
{ key: 'message', title: '消息', icon: '💬', component: MessageTestPage },
]
// 开发环境检测
const isDev = ref(process.env.NODE_ENV === 'development' || import.meta.env?.DEV === true)
const isDev = ref(
process.env.NODE_ENV === 'development' ||
(import.meta as any).env?.DEV === true ||
window.location.hostname === 'localhost'
)
// 面板状态
const isCollapsed = ref(false)
const isDragging = ref(false)
const activeTab = ref('route')
const activeTab = ref('backend') // 默认显示后端页面
// 面板位置
const panelPosition = ref({