diff --git a/launcher/file/download.py b/launcher/file/download.py index ccda67e..2acf26a 100644 --- a/launcher/file/download.py +++ b/launcher/file/download.py @@ -5,7 +5,7 @@ import requests from launcher.file.extract import extract_7z_file from launcher.file.utils import format_size -from launcher.webview.events import custom_event +from launcher.webview.events import custom_event, LogType def download_file(download_name, download_url, destination_folder): @@ -21,7 +21,7 @@ def download_file(download_name, download_url, destination_folder): filename = os.path.basename(download_url) download_path = os.path.join(destination_folder, filename) - custom_event(f"开始下载: {download_name}") + custom_event(LogType.info, f"开始下载: {download_name}") response = requests.get(download_url, stream=True) if response.status_code == 200: @@ -52,8 +52,8 @@ def download_file(download_name, download_url, destination_folder): formatted_total_size = format_size(total_size) formatted_speed = format_size(download_speed) + "/s" - custom_event( - f"下载进度: {progress_percent:.2f}% ({formatted_downloaded_size}/{formatted_total_size}), 下载速度: {formatted_speed}") + custom_event(LogType.info, + f"下载进度: {progress_percent:.2f}% ({formatted_downloaded_size}/{formatted_total_size}), 下载速度: {formatted_speed}") last_update_time = current_time # 更新上次更新时间 end_time = time.time() @@ -63,10 +63,10 @@ def download_file(download_name, download_url, destination_folder): formatted_total_elapsed_time = f"{total_elapsed_time:.2f} 秒" formatted_average_download_speed = format_size(average_download_speed) + "/s" - custom_event( - f"下载完成: {filename}, 耗时: {formatted_total_elapsed_time}, 下载速度: {formatted_average_download_speed}") + custom_event(LogType.info, + f"下载完成: {filename}, 耗时: {formatted_total_elapsed_time}, 下载速度: {formatted_average_download_speed}") else: - custom_event(f"下载失败: {response.status_code}") + custom_event(LogType.error, f"下载失败: {response.status_code}") return False return True @@ -84,7 +84,7 @@ def init_download(download_name, download_url, destination_folder): def download(): target_folder = os.path.join(download_name) if os.path.exists(target_folder): - custom_event(f"{download_name} 文件夹已存在,跳过下载") + custom_event(LogType.info, f"{download_name} 文件夹已存在,跳过下载") return True filename = os.path.basename(download_url) diff --git a/launcher/file/extract.py b/launcher/file/extract.py index 17269a2..1588c41 100644 --- a/launcher/file/extract.py +++ b/launcher/file/extract.py @@ -5,7 +5,7 @@ from py7zr import py7zr from py7zr.callbacks import ExtractCallback from launcher.file.utils import format_size -from launcher.webview.events import custom_event +from launcher.webview.events import custom_event, LogType class MyExtractCallback(ExtractCallback): @@ -24,7 +24,7 @@ class MyExtractCallback(ExtractCallback): self.total_size += int(wrote_bytes) current_time = time.time() if current_time - self.last_print_time >= 1.0: # 至少每隔1秒输出一次 - custom_event(f"已解压: {format_size(self.total_size)}") + custom_event(LogType.info, f"已解压: {format_size(self.total_size)}") self.last_print_time = current_time def report_postprocess(self): @@ -49,7 +49,7 @@ def extract_7z_file(file_name, file_path, destination_folder, delete_after_extra if not os.path.exists(destination_folder): os.makedirs(destination_folder) - custom_event(f"开始解压文件: {file_name}") + custom_event(LogType.info, f"开始解压文件: {file_name}") try: start_time = time.time() with py7zr.SevenZipFile(file_path, mode='r') as z: @@ -58,18 +58,17 @@ def extract_7z_file(file_name, file_path, destination_folder, delete_after_extra end_time = time.time() total_elapsed_time = end_time - start_time formatted_total_elapsed_time = f"{total_elapsed_time:.2f} 秒" - custom_event( - f"解压完成: {file_name}, 总大小: {format_size(callback.total_size)}, 耗时: {formatted_total_elapsed_time}") + custom_event(LogType.info, + f"解压完成: {file_name}, 总大小: {format_size(callback.total_size)}, 耗时: {formatted_total_elapsed_time}") except Exception as e: - e.print_exc() - custom_event(f"解压失败: {str(e)}") + custom_event(LogType.error, f"解压失败: {repr(e)}") return False if delete_after_extract: try: os.remove(file_path) - custom_event(f"删除{file_path}成功") + custom_event(LogType.info, f"删除{file_path}成功") except OSError as e: - custom_event(f"删除{file_path}失败: {str(e)}") + custom_event(LogType.error, f"删除{file_path}失败: {repr(e)}") return True diff --git a/launcher/sys_config/__init__.py b/launcher/sys_config/__init__.py index f81abf3..94853c0 100644 --- a/launcher/sys_config/__init__.py +++ b/launcher/sys_config/__init__.py @@ -47,7 +47,7 @@ class SysConfig: # logger.error(f"配置文件未找到: {self.config_path}") except Exception: pass - # logger.error(f"加载配置文件时出错: {str(e)}") + # logger.error(f"加载配置文件时出错: {repr(e)}") def get(self, key): return self.config.get(key) diff --git a/launcher/webview/api.py b/launcher/webview/api.py index fae921a..c67ee37 100644 --- a/launcher/webview/api.py +++ b/launcher/webview/api.py @@ -17,7 +17,7 @@ from launcher.file.extract import extract_7z_file from launcher.file.utils import ensure_directory_exists from launcher.log import logger from launcher.sys_config import sys_config -from launcher.webview.events import custom_event +from launcher.webview.events import custom_event, LogType command_list = { "download_git": lambda: init_download("git", download_git_url, os.getcwd()), @@ -35,11 +35,11 @@ command_list = { } -def read_stream(stream, log_func): +def read_stream(stream, log_type): def process_lines(text_io): for line in iter(text_io.readline, ''): text = line.rstrip('\n') - custom_event(f"{text.strip()}\n") + custom_event(log_type, f"{text.strip()}") detected_encoding = 'utf-8' text_io = io.TextIOWrapper(stream, encoding=detected_encoding, errors='replace') @@ -128,14 +128,16 @@ class Api: command = command() if callable(command): return "success" if command() else "failed" - custom_event(command + "\n") + if cwd is not None: + custom_event(LogType.execute_command, f"路径:{cwd}") + custom_event(LogType.execute_command, command) try: with subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=cwd, bufsize=0, universal_newlines=False ) as p: - stdout_thread = threading.Thread(target=read_stream, args=(p.stdout, logger.info)) - stderr_thread = threading.Thread(target=read_stream, args=(p.stderr, logger.error)) + stdout_thread = threading.Thread(target=read_stream, args=(p.stdout, LogType.command_out)) + stderr_thread = threading.Thread(target=read_stream, args=(p.stderr, LogType.command_out)) stdout_thread.start() stderr_thread.start() @@ -149,4 +151,4 @@ class Api: return "failed" except Exception as e: logger.exception(e) - custom_event(str(e)) + custom_event(LogType.error, repr(e)) diff --git a/launcher/webview/events.py b/launcher/webview/events.py index a28fd4f..cb4d714 100644 --- a/launcher/webview/events.py +++ b/launcher/webview/events.py @@ -1,11 +1,32 @@ import json +from enum import Enum import launcher from launcher.log import logger -def custom_event(data): - logger.info(data) +class LogType(Enum): + info = "信息" + error = "错误" + execute_command = "执行命令" + command_out = "命令输出" + + +def custom_event(log_type, data): + data = f"[{log_type.value}] {data}" + + match log_type: + case LogType.info: + logger.info(data) + case LogType.error: + logger.error(data) + case LogType.execute_command: + logger.info(data) + case LogType.command_out: + logger.info(data) + case _: + logger.info(data) + data = json.dumps({"log": data + "\n"}) js = f"var event = new CustomEvent('log', {{detail: {data}}}); window.dispatchEvent(event);" launcher.webview.window.evaluate_js(js) diff --git a/ui/package-lock.json b/ui/package-lock.json index 3e30e87..7010b68 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -8,6 +8,7 @@ "name": "ui", "version": "0.0.0", "dependencies": { + "highlight.js": "^11.11.1", "pinia": "^2.2.8", "vue": "^3.5.11" }, @@ -2075,11 +2076,9 @@ } }, "node_modules/highlight.js": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.10.0.tgz", - "integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==", - "dev": true, - "license": "BSD-3-Clause", + "version": "11.11.1", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", "engines": { "node": ">=12.0.0" } diff --git a/ui/package.json b/ui/package.json index 4e707b9..ccf202c 100644 --- a/ui/package.json +++ b/ui/package.json @@ -11,6 +11,7 @@ "format": "prettier --write src/" }, "dependencies": { + "highlight.js": "^11.11.1", "pinia": "^2.2.8", "vue": "^3.5.11" }, diff --git a/ui/src/components/LogComponent.vue b/ui/src/components/LogComponent.vue index 8b19c0d..7fc5d72 100644 --- a/ui/src/components/LogComponent.vue +++ b/ui/src/components/LogComponent.vue @@ -1,14 +1,40 @@ @@ -23,10 +49,27 @@ const log_ele = inject('log_ele') height: 100% !important; box-sizing: border-box; } + +.selectable-log { + user-select: text; +}