删除分页和结果分析功能,优化了表格导入的功能

This commit is contained in:
吕继龙 2025-04-09 17:43:19 +08:00
parent f045fc0d37
commit 879ee40f82
11 changed files with 1764 additions and 230 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
node_modules node_modules
build
dist
release

View File

@ -0,0 +1,22 @@
{
"appId": "com.datatools.app",
"productName": "DataTools",
"files": [
"build/**/*",
"node_modules/**/*",
"main.js",
"preload.js"
],
"directories": {
"buildResources": "assets",
"output": "release"
},
"win": {
"target": "dir"
},
"npmRebuild": false,
"asar": false,
"extraMetadata": {
"main": "main.js"
}
}

43
main.js
View File

@ -30,6 +30,9 @@ function createWindow() {
} }
}); });
// 将mainWindow设为全局变量以便其他模块可以访问
global.mainWindow = mainWindow;
// 加载应用 // 加载应用
const loadAppUrl = () => { const loadAppUrl = () => {
const devUrl = 'http://localhost:3000'; const devUrl = 'http://localhost:3000';
@ -236,6 +239,46 @@ app.whenReady().then(() => {
} }
}); });
// 注册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', () => { app.on('activate', () => {
// 在macOS上当点击dock图标并且没有其他窗口打开时通常在应用程序中重新创建一个窗口 // 在macOS上当点击dock图标并且没有其他窗口打开时通常在应用程序中重新创建一个窗口
if (BrowserWindow.getAllWindows().length === 0) { if (BrowserWindow.getAllWindows().length === 0) {

1083
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,9 @@
"dev": "concurrently \"npm run start\" \"cross-env BROWSER=none npm run react-start\"", "dev": "concurrently \"npm run start\" \"cross-env BROWSER=none npm run react-start\"",
"react-start": "react-scripts start", "react-start": "react-scripts start",
"react-build": "react-scripts build", "react-build": "react-scripts build",
"build": "electron-builder", "rebuild": "electron-rebuild",
"build": "electron-builder --publish never",
"package": "npm run react-build && npm run rebuild && npm run build",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
@ -32,26 +34,36 @@
"preload.js" "preload.js"
], ],
"directories": { "directories": {
"buildResources": "assets" "buildResources": "assets",
"output": "release"
}, },
"win": { "win": {
"target": "nsis" "target": "nsis",
} "sign": null
},
"npmRebuild": false,
"extraMetadata": {
"main": "main.js"
},
"asar": true,
"forceCodeSigning": false
}, },
"dependencies": { "dependencies": {
"antd": "^5.7.1", "antd": "^5.7.1",
"sqlite3": "^5.1.6",
"electron-is-dev": "^2.0.0", "electron-is-dev": "^2.0.0",
"exceljs": "^4.3.0", "exceljs": "^4.3.0",
"fs-extra": "^11.1.1", "fs-extra": "^11.1.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0" "react-dom": "^18.2.0",
"sqlite3": "^5.1.6"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^8.2.0", "concurrently": "^8.2.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"electron": "^27.3.8", "electron": "^27.3.8",
"electron-builder": "^24.6.3", "electron-builder": "^24.6.3",
"electron-packager": "^17.1.2",
"electron-rebuild": "^3.2.9",
"react-scripts": "5.0.1" "react-scripts": "5.0.1"
}, },
"browserslist": { "browserslist": {

View File

@ -10,7 +10,19 @@ contextBridge.exposeInMainWorld('electronAPI', {
getTreeStructure: () => ipcRenderer.invoke('get-tree-structure'), getTreeStructure: () => ipcRenderer.invoke('get-tree-structure'),
filterProjects: (filters) => ipcRenderer.invoke('filter-projects', filters), filterProjects: (filters) => ipcRenderer.invoke('filter-projects', filters),
updateProject: (project) => ipcRenderer.invoke('update-project', project), updateProject: (project) => ipcRenderer.invoke('update-project', project),
deleteProject: (projectId) => ipcRenderer.invoke('delete-project', projectId),
// Excel处理 // Excel处理
importExcel: (filePath) => ipcRenderer.invoke('import-excel', filePath), importExcel: (filePath) => ipcRenderer.invoke('import-excel', filePath),
// 数据清除
clearAllData: () => ipcRenderer.invoke('clear-all-data'),
// 事件监听
onImportProgress: (callback) => {
// 移除之前的监听器,避免重复
ipcRenderer.removeAllListeners('import-progress');
// 添加新的监听器
ipcRenderer.on('import-progress', (event, data) => callback(data));
}
}); });

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { ConfigProvider, theme, Switch, Space } from 'antd'; import { ConfigProvider, theme, Switch, Space, Modal, Progress, message } from 'antd';
import zhCN from 'antd/locale/zh_CN'; import zhCN from 'antd/locale/zh_CN';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn'; import 'dayjs/locale/zh-cn';
@ -12,7 +12,6 @@ dayjs.locale('zh-cn');
import Toolbar from './components/Toolbar'; import Toolbar from './components/Toolbar';
import TreeView from './components/TreeView'; import TreeView from './components/TreeView';
import DataView from './components/DataView'; import DataView from './components/DataView';
import AnalysisView from './components/AnalysisView';
import ProjectDetailForm from './components/ProjectDetailForm'; import ProjectDetailForm from './components/ProjectDetailForm';
function App() { function App() {
@ -206,21 +205,123 @@ function App() {
if (filePath) { if (filePath) {
setLoading(true); setLoading(true);
// 创建进度对话框
const progressModal = Modal.info({
title: '导入Excel',
content: (
<div>
<p id="import-progress-message">正在准备导入...</p>
<Progress id="import-progress-bar" percent={0} status="active" />
</div>
),
icon: null,
okButtonProps: { style: { display: 'none' } },
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');
if (antProgress) {
antProgress.style.width = `${data.progress}%`;
antProgress.setAttribute('aria-valuenow', data.progress);
}
// 更新文本
progressMessage.textContent = data.message;
// 如果导入完成,关闭对话框并重新加载数据
if (data.status === 'complete') {
setTimeout(() => {
progressModal.destroy();
message.success('Excel导入成功');
// 重新加载数据
loadData();
setLoading(false);
}, 1000);
} else if (data.status === 'error') {
setTimeout(() => {
progressModal.destroy();
message.error(`导入失败: ${data.message}`);
setLoading(false);
}, 1000);
}
}
});
// 导入Excel文件 // 导入Excel文件
const result = await window.electronAPI.importExcel(filePath); const result = await window.electronAPI.importExcel(filePath);
if (result.success) { // 如果导入失败且没有进度事件处理,则显示错误消息
// 导入成功,重新加载数据 if (!result.success) {
loadData(); progressModal.destroy();
} else { message.error(`导入失败: ${result.error}`);
console.error('导入Excel文件失败:', result.error); console.error('导入Excel文件失败:', result.error);
setLoading(false);
} }
} }
} catch (error) { } catch (error) {
console.error('导入Excel文件失败:', error); console.error('导入Excel文件失败:', error);
message.error(`导入失败: ${error.message}`);
setLoading(false);
}
};
// 处理清除数据
const handleClearData = async () => {
// 显示确认对话框
Modal.confirm({
title: '确认清除数据',
content: '此操作将清除所有项目数据,无法恢复。确定要继续吗?',
okText: '确定',
okType: 'danger',
cancelText: '取消',
onOk: async () => {
setLoading(true);
try {
// 调用API清除数据
const result = await window.electronAPI.clearAllData();
if (result.success) {
// 清除成功,重置状态
setProjects([]);
setTreeData([]);
setSelectedNode(null);
setSelectedProjects([]);
setActiveFilter(null);
setSearchText('');
// 显示成功提示
Modal.success({
title: '操作成功',
content: '所有数据已清除'
});
} else {
// 显示错误提示
Modal.error({
title: '操作失败',
content: result.error || '清除数据时发生错误'
});
}
} catch (error) {
console.error('清除数据失败:', error);
// 显示错误提示
Modal.error({
title: '操作失败',
content: '清除数据时发生错误'
});
} finally { } finally {
setLoading(false); setLoading(false);
} }
}
});
}; };
return ( return (
@ -239,6 +340,7 @@ function App() {
activeFilter={activeFilter} activeFilter={activeFilter}
onFilterChange={handleToolbarFilter} onFilterChange={handleToolbarFilter}
onImportExcel={handleImportExcel} onImportExcel={handleImportExcel}
onClearData={handleClearData}
/> />
{/* 树状结构区 */} {/* 树状结构区 */}
@ -260,11 +362,6 @@ function App() {
loading={loading} loading={loading}
/> />
{/* 分析结果区 */}
<AnalysisView
selectedProjects={projects.filter(p => selectedProjects.includes(p.id))}
/>
{/* 项目详情表单 */} {/* 项目详情表单 */}
<ProjectDetailForm <ProjectDetailForm
visible={detailModalVisible} visible={detailModalVisible}

View File

@ -40,6 +40,13 @@ const DataView = ({
key: 'current_progress', key: 'current_progress',
width: 120, width: 120,
ellipsis: true, ellipsis: true,
render: (text) => {
// 如果进度文本过长只显示前15个字符加省略号
if (text && text.length > 15) {
return <span title={text}>{text.substring(0, 15)}...</span>;
}
return text;
},
}, },
{ {
title: '当前工程状态', title: '当前工程状态',
@ -115,17 +122,15 @@ const DataView = ({
selectedRowKeys: selectedProjects, selectedRowKeys: selectedProjects,
onChange: onSelect, onChange: onSelect,
}} }}
pagination={{ pagination={false}
pageSize: 20,
showSizeChanger: true,
showTotal: (total) => `${total} 条数据`,
}}
scroll={{ y: 'calc(100vh - 150px)', x: 'max-content' }} scroll={{ y: 'calc(100vh - 150px)', x: 'max-content' }}
size="small" size="small"
bordered
onRow={(record) => ({ onRow={(record) => ({
onClick: () => onClick(record), // 点击行时调用传入的onClick函数 onClick: () => onClick(record), // 点击行时调用传入的onClick函数
style: { cursor: 'pointer' } // 鼠标悬停时显示指针样式 style: { cursor: 'pointer' } // 鼠标悬停时显示指针样式
})} })}
style={{ margin: '10px' }}
/> />
)} )}
</div> </div>

