diff --git a/main.js b/main.js index d4dad57..f4f5844 100644 --- a/main.js +++ b/main.js @@ -1,12 +1,12 @@ -const { app, BrowserWindow, ipcMain, dialog, Menu } = require('electron'); +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; + isDev = module.default; }).catch(err => { - console.error('Failed to load electron-is-dev:', err); - isDev = false; + console.error('Failed to load electron-is-dev:', err); + isDev = false; }); const sqlite3 = require('sqlite3').verbose(); const ExcelService = require('./src/services/ExcelService'); @@ -18,88 +18,88 @@ 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') - } - }); - - // 将mainWindow设为全局变量,以便其他模块可以访问 - global.mainWindow = mainWindow; + // 创建浏览器窗口 + 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设为全局变量,以便其他模块可以访问 + global.mainWindow = mainWindow; - 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,

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

+ // 加载应用 + 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)

`); + }); } - }); - }; - 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(); + // 开发模式立即尝试加载,生产模式直接加载 + 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(` + 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, -- 单位 @@ -131,11 +131,24 @@ function initDatabase() { risk_tips TEXT, -- 隐患提示/工作要求 completion_time TEXT, -- 完成时间 next_review_time TEXT, -- 下次梳理时间 - remarks TEXT -- 备注 + + new_team INTEGER, --新进班组数量 + new_members INTEGER, --新进班组骨干数量 + new_high_altitude INTEGER, --新进高空人员数量 + new_hired_general INTEGER, --新进一般人员数量 + new_homework_content TEXT, --新的作业内容 + change_homework_method TEXT, --改变作业方法 + changes_geographical TEXT, --地理环境的变化 + changes_meteorological TEXT, --气象环境的变化 + changes_social TEXT, --社会环境的变化 + changes_management TEXT, --管理要求的变化 + changes_homework_plan TEXT, --作业计划的变化 + changes_management_personnel TEXT, --管理人员的变化 + remarks TEXT -- 备注 ) `); - db.run(` + db.run(` CREATE TABLE IF NOT EXISTS tree_structure ( id INTEGER PRIMARY KEY AUTOINCREMENT, headquarters TEXT, -- 总部 @@ -147,298 +160,298 @@ function initDatabase() { ) `); - 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; + 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' - }) - } - } - ] - } + { + 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 }; - } - }); - - // 注册clear-all-data事件处理器 - ipcMain.removeHandler('clear-all-data'); // 先移除已有的处理器 - ipcMain.handle('clear-all-data', async () => { - try { - console.log('处理清除数据请求...'); - return new Promise((resolve, reject) => { - db.serialize(() => { - db.run('DELETE FROM projects', function(err) { - if (err) { - console.error('清除项目数据失败:', err); - reject({ success: false, error: err.message }); - return; - } - - db.run('DELETE FROM tree_structure', function(err) { - if (err) { - console.error('清除树状结构数据失败:', err); - reject({ success: false, error: err.message }); - return; - } - - // 重置自增ID - db.run('DELETE FROM sqlite_sequence WHERE name IN (\'projects\', \'tree_structure\')', function(err) { - if (err) { - console.error('重置自增ID失败:', err); - // 不影响主要功能,继续执行 - } - - resolve({ success: true, message: '所有数据已清除' }); - }); + // 设置中文菜单 + 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}; + } + }); + + // 注册clear-all-data事件处理器 + ipcMain.removeHandler('clear-all-data'); // 先移除已有的处理器 + ipcMain.handle('clear-all-data', async () => { + try { + console.log('处理清除数据请求...'); + return new Promise((resolve, reject) => { + db.serialize(() => { + db.run('DELETE FROM projects', function (err) { + if (err) { + console.error('清除项目数据失败:', err); + reject({success: false, error: err.message}); + return; + } + + db.run('DELETE FROM tree_structure', function (err) { + if (err) { + console.error('清除树状结构数据失败:', err); + reject({success: false, error: err.message}); + return; + } + + // 重置自增ID + db.run('DELETE FROM sqlite_sequence WHERE name IN (\'projects\', \'tree_structure\')', function (err) { + if (err) { + console.error('重置自增ID失败:', err); + // 不影响主要功能,继续执行 + } + + resolve({success: true, message: '所有数据已清除'}); + }); + }); + }); + }); }); - }); - }); - }); - } catch (error) { - console.error('清除数据错误:', error); - return { success: false, error: error.message }; - } - }); - - app.on('activate', () => { - // 在macOS上,当点击dock图标并且没有其他窗口打开时,通常在应用程序中重新创建一个窗口 - if (BrowserWindow.getAllWindows().length === 0) { - createWindow(); - } - }); + } catch (error) { + console.error('清除数据错误:', 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(); - } + // 在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]; + 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 || []); - } + 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); - }); - }); + 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); + } }); - - 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 || []); - } + 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 = ` + return new Promise((resolve, reject) => { + const query = ` UPDATE projects SET unit = ?, project_number = ?, @@ -469,49 +482,75 @@ ipcMain.handle('update-project', (event, project) => { risk_tips = ?, completion_time = ?, next_review_time = ?, + + new_team = ?, + new_members = ?, + new_high_altitude = ?, + new_hired_general = ?, + new_homework_content= ?, + change_homework_method = ?, + changes_geographical = ?, + changes_meteorological = ?, + changes_social = ?, + changes_management = ?, + changes_homework_plan = ?, + changes_management_personnel = ?, + 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 }); - } + + 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.new_team, + project.new_members, + project.new_high_altitude, + project.new_hired_general, + project.new_homework_content, + project.change_homework_method, + project.changes_geographical, + project.changes_meteorological, + project.changes_social, + project.changes_management, + project.changes_homework_plan, + project.changes_management_personnel, + project.remarks, + project.id + ], function (err) { + if (err) { + console.error('更新项目数据失败:', err); + reject(err); + } else { + resolve({success: true, changes: this.changes}); + } + }); }); - }); }); diff --git a/src/App.js b/src/App.js index 8f4040d..2fa5fe8 100644 --- a/src/App.js +++ b/src/App.js @@ -13,6 +13,7 @@ import Toolbar from './components/Toolbar'; import TreeView from './components/TreeView'; import DataView from './components/DataView'; import ProjectDetailForm from './components/ProjectDetailForm'; +import ProjectWarningView from "./components/ProjectWarningView"; function App() { // 状态管理 @@ -40,10 +41,10 @@ function App() { // 获取项目数据 const projectsData = await window.electronAPI.getProjects(); setProjects(projectsData); - + // 获取树状结构数据 const treeStructureData = await window.electronAPI.getTreeStructure(); - + // 如果树状结构为空,则从项目数据构建树状结构 if (treeStructureData.length === 0 && projectsData.length > 0) { const tree = buildTreeFromProjects(projectsData); @@ -67,14 +68,14 @@ function App() { level: 1, children: [] }; - + // 单位映射 const unitMap = {}; - + // 遍历项目数据,构建树状结构 projectsData.forEach(project => { const { unit, construction_unit } = project; - + // 如果单位不存在,则创建单位节点 if (!unitMap[unit]) { unitMap[unit] = { @@ -85,7 +86,7 @@ function App() { }; headquarters.children.push(unitMap[unit]); } - + // 如果建设单位不为空,则添加建设单位节点 if (construction_unit && !unitMap[unit].children.find(child => child.title === construction_unit)) { unitMap[unit].children.push({ @@ -96,7 +97,7 @@ function App() { }); } }); - + return [headquarters]; }; @@ -105,10 +106,10 @@ function App() { if (selectedKeys.length > 0) { const key = selectedKeys[0]; setSelectedNode(key); - + // 根据选中的节点筛选项目数据 let filters = {}; - + if (key === 'headquarters') { // 总部节点,显示所有数据 setProjects(projects); @@ -123,7 +124,7 @@ function App() { const constructionUnit = parts.slice(2).join('-'); filters = { unit, constructionUnit }; } - + // 应用筛选 filterProjects(filters); } @@ -145,14 +146,14 @@ function App() { // 处理工具栏筛选 const handleToolbarFilter = (filter) => { setActiveFilter(filter === activeFilter ? null : filter); - + // 根据筛选条件筛选项目数据 let filters = {}; - + if (filter === 'risk') { filters = { riskLevel: '高风险' }; } - + // 应用筛选 filterProjects(filters); }; @@ -160,7 +161,7 @@ function App() { // 处理项目搜索 const handleSearch = (text) => { setSearchText(text); - + // 根据搜索文本筛选项目数据 const filters = { subProjectName: text }; filterProjects(filters); @@ -170,7 +171,7 @@ function App() { const handleProjectSelect = (selectedRowKeys) => { setSelectedProjects(selectedRowKeys); }; - + // 处理项目点击,打开详情弹窗 const handleProjectClick = (project) => { setCurrentProject(project); @@ -182,7 +183,7 @@ function App() { setLoading(true); try { const result = await window.electronAPI.updateProject(updatedProject); - + if (result.success) { // 更新成功,重新加载数据 loadData(); @@ -202,7 +203,7 @@ function App() { try { // 选择Excel文件 const filePath = await window.electronAPI.selectExcelFile(); - + if (filePath) { // 检查是否重复导入同一个文件 if (lastImportedFilePath === filePath) { @@ -233,7 +234,7 @@ function App() { const importFile = async (filePath) => { try { setLoading(true); - + // 创建进度对话框 const progressModal = Modal.info({ title: '导入Excel', @@ -248,13 +249,13 @@ function App() { maskClosable: false, closable: false, }); - + // 监听导入进度 window.electronAPI.onImportProgress((data) => { // 更新进度条和消息 const progressBar = document.getElementById('import-progress-bar'); const progressMessage = document.getElementById('import-progress-message'); - + if (progressBar && progressMessage) { // 更新进度条 const antProgress = progressBar.querySelector('.ant-progress-bg'); @@ -262,10 +263,10 @@ function App() { antProgress.style.width = `${data.progress}%`; antProgress.setAttribute('aria-valuenow', data.progress); } - + // 更新文本 progressMessage.textContent = data.message; - + // 如果导入完成,关闭对话框并重新加载数据 if (data.status === 'complete') { setTimeout(() => { @@ -284,10 +285,10 @@ function App() { } } }); - + // 导入Excel文件 const result = await window.electronAPI.importExcel(filePath); - + // 如果导入失败且没有进度事件处理,则显示错误消息 if (!result.success) { progressModal.destroy(); @@ -319,7 +320,7 @@ function App() { try { // 调用API清除数据 const result = await window.electronAPI.clearAllData(); - + if (result.success) { // 清除成功,重置状态 setProjects([]); @@ -328,7 +329,7 @@ function App() { setSelectedProjects([]); setActiveFilter(null); setSearchText(''); - + // 显示成功提示 Modal.success({ title: '操作成功', @@ -367,24 +368,24 @@ function App() { >
{/* 工具栏 */} - - + {/* 树状结构区 */} - - + {/* 数据展示区 */} - - + + {/* 树状结构区 */} + + {/* 项目详情表单 */} { - const [form] = Form.useForm(); +const ProjectDetailForm = ({visible, project, onCancel, onSave}) => { + const [form] = Form.useForm(); - useEffect(() => { - if (project && visible) { - // 格式化日期字段为字符串 - const formattedProject = { ...project }; - ['actual_start_time', 'planned_completion_time', 'completion_time', 'next_review_time'].forEach(field => { - if (formattedProject[field]) { - formattedProject[field] = dayjs(formattedProject[field]).format('YYYY-MM-DD'); + useEffect(() => { + if (project && visible) { + // 格式化日期字段为字符串 + const formattedProject = {...project}; + ['actual_start_time', 'planned_completion_time', 'completion_time', 'next_review_time'].forEach(field => { + if (formattedProject[field]) { + formattedProject[field] = dayjs(formattedProject[field]).format('YYYY-MM-DD'); + } + }); + form.setFieldsValue(formattedProject); } - }); - form.setFieldsValue(formattedProject); - } - }, [form, project, visible]); + }, [form, project, visible]); - const handleSave = () => { - form.validateFields() - .then(values => { - onSave({ ...project, ...values }); - }) - .catch(info => { - console.log('验证失败:', info); - }); - }; + const handleSave = () => { + form.validateFields() + .then(values => { + onSave({...project, ...values}); + }) + .catch(info => { + console.log('验证失败:', info); + }); + }; - return ( - - 取消 - , - - ]} - > -
- - - - - - - - - - - - - - - - -