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,无法连接开发服务器,请检查:
- React开发服务器是否启动(npm run react-start)
- 端口3000是否被占用
- 网络连接是否正常
`);
+ }
+ });
+ };
+ 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 (
-
- 取消
- ,
-
- ]}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
+ return (
+
+ 取消
+ ,
+
+ ]}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
机的变化
+
+
+
+
+
+
+
+
+
+
+
+
环境的变化
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
管理的变化
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
};
export default ProjectDetailForm;
diff --git a/src/components/ProjectWarningView.js b/src/components/ProjectWarningView.js
new file mode 100644
index 0000000..b028be0
--- /dev/null
+++ b/src/components/ProjectWarningView.js
@@ -0,0 +1,157 @@
+import React from 'react';
+import { Spin } from 'antd';
+
+const ProjectWarningView = ({ projects = [], loading = false,onClick}) => {
+ const warningFields = [
+ { key: 'new_team', label: '新班组', message: '存在新班组,请做好班组入场管理。' },
+ { key: 'new_homework_content', label: '新的作业内容', message: '存在新的作业内容,请加强现场管控。' },
+ { key: 'change_homework_method', label: '改变作业方法', message: '存在改变作业方法,请及时核查施工方案编审、方案交底及人员、机具准备情况。' },
+ { key: 'changes_geographical', label: '地理环境的变化', message: '存在作业环境的变化,请及时核查施工方案编审、方案交底及人员、机具准备情况。' },
+ { key: 'changes_meteorological', label: '气象环境的变化', message: '存在气象预警,请关注天气变化,做好应对措施。' },
+ { key: 'changes_social', label: '社会环境的变化', message: '存在社会环境变化,请合理安排作业计划,避免人员失控。' },
+ { key: 'changes_management', label: '管理要求的变化', message: '存在管理要求的变化,请加强现场巡查力度,严防无计划作业。' },
+ { key: 'changes_homework_plan', label: '作业计划的变化', message: '存在作业计划的变化,请做好施工力量配备。' },
+ { key: 'changes_management_personnel', label: '管理人员的变化', message: '存在管理人员的变化,请加强现场管控。' },
+ ];
+
+ const hasWarnings = (item) => {
+ if (typeof item !== 'object') return false;
+ if (item.new_members || item.new_high_altitude || item.new_hired_general) return true;
+ return warningFields.some(({ key }) => item[key]);
+ };
+
+ const filteredProjects = projects.filter(hasWarnings);
+
+ return (
+
+
+ 工程预警
+
+
+
+
+ {!loading && (
+ <>
+ {filteredProjects.length > 0 ? (
+
+ {filteredProjects.map((item, index) => (
+
onClick(item)}
+ onMouseEnter={(e) => {
+ e.currentTarget.style.transform = 'translateY(-6px) scale(1.02)';
+ e.currentTarget.style.boxShadow = '0 12px 24px rgba(0,0,0,0.15)';
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.transform = 'translateY(0) scale(1)';
+ e.currentTarget.style.boxShadow = '0 6px 15px rgba(0,0,0,0.07)';
+ }}
+ title={typeof item === 'string' ? item : item.sub_project_name || '未命名项目'}
+ >
+
+ {typeof item === 'string' ? item : item.sub_project_name || '未命名项目'}
+
+
+ {(item.new_members || item.new_high_altitude || item.new_hired_general) && (
+
+ 新人: 存在新人员,请做好人员“面对面”核实。
+
+ )}
+
+ {warningFields.map(({key, label, message}) =>
+ item[key] ? (
+
+ {label}: {message}
+
+ ) : null
+ )}
+
+ ))}
+
+ ) : (
+
+ 暂无项目预警
+
+ )}
+ >
+ )}
+
+
+
+ );
+};
+
+export default ProjectWarningView;
diff --git a/src/components/TreeView.js b/src/components/TreeView.js
index 00c0658..dce2fc7 100644
--- a/src/components/TreeView.js
+++ b/src/components/TreeView.js
@@ -1,62 +1,62 @@
-import React, { useState, useEffect } from 'react';
-import { Tree, Spin } from 'antd';
-import { FolderOutlined, ApartmentOutlined, HomeOutlined } from '@ant-design/icons';
+import React, {useState, useEffect} from 'react';
+import {Tree, Spin} from 'antd';
+import {FolderOutlined, ApartmentOutlined, HomeOutlined} from '@ant-design/icons';
-const TreeView = ({ treeData, selectedNode, onSelect, loading }) => {
- const [expandedKeys, setExpandedKeys] = useState([]);
+const TreeView = ({treeData, selectedNode, onSelect, loading}) => {
+ const [expandedKeys, setExpandedKeys] = useState([]);
- // 默认只展开一级目录
- useEffect(() => {
- if (!treeData || treeData.length === 0) return;
-
- // 只获取第一级节点的key
- const firstLevelKeys = treeData.map(node => node.key);
- setExpandedKeys(firstLevelKeys);
- }, [treeData]);
- // 自定义树节点图标
- const getIcon = (node) => {
- if (node.level === 1) {
- return ;
- } else if (node.level === 2) {
- return ;
- } else {
- return ;
- }
- };
+ // 默认只展开一级目录
+ useEffect(() => {
+ if (!treeData || treeData.length === 0) return;
- // 自定义树节点标题
- const titleRender = (nodeData) => {
- return (
-
+ // 只获取第一级节点的key
+ const firstLevelKeys = treeData.map(node => node.key);
+ setExpandedKeys(firstLevelKeys);
+ }, [treeData]);
+ // 自定义树节点图标
+ const getIcon = (node) => {
+ if (node.level === 1) {
+ return ;
+ } else if (node.level === 2) {
+ return ;
+ } else {
+ return ;
+ }
+ };
+
+ // 自定义树节点标题
+ const titleRender = (nodeData) => {
+ return (
+
{getIcon(nodeData)} {nodeData.title}
- );
- };
+ );
+ };
- return (
-
-
- 项目结构
-
-
- {loading ? (
-
-
+ return (
+
+
+ 项目预警
+
+
+ {loading ? (
+
+
+
+ ) : (
+
setExpandedKeys(keys)}
+ defaultExpandedKeys={expandedKeys}
+ blockNode
+ />
+ )}
- ) : (
-
setExpandedKeys(keys)}
- defaultExpandedKeys={expandedKeys}
- blockNode
- />
- )}
-
- );
+ );
};
export default TreeView;
diff --git a/src/styles/index.css b/src/styles/index.css
index 5959bb2..c0d61c9 100644
--- a/src/styles/index.css
+++ b/src/styles/index.css
@@ -49,6 +49,13 @@ code {
overflow-y: auto;
}
+
+.project-warning-view {
+ width: 400px;
+ background-color: var(--vscode-sidebar-bg);
+ border-right: 1px solid var(--vscode-sidebar-border);
+}
+
.data-view {
flex: 1;
background-color: var(--vscode-editor-bg);