View File

@ -5,10 +5,11 @@ import {
WarningOutlined, WarningOutlined,
SearchOutlined, SearchOutlined,
ImportOutlined, ImportOutlined,
SettingOutlined SettingOutlined,
ClearOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
const Toolbar = ({ activeFilter, onFilterChange, onImportExcel }) => { const Toolbar = ({ activeFilter, onFilterChange, onImportExcel, onClearData }) => {
// 显示"正在开发中"的提示 // 显示"正在开发中"的提示
const showDevelopingMessage = () => { const showDevelopingMessage = () => {
Modal.info({ Modal.info({
@ -48,6 +49,12 @@ const Toolbar = ({ activeFilter, onFilterChange, onImportExcel }) => {
icon: <SettingOutlined />, icon: <SettingOutlined />,
title: '设置', title: '设置',
onClick: showDevelopingMessage onClick: showDevelopingMessage
},
{
key: 'clear',
icon: <ClearOutlined />,
title: '清除数据',
onClick: onClearData
} }
]; ];

View File

@ -20,12 +20,22 @@ class ExcelService {
throw new Error('文件路径不能为空'); throw new Error('文件路径不能为空');
} }
// 发送开始导入进度事件
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'reading',
progress: 0,
message: '正在读取Excel文件...'
});
}
const workbook = new ExcelJS.Workbook(); const workbook = new ExcelJS.Workbook();
await workbook.xlsx.readFile(filePath); await workbook.xlsx.readFile(filePath);
// 读取第一个工作表 // 读取第一个工作表
const worksheet = workbook.getWorksheet(1); const worksheet = workbook.getWorksheet(1);
const data = []; const data = [];
const totalRows = worksheet.rowCount - 1; // 减去表头行
// 读取表头 // 读取表头
const headers = []; const headers = [];
@ -33,7 +43,17 @@ class ExcelService {
headers[colNumber - 1] = cell.value; headers[colNumber - 1] = cell.value;
}); });
// 发送读取表头完成进度事件
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'reading',
progress: 10,
message: '表头读取完成,正在读取数据...'
});
}
// 读取数据 // 读取数据
let processedRows = 0;
worksheet.eachRow((row, rowNumber) => { worksheet.eachRow((row, rowNumber) => {
if (rowNumber > 1) { // 跳过表头 if (rowNumber > 1) { // 跳过表头
const rowData = {}; const rowData = {};
@ -41,12 +61,64 @@ class ExcelService {
rowData[headers[colNumber - 1]] = cell.value; rowData[headers[colNumber - 1]] = cell.value;
}); });
data.push(rowData); data.push(rowData);
// 计算并发送进度
processedRows++;
if (processedRows % 10 === 0 || processedRows === totalRows) { // 每10行或最后一行发送一次进度
const progress = Math.floor(10 + (processedRows / totalRows) * 40); // 10%-50%的进度
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'reading',
progress: progress,
message: `正在读取数据...${processedRows}/${totalRows}`
});
}
}
} }
}); });
// 发送数据读取完成进度事件
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'saving',
progress: 50,
message: `数据读取完成,正在保存到数据库...`
});
}
// 将数据保存到数据库(这里可以根据实际需求修改) // 将数据保存到数据库(这里可以根据实际需求修改)
if (data.length > 0 && this.db) { if (data.length > 0 && this.db) {
this.saveDataToDatabase(data); await this.saveDataToDatabase(data);
// 发送数据保存完成进度事件
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'building',
progress: 70,
message: `数据保存完成,正在构建树状结构...`
});
}
// 构建树状结构
await this.buildTreeStructure(data);
// 发送导入完成进度事件
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'complete',
progress: 100,
message: `导入完成,共导入 ${data.length} 条数据`
});
}
} else {
// 如果没有数据,直接发送完成事件
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'complete',
progress: 100,
message: `导入完成,没有数据需要导入`
});
}
} }
return { return {
@ -56,6 +128,16 @@ class ExcelService {
}; };
} catch (error) { } catch (error) {
console.error('Excel导入错误:', error); console.error('Excel导入错误:', error);
// 发送导入错误进度事件
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'error',
progress: 0,
message: `导入失败: ${error.message}`
});
}
return { return {
success: false, success: false,
error: error.message error: error.message
@ -65,10 +147,12 @@ class ExcelService {
// 将数据保存到数据库 // 将数据保存到数据库
saveDataToDatabase(data) { saveDataToDatabase(data) {
return new Promise((resolve, reject) => {
// 清空现有数据 // 清空现有数据
this.db.run('DELETE FROM projects', (err) => { this.db.run('DELETE FROM projects', (err) => {
if (err) { if (err) {
console.error('清空项目数据失败:', err); console.error('清空项目数据失败:', err);
reject(err);
return; return;
} }
@ -112,49 +196,97 @@ class ExcelService {
this.db.serialize(() => { this.db.serialize(() => {
this.db.run('BEGIN TRANSACTION'); this.db.run('BEGIN TRANSACTION');
// 发送进度更新
let processedRows = 0;
const totalRows = data.length;
for (const row of data) { for (const row of data) {
// 处理字段值,确保所有字段都能正确导入
const getValue = (fieldName, defaultValue = null) => {
// 检查字段是否存在,并处理空值
const value = row[fieldName];
if (value === undefined || value === null || value === '') {
return defaultValue;
}
return value;
};
// 处理布尔值字段
const getBooleanValue = (fieldName) => {
const value = row[fieldName];
if (value === '是' || value === 'true' || value === true || value === 1) {
return 1;
}
return 0;
};
// 处理数字字段
const getNumberValue = (fieldName, defaultValue = 0) => {
const value = row[fieldName];
if (value === undefined || value === null || value === '') {
return defaultValue;
}
// 尝试将值转换为数字
const num = Number(value);
return isNaN(num) ? defaultValue : num;
};
// 使用处理函数获取字段值
stmt.run( stmt.run(
row['单位'] || null, getValue('单位'),
row['项目编号'] || null, getValue('项目编号'),
row['安全编码'] || null, getValue('安全编码'),
row['大项工程名称'] || null, getValue('大项工程名称'),
row['单项工程名称'] || null, getValue('单项工程名称'),
row['在施工程作业范围'] || null, getValue('在施工程作业范围'),
row['工程规模'] || null, getValue('工程规模'),
row['安全总监'] || null, getValue('安全总监'),
row['建设单位'] || null, getValue('建设单位'),
row['监理单位'] || null, getValue('监理单位'),
row['施工单位'] || null, getValue('施工单位'),
row['工程位置'] || null, getValue('工程位置'),
row['实际开工时间'] || null, getValue('实际开工时间'),
row['计划竣工时间'] || null, getValue('计划竣工时间'),
row['当前工程进度'] || null, getValue('当前工程进度'),
row['当前工程状态'] || null, getValue('当前工程状态'),
row['参建人数'] || 0, getNumberValue('参建人数'),
row['新班组进场数量'] || 0, getNumberValue('新班组进场数量'),
row['新人进场数量'] || 0, getNumberValue('新人进场数量'),
row['带班人姓名、电话'] || null, getValue('带班人姓名、电话'),
row['下周作业计划'] || null, getValue('下周作业计划'),
row['下周8+2工况内容'] || null, getValue('下周8+2工况内容'),
row['工期是否紧张'] === '是' ? 1 : 0, getBooleanValue('工期是否紧张'),
row['是否存在"账外事"'] === '是' ? 1 : 0, getBooleanValue('是否存在"账外事"'),
row['当前风险等级'] || null, getValue('当前风险等级'),
row['当前风险判断理由'] || null, getValue('当前风险判断理由'),
row['隐患提示/工作要求'] || null, getValue('隐患提示/工作要求'),
row['完成时间'] || null, getValue('完成时间'),
row['下次梳理时间'] || null, getValue('下次梳理时间'),
row['备注'] || null getValue('备注')
); );
// 更新进度
processedRows++;
if (processedRows % 10 === 0 || processedRows === totalRows) {
const progress = Math.floor(50 + (processedRows / totalRows) * 20); // 50%-70%的进度
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'saving',
progress: progress,
message: `正在保存数据到数据库...${processedRows}/${totalRows}`
});
}
}
} }
this.db.run('COMMIT', (err) => { this.db.run('COMMIT', (err) => {
if (err) { if (err) {
console.error('提交事务失败:', err); console.error('提交事务失败:', err);
this.db.run('ROLLBACK'); this.db.run('ROLLBACK');
reject(err);
} else { } else {
console.log(`成功导入 ${data.length} 条数据到数据库`); console.log(`成功导入 ${data.length} 条数据到数据库`);
// 构建树状结构 resolve();
this.buildTreeStructure(data);
} }
}); });
}); });
@ -162,55 +294,98 @@ class ExcelService {
// 完成后关闭准备好的语句 // 完成后关闭准备好的语句
stmt.finalize(); stmt.finalize();
}); });
});
} }
// 构建树状结构 // 构建树状结构
buildTreeStructure(data) { buildTreeStructure(data) {
return new Promise((resolve, reject) => {
// 清空现有树状结构 // 清空现有树状结构
this.db.run('DELETE FROM tree_structure', (err) => { this.db.run('DELETE FROM tree_structure', (err) => {
if (err) { if (err) {
console.error('清空树状结构失败:', err); console.error('清空树状结构失败:', err);
reject(err);
return; return;
} }
// 创建总部节点 // 使用Promise和事务来确保操作的原子性和顺序性
const headquarters = '总部'; const headquarters = '总部';
const insertHeadquarters = this.db.prepare(`
INSERT INTO tree_structure ( // 开始事务
headquarters, this.db.run('BEGIN TRANSACTION', (err) => {
unit, if (err) {
construction_unit, console.error('开始事务失败:', err);
parent_id, reject(err);
node_level return;
) VALUES (?, ?, ?, ?, ?) }
`);
// 发送进度更新 - 开始创建树状结构
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'building',
progress: 75,
message: `正在创建总部节点...`
});
}
// 插入总部节点 // 插入总部节点
insertHeadquarters.run(headquarters, null, null, null, 1, function(err) { this.db.run(
`INSERT INTO tree_structure (headquarters, unit, construction_unit, parent_id, node_level)
VALUES (?, ?, ?, ?, ?)`,
[headquarters, null, null, null, 1],
(err) => {
if (err) { if (err) {
console.error('插入总部节点失败:', err); console.error('插入总部节点失败:', err);
this.db.run('ROLLBACK');
reject(err);
return; return;
} }
const headquartersId = this.lastID; // 获取总部节点ID
this.db.get('SELECT last_insert_rowid() as id', (err, row) => {
if (err) {
console.error('获取总部节点ID失败:', err);
this.db.run('ROLLBACK');
reject(err);
return;
}
const headquartersId = row.id;
console.log(`创建总部节点成功, ID: ${headquartersId}`); console.log(`创建总部节点成功, ID: ${headquartersId}`);
// 单位映射 // 单位映射
const unitMap = {}; const unitMap = {};
// 遍历数据,构建树状结构 // 首先处理所有单位节点
for (const row of data) { const uniqueUnits = [...new Set(data.filter(row => row['单位']).map(row => row['单位']))];
const unit = row['单位'];
const constructionUnit = row['建设单位'];
// 检查单位是否为空 // 发送进度更新 - 开始创建单位节点
if (!unit) continue; if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'building',
progress: 80,
message: `正在创建单位节点...`
});
}
// 如果单位不存在,则创建单位节点 const processUnits = () => {
if (!unitMap[unit]) { return new Promise((resolve, reject) => {
insertHeadquarters.run(headquarters, unit, null, headquartersId, 2, function(err) { let unitProcessed = 0;
if (uniqueUnits.length === 0) {
resolve();
return;
}
uniqueUnits.forEach(unit => {
this.db.run(
`INSERT INTO tree_structure (headquarters, unit, construction_unit, parent_id, node_level)
VALUES (?, ?, ?, ?, ?)`,
[headquarters, unit, null, headquartersId, 2],
function(err) {
if (err) { if (err) {
console.error(`创建单位节点失败: ${unit}`, err); console.error(`创建单位节点失败: ${unit}`, err);
reject(err);
return; return;
} }
@ -218,34 +393,127 @@ class ExcelService {
unitMap[unit] = { id: unitId, constructionUnits: {} }; unitMap[unit] = { id: unitId, constructionUnits: {} };
console.log(`创建单位节点: ${unit}, ID: ${unitId}`); console.log(`创建单位节点: ${unit}, ID: ${unitId}`);
// 如果建设单位不为空且不存在,则添加建设单位节点 unitProcessed++;
if (constructionUnit && !unitMap[unit].constructionUnits[constructionUnit]) {
insertHeadquarters.run(headquarters, unit, constructionUnit, unitId, 3, function(err) { // 更新进度
if (err) { if (unitProcessed % 5 === 0 || unitProcessed === uniqueUnits.length) {
console.error(`创建建设单位节点失败: ${constructionUnit}`, err); const progress = Math.floor(80 + (unitProcessed / uniqueUnits.length) * 10); // 80%-90%的进度
} else { if (global.mainWindow) {
unitMap[unit].constructionUnits[constructionUnit] = true; global.mainWindow.webContents.send('import-progress', {
console.log(`创建建设单位节点: ${constructionUnit}, 父节点: ${unit}`); status: 'building',
} progress: progress,
}); message: `正在创建单位节点...${unitProcessed}/${uniqueUnits.length}`
}
});
} else if (constructionUnit && !unitMap[unit].constructionUnits[constructionUnit]) {
// 如果单位已存在但建设单位不存在,则添加建设单位节点
insertHeadquarters.run(headquarters, unit, constructionUnit, unitMap[unit].id, 3, function(err) {
if (err) {
console.error(`创建建设单位节点失败: ${constructionUnit}`, err);
} else {
unitMap[unit].constructionUnits[constructionUnit] = true;
console.log(`创建建设单位节点: ${constructionUnit}, 父节点: ${unit}`);
}
}); });
} }
} }
// 完成后关闭准备好的语句 if (unitProcessed === uniqueUnits.length) {
insertHeadquarters.finalize(); resolve();
}
}
);
});
});
};
// 然后处理所有建设单位节点
const processConstructionUnits = () => {
return new Promise((resolve, reject) => {
// 收集所有有效的建设单位数据
const constructionUnitData = [];
data.forEach(row => {
const unit = row['单位'];
const constructionUnit = row['建设单位'];
if (unit && constructionUnit && unitMap[unit] &&
!unitMap[unit].constructionUnits[constructionUnit]) {
constructionUnitData.push({
unit,
constructionUnit,
unitId: unitMap[unit].id
});
unitMap[unit].constructionUnits[constructionUnit] = true;
}
});
// 发送进度更新 - 开始创建建设单位节点
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'building',
progress: 90,
message: `正在创建建设单位节点...`
});
}
if (constructionUnitData.length === 0) {
resolve();
return;
}
let processed = 0;
constructionUnitData.forEach(item => {
this.db.run(
`INSERT INTO tree_structure (headquarters, unit, construction_unit, parent_id, node_level)
VALUES (?, ?, ?, ?, ?)`,
[headquarters, item.unit, item.constructionUnit, item.unitId, 3],
function(err) {
if (err) {
console.error(`创建建设单位节点失败: ${item.constructionUnit}`, err);
// 不中断整个过程,继续处理其他节点
} else {
console.log(`创建建设单位节点: ${item.constructionUnit}, 父节点: ${item.unit}`);
}
processed++;
// 更新进度
if (processed % 10 === 0 || processed === constructionUnitData.length) {
const progress = Math.floor(90 + (processed / constructionUnitData.length) * 10); // 90%-100%的进度
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'building',
progress: progress,
message: `正在创建建设单位节点...${processed}/${constructionUnitData.length}`
});
}
}
if (processed === constructionUnitData.length) {
resolve();
}
}
);
});
});
};
// 按顺序执行:先处理单位,再处理建设单位
processUnits()
.then(() => processConstructionUnits())
.then(() => {
// 提交事务
this.db.run('COMMIT', (err) => {
if (err) {
console.error('提交事务失败:', err);
this.db.run('ROLLBACK');
reject(err);
} else {
console.log('树状结构构建完成'); console.log('树状结构构建完成');
resolve();
}
});
})
.catch(err => {
console.error('构建树状结构失败:', err);
this.db.run('ROLLBACK');
reject(err);
});
});
}
);
});
}); });
}); });
} }

