const { app, BrowserWindow, ipcMain, dialog, Menu } = require('electron'); const path = require('path'); const fs = require('fs-extra'); let isDev; import('electron-is-dev').then(module => { isDev = module.default; }).catch(err => { console.error('Failed to load electron-is-dev:', err); isDev = false; }); const sqlite3 = require('sqlite3').verbose(); const ExcelService = require('./src/services/ExcelService'); // 保持对window对象的全局引用,避免JavaScript对象被垃圾回收时,窗口被自动关闭 let mainWindow; // 数据库连接 let db; function createWindow() { // 创建浏览器窗口 mainWindow = new BrowserWindow({ width: 1200, height: 800, title: '值班助手工具', webPreferences: { nodeIntegration: false, contextIsolation: true, preload: path.join(__dirname, 'preload.js') } }); // 加载应用 const loadAppUrl = () => { const devUrl = 'http://localhost:3000'; const prodUrl = `file://${path.join(__dirname, 'build/index.html')}`; const url = isDev ? devUrl : prodUrl; mainWindow.loadURL(url).catch((err) => { if (isDev) { console.log(`首次加载失败,10秒后重试...`); let retries = 3; const retryLoad = () => { mainWindow.loadURL(devUrl).catch((retryErr) => { if (retries-- > 0) { console.log(`剩余重试次数:${retries}`); setTimeout(retryLoad, 10000); } else { mainWindow.loadURL(`data:text/html,

无法连接开发服务器,请检查:

  1. React开发服务器是否启动(npm run react-start)
  2. 端口3000是否被占用
  3. 网络连接是否正常
`); } }); }; setTimeout(retryLoad, 10000); } else { mainWindow.loadURL(prodUrl).catch(() => { mainWindow.loadURL(`data:text/html,

生产文件加载失败,请重新编译(npm run react-build)

