抓变化

This commit is contained in:
jiang 2025-06-04 17:52:17 +08:00
parent 7d839712ca
commit bed25c9317
4 changed files with 599 additions and 399 deletions

48
main.js
View File

@ -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 || []);
}
});
});
});

View File

@ -7,6 +7,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
// 数据库操作 // 数据库操作
getProjects: () => ipcRenderer.invoke('get-projects'), getProjects: () => ipcRenderer.invoke('get-projects'),
getProjectsRange: () => ipcRenderer.invoke('get-projects-range'),
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),

View File

@ -18,6 +18,7 @@ import ProjectWarningView from "./components/ProjectWarningView";
function App() { function App() {
// 状态管理 // 状态管理
const [projects, setProjects] = useState([]); const [projects, setProjects] = useState([]);
const [projectsDataRange, setProjectsDataRange] = useState([]);
const [treeData, setTreeData] = useState([]); const [treeData, setTreeData] = useState([]);
const [selectedNode, setSelectedNode] = useState(null); const [selectedNode, setSelectedNode] = useState(null);
const [selectedProjects, setSelectedProjects] = useState([]); const [selectedProjects, setSelectedProjects] = useState([]);
@ -42,6 +43,9 @@ function App() {
const projectsData = await window.electronAPI.getProjects(); const projectsData = await window.electronAPI.getProjects();
setProjects(projectsData); setProjects(projectsData);
const projectsDataRange = await window.electronAPI.getProjectsRange();
setProjectsDataRange(projectsDataRange);
// 获取树状结构数据 // 获取树状结构数据
const treeStructureData = await window.electronAPI.getTreeStructure(); const treeStructureData = await window.electronAPI.getTreeStructure();
@ -397,6 +401,7 @@ function App() {
{/* 树状结构区 */} {/* 树状结构区 */}
<ProjectWarningView <ProjectWarningView
projects={projects} projects={projects}
projectsDataRange={projectsDataRange}
loading={loading} loading={loading}
onClick={handleProjectClick} // 传入点击事件处理函数 onClick={handleProjectClick} // 传入点击事件处理函数
/> />

View File

@ -1,26 +1,129 @@
import React from 'react'; 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 = [ const warningFields = [
{key: 'new_team', label: '新班组', message: '存在新班组,请做好班组入场管理。'}, {key: 'new_team', label: '新班组', message: '存在新班组,请做好班组入场管理。'},
{key: 'new_homework_content', label: '新的作业内容', message: '存在新的作业内容,请加强现场管控。'}, {key: 'new_homework_content', label: '新的作业内容', message: '存在新的作业内容,请加强现场管控。'},
{ key: 'change_homework_method', label: '改变作业方法', message: '存在改变作业方法,请及时核查施工方案编审、方案交底及人员、机具准备情况。' }, {
{ key: 'changes_geographical', label: '地理环境的变化', message: '存在作业环境的变化,请及时核查施工方案编审、方案交底及人员、机具准备情况。' }, key: 'change_homework_method',
label: '改变作业方法',
message: '存在改变作业方法,请及时核查施工方案编审、方案交底及人员、机具准备情况。'
},
{
key: 'changes_geographical',
label: '地理环境的变化',
message: '存在作业环境的变化,请及时核查施工方案编审、方案交底及人员、机具准备情况。'
},
{key: 'changes_meteorological', label: '气象环境的变化', message: '存在气象预警,请关注天气变化,做好应对措施。'}, {key: 'changes_meteorological', label: '气象环境的变化', message: '存在气象预警,请关注天气变化,做好应对措施。'},
{key: 'changes_social', label: '社会环境的变化', message: '存在社会环境变化,请合理安排作业计划,避免人员失控。'}, {key: 'changes_social', label: '社会环境的变化', message: '存在社会环境变化,请合理安排作业计划,避免人员失控。'},
{ key: 'changes_management', label: '管理要求的变化', message: '存在管理要求的变化,请加强现场巡查力度,严防无计划作业。' }, {
key: 'changes_management',
label: '管理要求的变化',
message: '存在管理要求的变化,请加强现场巡查力度,严防无计划作业。'
},
{key: 'changes_homework_plan', label: '作业计划的变化', message: '存在作业计划的变化,请做好施工力量配备。'}, {key: 'changes_homework_plan', label: '作业计划的变化', message: '存在作业计划的变化,请做好施工力量配备。'},
{key: 'changes_management_personnel', label: '管理人员的变化', message: '存在管理人员的变化,请加强现场管控。'}, {key: 'changes_management_personnel', label: '管理人员的变化', message: '存在管理人员的变化,请加强现场管控。'},
]; ];
const hasWarnings = (item) => { const hasWarnings = (item) => {
if (typeof item !== 'object') return false; if (typeof item !== 'object') return false;
if (item.new_members || item.new_high_altitude || item.new_hired_general) return true; 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 ( return (
<div <div
@ -130,6 +233,49 @@ const ProjectWarningView = ({ projects = [], loading = false,onClick}) => {
</div> </div>
) : null ) : null
)} )}
{mismatchWarning(item) && (
<div
style={{
fontSize: '14px',
color: '#e64c3c',
fontWeight: '600',
borderLeft: '4px solid #e64c3c',
paddingLeft: '10px',
}}
>
工程进展与下周作业计划不匹配请注意
</div>
)}
{projectsRangeWarning(item) && (
<div
style={{
fontSize: '14px',
color: '#e64c3c',
fontWeight: '600',
borderLeft: '4px solid #e64c3c',
paddingLeft: '10px',
}}
>
工程参建人数与作业内容不匹配请注意
</div>
)}
{productionPlanScaleWarning(item) && (
<div
style={{
fontSize: '14px',
color: '#e64c3c',
fontWeight: '600',
borderLeft: '4px solid #e64c3c',
paddingLeft: '10px',
}}
>
投产计划30天内导线展放进度不足80%请关注
</div>
)}
</div> </div>
))} ))}
</div> </div>