dataTool/main.js

518 lines
16 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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')
}
});
// 将mainWindow设为全局变量以便其他模块可以访问
global.mainWindow = mainWindow;
// 加载应用
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,<h1 style="color:red">无法连接开发服务器,请检查:</h1>
<ol>
<li>React开发服务器是否启动npm run react-start</li>
<li>端口3000是否被占用</li>
<li>网络连接是否正常</li>
</ol>`);
}
});
};
setTimeout(retryLoad, 10000);
} else {
mainWindow.loadURL(prodUrl).catch(() => {
mainWindow.loadURL(`data:text/html,<h1 style="color:red">生产文件加载失败请重新编译npm run react-build</h1>`);
});
}
});
};
// 开发模式立即尝试加载,生产模式直接加载
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 };
}
});
// 注册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();
}
});
});
// 当所有窗口关闭时退出应用
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 });
}
});
});
});