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

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
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

@ -29,6 +29,9 @@ function createWindow() {
preload: path.join(__dirname, 'preload.js')
}
});
// 将mainWindow设为全局变量以便其他模块可以访问
global.mainWindow = mainWindow;
// 加载应用
const loadAppUrl = () => {
@ -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', () => {
// 在macOS上当点击dock图标并且没有其他窗口打开时通常在应用程序中重新创建一个窗口
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\"",
"react-start": "react-scripts start",
"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",
"eject": "react-scripts eject"
},
@ -32,26 +34,36 @@
"preload.js"
],
"directories": {
"buildResources": "assets"
"buildResources": "assets",
"output": "release"
},
"win": {
"target": "nsis"
}
"target": "nsis",
"sign": null
},
"npmRebuild": false,
"extraMetadata": {
"main": "main.js"
},
"asar": true,
"forceCodeSigning": false
},
"dependencies": {
"antd": "^5.7.1",
"sqlite3": "^5.1.6",
"electron-is-dev": "^2.0.0",
"exceljs": "^4.3.0",
"fs-extra": "^11.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"sqlite3": "^5.1.6"
},
"devDependencies": {
"concurrently": "^8.2.0",
"cross-env": "^7.0.3",
"electron": "^27.3.8",
"electron-builder": "^24.6.3",
"electron-packager": "^17.1.2",
"electron-rebuild": "^3.2.9",
"react-scripts": "5.0.1"
},
"browserslist": {

View File

@ -10,7 +10,19 @@ contextBridge.exposeInMainWorld('electronAPI', {
getTreeStructure: () => ipcRenderer.invoke('get-tree-structure'),
filterProjects: (filters) => ipcRenderer.invoke('filter-projects', filters),
updateProject: (project) => ipcRenderer.invoke('update-project', project),
deleteProject: (projectId) => ipcRenderer.invoke('delete-project', projectId),
// Excel处理
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 { ConfigProvider, theme, Switch, Space } from 'antd';
import { ConfigProvider, theme, Switch, Space, Modal, Progress, message } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
@ -12,7 +12,6 @@ dayjs.locale('zh-cn');
import Toolbar from './components/Toolbar';
import TreeView from './components/TreeView';
import DataView from './components/DataView';
import AnalysisView from './components/AnalysisView';
import ProjectDetailForm from './components/ProjectDetailForm';
function App() {
@ -206,23 +205,125 @@ function App() {
if (filePath) {
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文件
const result = await window.electronAPI.importExcel(filePath);
if (result.success) {
// 导入成功,重新加载数据
loadData();
} else {
// 如果导入失败且没有进度事件处理,则显示错误消息
if (!result.success) {
progressModal.destroy();
message.error(`导入失败: ${result.error}`);
console.error('导入Excel文件失败:', result.error);
setLoading(false);
}
}
} catch (error) {
console.error('导入Excel文件失败:', error);
} finally {
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 {
setLoading(false);
}
}
});
};
return (
<ConfigProvider
locale={zhCN}
@ -239,6 +340,7 @@ function App() {
activeFilter={activeFilter}
onFilterChange={handleToolbarFilter}
onImportExcel={handleImportExcel}
onClearData={handleClearData}
/>
{/* 树状结构区 */}
@ -259,11 +361,6 @@ function App() {
searchText={searchText}
loading={loading}
/>
{/* 分析结果区 */}
<AnalysisView
selectedProjects={projects.filter(p => selectedProjects.includes(p.id))}
/>
{/* 项目详情表单 */}
<ProjectDetailForm
@ -277,4 +374,4 @@ function App() {
);
}
export default App;
export default App;

View File

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

View File

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

View File

@ -20,12 +20,22 @@ class ExcelService {
throw new Error('文件路径不能为空');
}
// 发送开始导入进度事件
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'reading',
progress: 0,
message: '正在读取Excel文件...'
});
}
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.readFile(filePath);
// 读取第一个工作表
const worksheet = workbook.getWorksheet(1);
const data = [];
const totalRows = worksheet.rowCount - 1; // 减去表头行
// 读取表头
const headers = [];
@ -33,7 +43,17 @@ class ExcelService {
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) => {
if (rowNumber > 1) { // 跳过表头
const rowData = {};
@ -41,12 +61,64 @@ class ExcelService {
rowData[headers[colNumber - 1]] = cell.value;
});
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) {
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 {
@ -56,6 +128,16 @@ class ExcelService {
};
} catch (error) {
console.error('Excel导入错误:', error);
// 发送导入错误进度事件
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'error',
progress: 0,
message: `导入失败: ${error.message}`
});
}
return {
success: false,
error: error.message
@ -65,187 +147,373 @@ class ExcelService {
// 将数据保存到数据库
saveDataToDatabase(data) {
// 清空现有数据
this.db.run('DELETE FROM projects', (err) => {
if (err) {
console.error('清空项目数据失败:', err);
return;
}
// 插入新数据
const stmt = this.db.prepare(`
INSERT INTO projects (
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
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
// 开始事务
this.db.serialize(() => {
this.db.run('BEGIN TRANSACTION');
for (const row of data) {
stmt.run(
row['单位'] || null,
row['项目编号'] || null,
row['安全编码'] || null,
row['大项工程名称'] || null,
row['单项工程名称'] || null,
row['在施工程作业范围'] || null,
row['工程规模'] || null,
row['安全总监'] || null,
row['建设单位'] || null,
row['监理单位'] || null,
row['施工单位'] || null,
row['工程位置'] || null,
row['实际开工时间'] || null,
row['计划竣工时间'] || null,
row['当前工程进度'] || null,
row['当前工程状态'] || null,
row['参建人数'] || 0,
row['新班组进场数量'] || 0,
row['新人进场数量'] || 0,
row['带班人姓名、电话'] || null,
row['下周作业计划'] || null,
row['下周8+2工况内容'] || null,
row['工期是否紧张'] === '是' ? 1 : 0,
row['是否存在"账外事"'] === '是' ? 1 : 0,
row['当前风险等级'] || null,
row['当前风险判断理由'] || null,
row['隐患提示/工作要求'] || null,
row['完成时间'] || null,
row['下次梳理时间'] || null,
row['备注'] || null
);
return new Promise((resolve, reject) => {
// 清空现有数据
this.db.run('DELETE FROM projects', (err) => {
if (err) {
console.error('清空项目数据失败:', err);
reject(err);
return;
}
this.db.run('COMMIT', (err) => {
if (err) {
console.error('提交事务失败:', err);
this.db.run('ROLLBACK');
} else {
console.log(`成功导入 ${data.length} 条数据到数据库`);
// 构建树状结构
this.buildTreeStructure(data);
// 插入新数据
const stmt = this.db.prepare(`
INSERT INTO projects (
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
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
// 开始事务
this.db.serialize(() => {
this.db.run('BEGIN TRANSACTION');
// 发送进度更新
let processedRows = 0;
const totalRows = data.length;
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(
getValue('单位'),
getValue('项目编号'),
getValue('安全编码'),
getValue('大项工程名称'),
getValue('单项工程名称'),
getValue('在施工程作业范围'),
getValue('工程规模'),
getValue('安全总监'),
getValue('建设单位'),
getValue('监理单位'),
getValue('施工单位'),
getValue('工程位置'),
getValue('实际开工时间'),
getValue('计划竣工时间'),
getValue('当前工程进度'),
getValue('当前工程状态'),
getNumberValue('参建人数'),
getNumberValue('新班组进场数量'),
getNumberValue('新人进场数量'),
getValue('带班人姓名、电话'),
getValue('下周作业计划'),
getValue('下周8+2工况内容'),
getBooleanValue('工期是否紧张'),
getBooleanValue('是否存在"账外事"'),
getValue('当前风险等级'),
getValue('当前风险判断理由'),
getValue('隐患提示/工作要求'),
getValue('完成时间'),
getValue('下次梳理时间'),
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) => {
if (err) {
console.error('提交事务失败:', err);
this.db.run('ROLLBACK');
reject(err);
} else {
console.log(`成功导入 ${data.length} 条数据到数据库`);
resolve();
}
});
});
// 完成后关闭准备好的语句
stmt.finalize();
});
// 完成后关闭准备好的语句
stmt.finalize();
});
}
// 构建树状结构
buildTreeStructure(data) {
// 清空现有树状结构
this.db.run('DELETE FROM tree_structure', (err) => {
if (err) {
console.error('清空树状结构失败:', err);
return;
}
// 创建总部节点
const headquarters = '总部';
const insertHeadquarters = this.db.prepare(`
INSERT INTO tree_structure (
headquarters,
unit,
construction_unit,
parent_id,
node_level
) VALUES (?, ?, ?, ?, ?)
`);
// 插入总部节点
insertHeadquarters.run(headquarters, null, null, null, 1, function(err) {
return new Promise((resolve, reject) => {
// 清空现有树状结构
this.db.run('DELETE FROM tree_structure', (err) => {
if (err) {
console.error('插入总部节点失败:', err);
console.error('清空树状结构失败:', err);
reject(err);
return;
}
const headquartersId = this.lastID;
console.log(`创建总部节点成功, ID: ${headquartersId}`);
// 使用Promise和事务来确保操作的原子性和顺序性
const headquarters = '总部';
// 单位映射
const unitMap = {};
// 遍历数据,构建树状结构
for (const row of data) {
const unit = row['单位'];
const constructionUnit = row['建设单位'];
// 开始事务
this.db.run('BEGIN TRANSACTION', (err) => {
if (err) {
console.error('开始事务失败:', err);
reject(err);
return;
}
// 检查单位是否为空
if (!unit) continue;
// 发送进度更新 - 开始创建树状结构
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'building',
progress: 75,
message: `正在创建总部节点...`
});
}
// 如果单位不存在,则创建单位节点
if (!unitMap[unit]) {
insertHeadquarters.run(headquarters, unit, null, headquartersId, 2, 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) {
console.error(`创建单位节点失败: ${unit}`, err);
console.error('插入总部节点失败:', err);
this.db.run('ROLLBACK');
reject(err);
return;
}
const unitId = this.lastID;
unitMap[unit] = { id: unitId, constructionUnits: {} };
console.log(`创建单位节点: ${unit}, ID: ${unitId}`);
// 如果建设单位不为空且不存在,则添加建设单位节点
if (constructionUnit && !unitMap[unit].constructionUnits[constructionUnit]) {
insertHeadquarters.run(headquarters, unit, constructionUnit, unitId, 3, function(err) {
if (err) {
console.error(`创建建设单位节点失败: ${constructionUnit}`, err);
} else {
unitMap[unit].constructionUnits[constructionUnit] = true;
console.log(`创建建设单位节点: ${constructionUnit}, 父节点: ${unit}`);
}
});
}
});
} 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}`);
}
});
}
}
// 完成后关闭准备好的语句
insertHeadquarters.finalize();
console.log('树状结构构建完成');
// 获取总部节点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}`);
// 单位映射
const unitMap = {};
// 首先处理所有单位节点
const uniqueUnits = [...new Set(data.filter(row => row['单位']).map(row => row['单位']))];
// 发送进度更新 - 开始创建单位节点
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'building',
progress: 80,
message: `正在创建单位节点...`
});
}
const processUnits = () => {
return new Promise((resolve, reject) => {
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) {
console.error(`创建单位节点失败: ${unit}`, err);
reject(err);
return;
}
const unitId = this.lastID;
unitMap[unit] = { id: unitId, constructionUnits: {} };
console.log(`创建单位节点: ${unit}, ID: ${unitId}`);
unitProcessed++;
// 更新进度
if (unitProcessed % 5 === 0 || unitProcessed === uniqueUnits.length) {
const progress = Math.floor(80 + (unitProcessed / uniqueUnits.length) * 10); // 80%-90%的进度
if (global.mainWindow) {
global.mainWindow.webContents.send('import-progress', {
status: 'building',
progress: progress,
message: `正在创建单位节点...${unitProcessed}/${uniqueUnits.length}`
});
}
}
if (unitProcessed === uniqueUnits.length) {
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('树状结构构建完成');
resolve();
}
});
})
.catch(err => {
console.error('构建树状结构失败:', err);
this.db.run('ROLLBACK');
reject(err);
});
});
}
);
});
});
});
}

View File

@ -55,14 +55,6 @@ code {
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 {
width: 32px;
@ -163,3 +155,61 @@ code {
border-color: #40a9ff !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;
}