View File

@ -55,14 +55,6 @@ code {
overflow-y: auto; overflow-y: auto;
} }
.analysis-view {
width: 300px;
background-color: var(--vscode-sidebar-bg);
border-left: 1px solid var(--vscode-sidebar-border);
overflow-y: auto;
padding: 10px;
}
/* 工具栏图标样式 */ /* 工具栏图标样式 */
.toolbar-icon { .toolbar-icon {
width: 32px; width: 32px;
@ -163,3 +155,61 @@ code {
border-color: #40a9ff !important; border-color: #40a9ff !important;
color: white !important; color: white !important;
} }
/* 自定义滚动条样式 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--vscode-sidebar-bg);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: #555;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #777;
}
/* 表格滚动条样式 */
.ant-table-body::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.ant-table-body::-webkit-scrollbar-track {
background: var(--vscode-sidebar-bg);
border-radius: 4px;
}
.ant-table-body::-webkit-scrollbar-thumb {
background: #555;
border-radius: 4px;
}
.ant-table-body::-webkit-scrollbar-thumb:hover {
background: #777;
}
/* 树状结构滚动条样式 */
.tree-view::-webkit-scrollbar {
width: 8px;
}
.tree-view::-webkit-scrollbar-track {
background: var(--vscode-sidebar-bg);
}
.tree-view::-webkit-scrollbar-thumb {
background: #555;
border-radius: 4px;
}
.tree-view::-webkit-scrollbar-thumb:hover {
background: #777;
}