`); }); } }); }; // 开发模式立即尝试加载,生产模式直接加载 if (isDev) { setTimeout(loadAppUrl, 3000); // 给开发服务器更多启动时间 } else { loadAppUrl(); } // 开发者工具已禁用 // 当window被关闭时,触发下面的事件 mainWindow.on('closed', () => { mainWindow = null; // 关闭数据库连接 if (db) { db.close(); } }); } // 初始化数据库 function initDatabase() { const userDataPath = app.getPath('userData'); const dbPath = path.join(userDataPath, 'datatools.db'); // 确保目录存在 fs.ensureDirSync(path.dirname(dbPath)); // 连接数据库 db = new sqlite3.Database(dbPath); // 创建表(如果不存在) db.serialize(() => { db.run(` CREATE TABLE IF NOT EXISTS projects ( id INTEGER PRIMARY KEY AUTOINCREMENT, unit TEXT, -- 单位 project_number TEXT, -- 项目编号 safety_code TEXT, -- 安全编码 major_project_name TEXT, -- 大项工程名称 sub_project_name TEXT, -- 单项工程名称 construction_scope TEXT, -- 在施工程作业范围 project_scale TEXT, -- 工程规模 safety_director TEXT, -- 安全总监 construction_unit TEXT, -- 建设单位 supervision_unit TEXT, -- 监理单位 construction_company TEXT, -- 施工单位 project_location TEXT, -- 工程位置 actual_start_time TEXT, -- 实际开工时间 planned_completion_time TEXT, -- 计划竣工时间 current_progress TEXT, -- 当前工程进度 current_status TEXT, -- 当前工程状态 participants_count INTEGER, -- 参建人数 new_team_count INTEGER, -- 新班组进场数量 new_person_count INTEGER, -- 新人进场数量 leader_info TEXT, -- 带班人姓名、电话 next_week_plan TEXT, -- 下周作业计划 next_week_condition TEXT, -- 下周8+2工况内容 is_schedule_tight BOOLEAN, -- 工期是否紧张 has_off_book_matters BOOLEAN, -- 是否存在"账外事" current_risk_level TEXT, -- 当前风险等级 risk_judgment_reason TEXT, -- 当前风险判断理由 risk_tips TEXT, -- 隐患提示/工作要求 completion_time TEXT, -- 完成时间 next_review_time TEXT, -- 下次梳理时间 remarks TEXT -- 备注 ) `); db.run(` CREATE TABLE IF NOT EXISTS tree_structure ( id INTEGER PRIMARY KEY AUTOINCREMENT, headquarters TEXT, -- 总部 unit TEXT, -- 单位 construction_unit TEXT, -- 建设单位 parent_id INTEGER, -- 父节点ID node_level INTEGER, -- 节点级别(1:总部, 2:单位, 3:建设单位) FOREIGN KEY (parent_id) REFERENCES tree_structure(id) ) `); db.run(`CREATE INDEX IF NOT EXISTS idx_unit ON projects(unit)`); db.run(`CREATE INDEX IF NOT EXISTS idx_construction_unit ON projects(construction_unit)`); db.run(`CREATE INDEX IF NOT EXISTS idx_sub_project_name ON projects(sub_project_name)`); db.run(`CREATE INDEX IF NOT EXISTS idx_current_risk_level ON projects(current_risk_level)`); }); return db; } // 创建中文菜单模板 const menuTemplate = [ { label: '文件', submenu: [ { role: 'quit', label: '退出' } ] }, { label: '编辑', submenu: [ { role: 'undo', label: '撤销' }, { role: 'redo', label: '恢复' }, { type: 'separator' }, { role: 'cut', label: '剪切' }, { role: 'copy', label: '复制' }, { role: 'paste', label: '粘贴' }, { role: 'delete', label: '删除' }, { role: 'selectAll', label: '全选' } ] }, { label: '视图', submenu: [ { role: 'reload', label: '重新加载' }, { role: 'forceReload', label: '强制重新加载' }, { role: 'toggleDevTools', label: '开发者工具' }, { type: 'separator' }, { role: 'resetZoom', label: '重置缩放' }, { role: 'zoomIn', label: '放大' }, { role: 'zoomOut', label: '缩小' }, { type: 'separator' }, { role: 'togglefullscreen', label: '全屏' } ] }, { label: '窗口', submenu: [ { role: 'minimize', label: '最小化' }, { role: 'zoom', label: '缩放' }, { type: 'separator' }, { role: 'front', label: '前置所有窗口' } ] }, { label: '帮助', submenu: [ { label: '关于', click: () => { dialog.showMessageBox({ title: '关于', message: '值班助手工具 v1.0.0' }) } } ] } ]; // 当Electron完成初始化并准备创建浏览器窗口时调用此方法 app.whenReady().then(() => { // 设置中文菜单 const menu = Menu.buildFromTemplate(menuTemplate); Menu.setApplicationMenu(menu); createWindow(); db = initDatabase(); // 初始化Excel服务 const excelService = new ExcelService(db); // 注册import-excel事件处理器(确保只注册一次) ipcMain.removeHandler('import-excel'); // 先移除已有的处理器 ipcMain.handle('import-excel', async (event, filePath) => { try { console.log('处理Excel导入请求...'); return await excelService.importExcel(filePath); } catch (error) { console.error('导入Excel错误:', error); return { success: false, error: error.message }; } }); app.on('activate', () => { // 在macOS上,当点击dock图标并且没有其他窗口打开时,通常在应用程序中重新创建一个窗口 if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); }); // 当所有窗口关闭时退出应用 app.on('window-all-closed', () => { // 在macOS上,除非用户用Cmd + Q确定地退出,否则绝大部分应用及其菜单栏会保持激活 if (process.platform !== 'darwin') { app.quit(); } }); // 处理Excel文件选择 ipcMain.handle('select-excel-file', async () => { const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, { properties: ['openFile'], filters: [ { name: 'Excel Files', extensions: ['xlsx', 'xls'] } ] }); if (canceled) { return null; } return filePaths[0]; }); // 获取数据库中的项目数据 ipcMain.handle('get-projects', () => { return new Promise((resolve, reject) => { db.all('SELECT * FROM projects', (err, rows) => { if (err) { console.error('获取项目数据失败:', err); reject(err); } else { resolve(rows || []); } }); }); }); // 获取树状结构数据 ipcMain.handle('get-tree-structure', () => { return new Promise((resolve, reject) => { db.all('SELECT * FROM tree_structure ORDER BY node_level, id', (err, rows) => { if (err) { console.error('获取树状结构数据失败:', err); reject(err); } else { // 转换为Ant Design Tree组件所需的格式 const treeData = []; const nodeMap = {}; // 首先找到总部节点 const headquartersNodes = rows.filter(node => node.node_level === 1); // 处理每个总部节点 headquartersNodes.forEach(hqNode => { const headquartersTreeNode = { key: `headquarters-${hqNode.id}`, title: hqNode.headquarters, level: 1, children: [] }; // 添加到树数据和节点映射 treeData.push(headquartersTreeNode); nodeMap[hqNode.id] = headquartersTreeNode; // 找到该总部下的所有单位节点 const unitNodes = rows.filter(node => node.node_level === 2 && node.parent_id === hqNode.id ); // 处理每个单位节点 unitNodes.forEach(unitNode => { const unitTreeNode = { key: `unit-${unitNode.unit}`, title: unitNode.unit, level: 2, children: [] }; // 添加到总部节点的子节点和节点映射 headquartersTreeNode.children.push(unitTreeNode); nodeMap[unitNode.id] = unitTreeNode; // 找到该单位下的所有建设单位节点 const constructionNodes = rows.filter(node => node.node_level === 3 && node.parent_id === unitNode.id ); // 处理每个建设单位节点 constructionNodes.forEach(constructionNode => { const constructionTreeNode = { key: `construction-${unitNode.unit}-${constructionNode.construction_unit}`, title: constructionNode.construction_unit, level: 3, isLeaf: true }; // 添加到单位节点的子节点 unitTreeNode.children.push(constructionTreeNode); }); }); }); console.log('树状结构数据转换完成,节点数量:', treeData.length); resolve(treeData); } }); }); }); // 根据条件筛选项目数据 ipcMain.handle('filter-projects', (event, filters) => { return new Promise((resolve, reject) => { let query = 'SELECT * FROM projects WHERE 1=1'; const params = []; if (filters.unit) { query += ' AND unit = ?'; params.push(filters.unit); } if (filters.constructionUnit) { query += ' AND construction_unit = ?'; params.push(filters.constructionUnit); } if (filters.subProjectName) { query += ' AND sub_project_name LIKE ?'; params.push(`%${filters.subProjectName}%`); } if (filters.riskLevel) { query += ' AND current_risk_level = ?'; params.push(filters.riskLevel); } db.all(query, params, (err, rows) => { if (err) { console.error('筛选项目数据失败:', err); reject(err); } else { resolve(rows || []); } }); }); }); // 更新项目数据 ipcMain.handle('update-project', (event, project) => { return new Promise((resolve, reject) => { const query = ` UPDATE projects SET unit = ?, project_number = ?, safety_code = ?, major_project_name = ?, sub_project_name = ?, construction_scope = ?, project_scale = ?, safety_director = ?, construction_unit = ?, supervision_unit = ?, construction_company = ?, project_location = ?, actual_start_time = ?, planned_completion_time = ?, current_progress = ?, current_status = ?, participants_count = ?, new_team_count = ?, new_person_count = ?, leader_info = ?, next_week_plan = ?, next_week_condition = ?, is_schedule_tight = ?, has_off_book_matters = ?, current_risk_level = ?, risk_judgment_reason = ?, risk_tips = ?, completion_time = ?, next_review_time = ?, remarks = ? WHERE id = ? `; db.run(query, [ project.unit, project.project_number, project.safety_code, project.major_project_name, project.sub_project_name, project.construction_scope, project.project_scale, project.safety_director, project.construction_unit, project.supervision_unit, project.construction_company, project.project_location, project.actual_start_time, project.planned_completion_time, project.current_progress, project.current_status, project.participants_count, project.new_team_count, project.new_person_count, project.leader_info, project.next_week_plan, project.next_week_condition, project.is_schedule_tight, project.has_off_book_matters, project.current_risk_level, project.risk_judgment_reason, project.risk_tips, project.completion_time, project.next_review_time, project.remarks, project.id ], function(err) { if (err) { console.error('更新项目数据失败:', err); reject(err); } else { resolve({ success: true, changes: this.changes }); } }); }); });