diff --git a/main.js b/main.js
index f4f5844..2fd5ecd 100644
--- a/main.js
+++ b/main.js
@@ -554,3 +554,51 @@ ipcMain.handle('update-project', (event, project) => {
});
});
});
+
+
+// 获取数据库中的项目数据
+ipcMain.handle('get-projects-range', () => {
+ return new Promise((resolve, reject) => {
+ const sql = `
+ WITH filtered AS (
+ SELECT
+ CASE
+ WHEN major_project_name LIKE '%变电工程%' THEN '变电工程'
+ WHEN major_project_name LIKE '%线路工程%' THEN '线路工程'
+ END AS project_type,
+ participants_count
+ FROM projects
+ WHERE (major_project_name LIKE '%变电工程%'
+ OR major_project_name LIKE '%线路工程%')
+ AND current_status = '在施'
+ ),
+ ordered AS (
+ SELECT
+ project_type,
+ participants_count,
+ ROW_NUMBER() OVER (PARTITION BY project_type ORDER BY participants_count) AS rn,
+ COUNT(*) OVER (PARTITION BY project_type) AS total_count
+ FROM filtered
+ ),
+ percentiles AS (
+ SELECT
+ project_type,
+ MAX(CASE WHEN rn = MAX(1, CAST(total_count * 0.05 AS INTEGER)) THEN participants_count END) AS p5,
+ MAX(CASE WHEN rn = MAX(1, CAST(total_count * 0.95 AS INTEGER)) THEN participants_count END) AS p95
+ FROM ordered
+ GROUP BY project_type
+ )
+ SELECT * FROM percentiles;
+ `;
+
+ db.all(sql, (err, rows) => {
+ if (err) {
+ console.error('获取项目数据失败:', err);
+ reject(err);
+ } else {
+ resolve(rows || []);
+ }
+ });
+ });
+});
+
diff --git a/preload.js b/preload.js
index 6b27f92..e4c4510 100644
--- a/preload.js
+++ b/preload.js
@@ -4,20 +4,21 @@ const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
// 文件操作
selectExcelFile: () => ipcRenderer.invoke('select-excel-file'),
-
+
// 数据库操作
getProjects: () => ipcRenderer.invoke('get-projects'),
+ getProjectsRange: () => ipcRenderer.invoke('get-projects-range'),
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) => {
// 移除之前的监听器,避免重复
diff --git a/src/App.js b/src/App.js
index 2fa5fe8..8a66c82 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,5 +1,5 @@
-import React, { useState, useEffect, useRef } from 'react';
-import { ConfigProvider, theme, Switch, Space, Modal, Progress, message } from 'antd';
+import React, {useState, useEffect, useRef} from 'react';
+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';
@@ -16,401 +16,406 @@ import ProjectDetailForm from './components/ProjectDetailForm';
import ProjectWarningView from "./components/ProjectWarningView";
function App() {
- // 状态管理
- const [projects, setProjects] = useState([]);
- const [treeData, setTreeData] = useState([]);
- const [selectedNode, setSelectedNode] = useState(null);
- const [selectedProjects, setSelectedProjects] = useState([]);
- const [activeFilter, setActiveFilter] = useState(null);
- const [searchText, setSearchText] = useState('');
- const [detailModalVisible, setDetailModalVisible] = useState(false);
- const [currentProject, setCurrentProject] = useState(null);
- const [loading, setLoading] = useState(false);
- const [isDarkMode] = useState(true); // 固定使用深色主题
- const [lastImportedFilePath, setLastImportedFilePath] = useState(null); // 记录最后导入的文件路径
+ // 状态管理
+ const [projects, setProjects] = useState([]);
+ const [projectsDataRange, setProjectsDataRange] = useState([]);
+ const [treeData, setTreeData] = useState([]);
+ const [selectedNode, setSelectedNode] = useState(null);
+ const [selectedProjects, setSelectedProjects] = useState([]);
+ const [activeFilter, setActiveFilter] = useState(null);
+ const [searchText, setSearchText] = useState('');
+ const [detailModalVisible, setDetailModalVisible] = useState(false);
+ const [currentProject, setCurrentProject] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [isDarkMode] = useState(true); // 固定使用深色主题
+ const [lastImportedFilePath, setLastImportedFilePath] = useState(null); // 记录最后导入的文件路径
- // 初始化数据
- useEffect(() => {
- loadData();
- }, []);
-
- // 加载数据
- const loadData = async () => {
- setLoading(true);
- try {
- // 获取项目数据
- 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);
- setTreeData(tree);
- } else {
- setTreeData(treeStructureData);
- }
- } catch (error) {
- console.error('加载数据失败:', error);
- } finally {
- setLoading(false);
- }
- };
-
- // 从项目数据构建树状结构
- const buildTreeFromProjects = (projectsData) => {
- // 总部节点
- const headquarters = {
- key: 'headquarters',
- title: '总部',
- level: 1,
- children: []
- };
-
- // 单位映射
- const unitMap = {};
-
- // 遍历项目数据,构建树状结构
- projectsData.forEach(project => {
- const { unit, construction_unit } = project;
-
- // 如果单位不存在,则创建单位节点
- if (!unitMap[unit]) {
- unitMap[unit] = {
- key: `unit-${unit}`,
- title: unit,
- level: 2,
- children: []
- };
- headquarters.children.push(unitMap[unit]);
- }
-
- // 如果建设单位不为空,则添加建设单位节点
- if (construction_unit && !unitMap[unit].children.find(child => child.title === construction_unit)) {
- unitMap[unit].children.push({
- key: `construction-${unit}-${construction_unit}`,
- title: construction_unit,
- level: 3,
- isLeaf: true
- });
- }
- });
-
- return [headquarters];
- };
-
- // 处理树节点选择
- const handleTreeSelect = (selectedKeys, info) => {
- if (selectedKeys.length > 0) {
- const key = selectedKeys[0];
- setSelectedNode(key);
-
- // 根据选中的节点筛选项目数据
- let filters = {};
-
- if (key === 'headquarters') {
- // 总部节点,显示所有数据
- setProjects(projects);
- } else if (key.startsWith('unit-')) {
- // 单位节点,筛选该单位的数据
- const unit = key.replace('unit-', '');
- filters = { unit };
- } else if (key.startsWith('construction-')) {
- // 建设单位节点,筛选该建设单位的数据
- const parts = key.split('-');
- const unit = parts[1];
- const constructionUnit = parts.slice(2).join('-');
- filters = { unit, constructionUnit };
- }
-
- // 应用筛选
- filterProjects(filters);
- }
- };
-
- // 筛选项目数据
- const filterProjects = async (filters) => {
- setLoading(true);
- try {
- const filteredProjects = await window.electronAPI.filterProjects(filters);
- setProjects(filteredProjects);
- } catch (error) {
- console.error('筛选项目数据失败:', error);
- } finally {
- setLoading(false);
- }
- };
-
- // 处理工具栏筛选
- const handleToolbarFilter = (filter) => {
- setActiveFilter(filter === activeFilter ? null : filter);
-
- // 根据筛选条件筛选项目数据
- let filters = {};
-
- if (filter === 'risk') {
- filters = { riskLevel: '高风险' };
- }
-
- // 应用筛选
- filterProjects(filters);
- };
-
- // 处理项目搜索
- const handleSearch = (text) => {
- setSearchText(text);
-
- // 根据搜索文本筛选项目数据
- const filters = { subProjectName: text };
- filterProjects(filters);
- };
-
- // 处理项目选择
- const handleProjectSelect = (selectedRowKeys) => {
- setSelectedProjects(selectedRowKeys);
- };
-
- // 处理项目点击,打开详情弹窗
- const handleProjectClick = (project) => {
- setCurrentProject(project);
- setDetailModalVisible(true);
- };
-
- // 处理项目更新
- const handleProjectUpdate = async (updatedProject) => {
- setLoading(true);
- try {
- const result = await window.electronAPI.updateProject(updatedProject);
-
- if (result.success) {
- // 更新成功,重新加载数据
+ // 初始化数据
+ useEffect(() => {
loadData();
- setDetailModalVisible(false);
- } else {
- console.error('更新项目数据失败:', result.error);
- }
- } catch (error) {
- console.error('更新项目数据失败:', error);
- } finally {
- setLoading(false);
- }
- };
+ }, []);
- // 处理Excel导入
- const handleImportExcel = async () => {
- try {
- // 选择Excel文件
- const filePath = await window.electronAPI.selectExcelFile();
-
- if (filePath) {
- // 检查是否重复导入同一个文件
- if (lastImportedFilePath === filePath) {
- // 显示确认对话框
- Modal.confirm({
- title: '文件已导入',
- content: '该文件已经导入过,重新导入会覆盖之前的记录。确定要继续吗?',
- okText: '确定',
- cancelText: '取消',
- onOk: () => {
- // 用户确认后继续导入
- importFile(filePath);
- }
- });
- } else {
- // 直接导入文件
- importFile(filePath);
- }
- }
- } catch (error) {
- console.error('导入Excel文件失败:', error);
- message.error(`导入失败: ${error.message}`);
- setLoading(false);
- }
- };
-
- // 导入文件的实际逻辑
- const importFile = async (filePath) => {
- try {
- setLoading(true);
-
- // 创建进度对话框
- const progressModal = Modal.info({
- title: '导入Excel',
- content: (
-
- ),
- 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) {
- progressModal.destroy();
- message.error(`导入失败: ${result.error}`);
- console.error('导入Excel文件失败:', result.error);
- setLoading(false);
- } else {
- // 导入成功,更新最后导入的文件路径
- setLastImportedFilePath(filePath);
- }
- } catch (error) {
- console.error('导入Excel文件失败:', error);
- message.error(`导入失败: ${error.message}`);
- setLoading(false);
- }
- };
-
- // 处理清除数据
- const handleClearData = async () => {
- // 显示确认对话框
- Modal.confirm({
- title: '确认清除数据',
- content: '此操作将清除所有项目数据,无法恢复。确定要继续吗?',
- okText: '确定',
- okType: 'danger',
- cancelText: '取消',
- onOk: async () => {
+ // 加载数据
+ const loadData = async () => {
setLoading(true);
try {
- // 调用API清除数据
- const result = await window.electronAPI.clearAllData();
+ // 获取项目数据
+ const projectsData = await window.electronAPI.getProjects();
+ setProjects(projectsData);
- if (result.success) {
- // 清除成功,重置状态
- setProjects([]);
- setTreeData([]);
- setSelectedNode(null);
- setSelectedProjects([]);
- setActiveFilter(null);
- setSearchText('');
+ const projectsDataRange = await window.electronAPI.getProjectsRange();
+ setProjectsDataRange(projectsDataRange);
- // 显示成功提示
- Modal.success({
- title: '操作成功',
- content: '所有数据已清除'
- });
- } else {
- // 显示错误提示
- Modal.error({
- title: '操作失败',
- content: result.error || '清除数据时发生错误'
- });
- }
+ // 获取树状结构数据
+ const treeStructureData = await window.electronAPI.getTreeStructure();
+
+ // 如果树状结构为空,则从项目数据构建树状结构
+ if (treeStructureData.length === 0 && projectsData.length > 0) {
+ const tree = buildTreeFromProjects(projectsData);
+ setTreeData(tree);
+ } else {
+ setTreeData(treeStructureData);
+ }
} catch (error) {
- console.error('清除数据失败:', error);
- // 显示错误提示
- Modal.error({
- title: '操作失败',
- content: '清除数据时发生错误'
- });
+ console.error('加载数据失败:', error);
} finally {
- setLoading(false);
+ setLoading(false);
}
- }
- });
- };
+ };
- return (
-
-
- {/* 工具栏 */}
-
+ // 从项目数据构建树状结构
+ const buildTreeFromProjects = (projectsData) => {
+ // 总部节点
+ const headquarters = {
+ key: 'headquarters',
+ title: '总部',
+ level: 1,
+ children: []
+ };
- {/* 树状结构区 */}
-
+ // 单位映射
+ const unitMap = {};
- {/* 数据展示区 */}
-
+ // 遍历项目数据,构建树状结构
+ projectsData.forEach(project => {
+ const {unit, construction_unit} = project;
- {/* 树状结构区 */}
-
+ // 如果单位不存在,则创建单位节点
+ if (!unitMap[unit]) {
+ unitMap[unit] = {
+ key: `unit-${unit}`,
+ title: unit,
+ level: 2,
+ children: []
+ };
+ headquarters.children.push(unitMap[unit]);
+ }
- {/* 项目详情表单 */}
-
setDetailModalVisible(false)}
- onSave={handleProjectUpdate}
- />
-
-
- );
+ // 如果建设单位不为空,则添加建设单位节点
+ if (construction_unit && !unitMap[unit].children.find(child => child.title === construction_unit)) {
+ unitMap[unit].children.push({
+ key: `construction-${unit}-${construction_unit}`,
+ title: construction_unit,
+ level: 3,
+ isLeaf: true
+ });
+ }
+ });
+
+ return [headquarters];
+ };
+
+ // 处理树节点选择
+ const handleTreeSelect = (selectedKeys, info) => {
+ if (selectedKeys.length > 0) {
+ const key = selectedKeys[0];
+ setSelectedNode(key);
+
+ // 根据选中的节点筛选项目数据
+ let filters = {};
+
+ if (key === 'headquarters') {
+ // 总部节点,显示所有数据
+ setProjects(projects);
+ } else if (key.startsWith('unit-')) {
+ // 单位节点,筛选该单位的数据
+ const unit = key.replace('unit-', '');
+ filters = {unit};
+ } else if (key.startsWith('construction-')) {
+ // 建设单位节点,筛选该建设单位的数据
+ const parts = key.split('-');
+ const unit = parts[1];
+ const constructionUnit = parts.slice(2).join('-');
+ filters = {unit, constructionUnit};
+ }
+
+ // 应用筛选
+ filterProjects(filters);
+ }
+ };
+
+ // 筛选项目数据
+ const filterProjects = async (filters) => {
+ setLoading(true);
+ try {
+ const filteredProjects = await window.electronAPI.filterProjects(filters);
+ setProjects(filteredProjects);
+ } catch (error) {
+ console.error('筛选项目数据失败:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 处理工具栏筛选
+ const handleToolbarFilter = (filter) => {
+ setActiveFilter(filter === activeFilter ? null : filter);
+
+ // 根据筛选条件筛选项目数据
+ let filters = {};
+
+ if (filter === 'risk') {
+ filters = {riskLevel: '高风险'};
+ }
+
+ // 应用筛选
+ filterProjects(filters);
+ };
+
+ // 处理项目搜索
+ const handleSearch = (text) => {
+ setSearchText(text);
+
+ // 根据搜索文本筛选项目数据
+ const filters = {subProjectName: text};
+ filterProjects(filters);
+ };
+
+ // 处理项目选择
+ const handleProjectSelect = (selectedRowKeys) => {
+ setSelectedProjects(selectedRowKeys);
+ };
+
+ // 处理项目点击,打开详情弹窗
+ const handleProjectClick = (project) => {
+ setCurrentProject(project);
+ setDetailModalVisible(true);
+ };
+
+ // 处理项目更新
+ const handleProjectUpdate = async (updatedProject) => {
+ setLoading(true);
+ try {
+ const result = await window.electronAPI.updateProject(updatedProject);
+
+ if (result.success) {
+ // 更新成功,重新加载数据
+ loadData();
+ setDetailModalVisible(false);
+ } else {
+ console.error('更新项目数据失败:', result.error);
+ }
+ } catch (error) {
+ console.error('更新项目数据失败:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 处理Excel导入
+ const handleImportExcel = async () => {
+ try {
+ // 选择Excel文件
+ const filePath = await window.electronAPI.selectExcelFile();
+
+ if (filePath) {
+ // 检查是否重复导入同一个文件
+ if (lastImportedFilePath === filePath) {
+ // 显示确认对话框
+ Modal.confirm({
+ title: '文件已导入',
+ content: '该文件已经导入过,重新导入会覆盖之前的记录。确定要继续吗?',
+ okText: '确定',
+ cancelText: '取消',
+ onOk: () => {
+ // 用户确认后继续导入
+ importFile(filePath);
+ }
+ });
+ } else {
+ // 直接导入文件
+ importFile(filePath);
+ }
+ }
+ } catch (error) {
+ console.error('导入Excel文件失败:', error);
+ message.error(`导入失败: ${error.message}`);
+ setLoading(false);
+ }
+ };
+
+ // 导入文件的实际逻辑
+ const importFile = async (filePath) => {
+ try {
+ setLoading(true);
+
+ // 创建进度对话框
+ const progressModal = Modal.info({
+ title: '导入Excel',
+ content: (
+
+ ),
+ 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) {
+ progressModal.destroy();
+ message.error(`导入失败: ${result.error}`);
+ console.error('导入Excel文件失败:', result.error);
+ setLoading(false);
+ } else {
+ // 导入成功,更新最后导入的文件路径
+ setLastImportedFilePath(filePath);
+ }
+ } catch (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 {
+ setLoading(false);
+ }
+ }
+ });
+ };
+
+ return (
+
+
+ {/* 工具栏 */}
+
+
+ {/* 树状结构区 */}
+
+
+ {/* 数据展示区 */}
+
+
+ {/* 树状结构区 */}
+
+
+ {/* 项目详情表单 */}
+
setDetailModalVisible(false)}
+ onSave={handleProjectUpdate}
+ />
+
+
+ );
}
export default App;
diff --git a/src/components/ProjectWarningView.js b/src/components/ProjectWarningView.js
index b028be0..ce141e6 100644
--- a/src/components/ProjectWarningView.js
+++ b/src/components/ProjectWarningView.js
@@ -1,26 +1,129 @@
import React from 'react';
-import { Spin } from 'antd';
+import {Spin} from 'antd';
-const ProjectWarningView = ({ projects = [], loading = false,onClick}) => {
+const ProjectWarningView = ({projects = [], projectsDataRange = [], 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: '存在管理人员的变化,请加强现场管控。' },
+ {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]);
+ return warningFields.some(({key}) => item[key]);
};
- const filteredProjects = projects.filter(hasWarnings);
+ const mismatchWarning = (item) => {
+ if (typeof item !== 'object') return false;
+ if (item.current_status !== '在施') return false;
+ const progressText = item.current_progress || '';
+ const planText = item.next_week_plan || '';
+
+ // 识别当前进度中的“完成数/总数”结构,例如:325.5/620
+ const extractRates = (text) => {
+ const matches = [...text.matchAll(/(\d+(\.\d+)?)\s*\/\s*(\d+(\.\d+)?)/g)];
+ return matches.map(match => {
+ const done = parseFloat(match[1]);
+ const total = parseFloat(match[3]);
+ if (!isNaN(done) && !isNaN(total) && total > 0) {
+ return done / total;
+ }
+ return null;
+ }).filter(r => r !== null);
+ };
+
+ const progressRates = extractRates(progressText);
+ const hasLowProgress = !progressRates.some(rate => rate > 0.7 && rate != null);
+ // 如果下周计划中包含关键作业,就说明任务已经安排了
+ const criticalTasks = [
+ '组塔', '导线展放'
+ ];
+ const hasHeavyNextPlan = criticalTasks.some(task => planText.includes(task));
+
+ // 核心判断逻辑
+ return hasLowProgress && hasHeavyNextPlan;
+ };
+// 转成以项目类型为key的对象,方便查找
+ const rangeMap = projectsDataRange.reduce((acc, item) => {
+ acc[item.project_type] = {min: item.p5, max: item.p95};
+ return acc;
+ }, {});
+
+ function getRangeByProjectName(projectName) {
+ if (typeof projectName !== 'string') return null;
+
+ if (projectName.includes('变电工程')) {
+ return rangeMap['变电工程'];
+ } else if (projectName.includes('线路工程')) {
+ return rangeMap['线路工程'];
+ } else {
+ return null;
+ }
+ }
+
+ const projectsRangeWarning = (item) => {
+ if (typeof item !== 'object') return false;
+ if (item.current_status !== '在施') return false;
+ const range = getRangeByProjectName(item.major_project_name);
+ if (!range) return false; // 未知工程类型,不判断
+ if (item.participants_count == 0) return false;
+ return !(item.participants_count > range.min && item.participants_count < range.max);
+ }
+
+
+ const productionPlanScaleWarning = (item) => {
+ if (typeof item !== 'object') return false;
+ if (item.current_status !== '在施') return false;
+ if (!item.planned_completion_time) return false;
+
+ const completionDate = new Date(item.planned_completion_time);
+ const now = new Date();
+ const daysToCompletion = (completionDate - now) / (1000 * 60 * 60 * 24);
+ if (daysToCompletion < 0 || daysToCompletion > 30) return false;
+
+ // 仅判断“导线展放”类型
+ const scale = item.project_scale || '';
+ if (!scale.includes('导线展放')) return false;
+
+ // 判断进度是否 < 80%
+ const progressStr = item.current_progress || '';
+ const progressMatch = progressStr.match(/(\d+(\.\d+)?)%/);
+ if (!progressMatch) return false;
+
+ const progress = parseFloat(progressMatch[1]);
+ return progress < 80;
+ };
+
+
+
+ const filteredProjects = projects.filter(item =>
+ hasWarnings(item) ||
+ mismatchWarning(item) ||
+ projectsRangeWarning(item) ||
+ productionPlanScaleWarning(item)
+ );
+
+
return (
{
) : null
)}
+
+ {mismatchWarning(item) && (
+
+ 工程进展与下周作业计划不匹配,请注意。
+
+ )}
+
+ {projectsRangeWarning(item) && (
+
+ 工程参建人数与作业内容不匹配,请注意。
+
+ )}
+
+ {productionPlanScaleWarning(item) && (
+
+ 投产计划30天内导线展放进度不足80%,请关注。
+
+ )}
+
))}