import { shell } from 'electron'; import { os } from "../service/os/os"; import { localSettingsService } from "../service/setting/localSettings"; import path from 'node:path'; import fs from 'node:fs'; import { logger } from "ee-core/log"; /** 本机设置里存离线地图目录的键名 */ const MAP_LOCAL_KEY = 'mapLocal'; /** * 窗口控制(用于无边框窗口拖拽) * @class */ class OsCtl { /** * 最小化窗口 */ async minimizeWindow() { await os.minimizeWindow(); } /** * 最大化窗口 */ async maximizeWindow() { await os.maximizeWindow(); } /** * 取消最大化窗口 */ async unmaximizeWindow() { await os.unmaximizeWindow(); } /** * 关闭窗口 */ async closeWindow() { await os.closeWindow(); } /** * 是否最大化窗口 */ async isMaximized() { return await os.isMaximized(); } /** * 切换开发工具 */ async toggleDevTools() { await os.toggleDevTools(); } /** * 打开用户数据目录 */ async openUserDataFolder() { await os.openUserDataFolder(); } /** * 打开安装目录 */ async openInstallFolder() { await os.openInstallFolder(); } /** * 选择目录(返回目录路径) */ async selectDirectory() { return await os.selectDirectory(); } /** * 分块读本机文件(离线地图瓦片用;params: { filePath, offset?, length })。 * * 方法名沿用改造前前端已调用的 `readBinaryFileRange`;返回项目统一的 {success,data|message} 信封, * 读取失败(路径非法/文件不存在等)不抛到 Electron 层,调用方看 message 即可。 */ async readBinaryFileRange(params) { try { return { success: true, data: await os.readBinaryFileRange(params) }; } catch (e) { const msg = e instanceof Error ? e.message : String(e); logger.warn('[osCtl] readBinaryFileRange failed:', msg); return { success: false, message: msg }; } } /** * 读本机文本文件(离线地图样式用;params: { filePath, encoding? }) */ async readTextFile(params) { try { return { success: true, data: { text: await os.readTextFile(params) } }; } catch (e) { const msg = e instanceof Error ? e.message : String(e); logger.warn('[osCtl] readTextFile failed:', msg); return { success: false, message: msg }; } } /** * 读取本机离线地图目录(未设置为空串)。 * * 存在本机(local-settings.json),不走服务端:地图瓦片在本机时,后端可能部署在别的机器上。 */ async getMapLocalPath() { return { success: true, data: { path: localSettingsService.get(MAP_LOCAL_KEY) } }; } /** * 保存本机离线地图目录;params: { path },path 传空表示清空。 * * 校验与 openPath 一致:只接受绝对路径、必须已存在、且必须是目录。 */ async setMapLocalPath(params) { try { const target = typeof params?.path === 'string' ? params.path.trim() : ''; if (target) { if (!path.isAbsolute(target)) throw new Error('只支持绝对路径'); if (!fs.existsSync(target)) throw new Error(`目录不存在:${target}`); if (!fs.statSync(target).isDirectory()) throw new Error('请选择目录,而不是文件'); } const saved = localSettingsService.set(MAP_LOCAL_KEY, target); return { success: true, data: { path: saved } }; } catch (e) { const msg = e instanceof Error ? e.message : String(e); logger.warn('[osCtl] setMapLocalPath failed:', msg); return { success: false, message: msg }; } } /** * 获取应用信息 */ async getAppInfo() { return await os.getAppInfo(); } /** * 使用系统默认浏览器打开外部链接 */ async openExternal(url) { return await os.openExternal(url); } /** * 通过 Node.js 主进程流式上传清鉴数据源文件 * 在 controller 层捕获异常并返回结构化结果,避免异常被 Electron * 的 ipcMain.handle 包装成 "Error occurred in handler for ..." 前缀 * @param params { dirPath, uploadUrl, token } * @returns { success: boolean, data?, message? } */ async uploadQingJianFiles(params) { try { const data = await os.uploadQingJianFiles(params); return { success: true, data }; } catch (e) { const msg = e instanceof Error ? e.message : String(e); logger.error('[osCtl] uploadQingJianFiles failed:', msg); return { success: false, message: msg }; } } } OsCtl.toString = () => "OsCtl"; export default OsCtl;