hb_zhgd_screen/js/pages/dataAnalysisOctober/projectManagement.js

628 lines
21 KiB
JavaScript
Raw Normal View History

2026-01-21 10:34:06 +08:00
// 项目管理分析页面
let expenditureTrendChart = null;
let expenditureProportionChart = null;
let projectRiskChart = null;
let riskAnalysisChart = null;
let civilSpecialtyChart = null;
let electricalSpecialtyChart = null;
// 页面初始化
$(document).ready(function () {
// 等待layui加载完成
layui.use(['laydate', 'layer'], function () {
initDateRange();
// 初始化项目信息模块的图表
initRiskAnalysisChart();
initCivilSpecialtyChart();
initElectricalSpecialtyChart();
// 初始化其他模块的图表
initExpenditureTrendChart();
initExpenditureProportionChart();
initProjectRiskChart();
initWarningTable();
// 窗口大小改变时重新调整图表
window.addEventListener('resize', debounce(() => {
if (riskAnalysisChart) riskAnalysisChart.resize();
if (civilSpecialtyChart) civilSpecialtyChart.resize();
if (electricalSpecialtyChart) electricalSpecialtyChart.resize();
if (expenditureTrendChart) expenditureTrendChart.resize();
if (expenditureProportionChart) expenditureProportionChart.resize();
if (projectRiskChart) projectRiskChart.resize();
}, 300));
2025-10-21 13:38:20 +08:00
});
2025-10-17 14:57:09 +08:00
});
2026-01-21 10:34:06 +08:00
// 防抖函数
function debounce(fn, delay) {
let t = null;
return function () {
clearTimeout(t);
t = setTimeout(() => fn.apply(this, arguments), delay);
};
2025-10-17 14:57:09 +08:00
}
2026-01-21 10:34:06 +08:00
// 初始化日期范围选择器
function initDateRange() {
const laydate = layui.laydate;
laydate.render({
elem: '#dateRange',
type: 'date',
range: true,
format: 'yyyy-MM-dd',
value: getDefaultDateRange(),
done: function (value) {
// 日期选择完成后的回调
2025-10-18 16:58:04 +08:00
}
2025-10-17 14:57:09 +08:00
});
}
2026-01-21 10:34:06 +08:00
// 获取默认日期范围最近7天
function getDefaultDateRange() {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 6);
return formatDate(start) + ' ~ ' + formatDate(end);
2025-10-17 14:57:09 +08:00
}
2026-01-21 10:34:06 +08:00
// 格式化日期
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return year + '-' + month + '-' + day;
2025-10-17 14:57:09 +08:00
}
2026-01-21 10:34:06 +08:00
// 查询数据
function queryData() {
const dateRange = $('#dateRange').val();
if (!dateRange) {
layui.layer.msg('请选择日期范围', { icon: 0 });
return;
}
// 这里可以调用API获取数据
// 目前使用模拟数据
loadData(dateRange);
layui.layer.msg('查询成功', { icon: 1, time: 1000 });
2025-10-17 14:57:09 +08:00
}
2026-01-21 10:34:06 +08:00
// 加载数据
function loadData(dateRange) {
// 模拟数据加载
// 实际应该调用API
updateCharts();
2025-10-18 16:58:04 +08:00
}
2026-01-21 10:34:06 +08:00
// 初始化风险分析饼图
function initRiskAnalysisChart() {
riskAnalysisChart = echarts.init(document.getElementById('riskAnalysisChart'));
2025-10-17 14:57:09 +08:00
const option = {
tooltip: {
2026-01-21 10:34:06 +08:00
trigger: 'item',
backgroundColor: 'rgba(19, 51, 55, 0.9)',
borderColor: 'rgba(0, 254, 252, 0.5)',
2025-10-17 14:57:09 +08:00
borderWidth: 1,
2026-01-21 10:34:06 +08:00
textStyle: { color: '#fff' },
formatter: '{b}: {c}个 ({d}%)'
2025-10-17 14:57:09 +08:00
},
legend: {
2026-01-21 10:34:06 +08:00
orient: 'vertical',
right: 10,
top: 'center',
textStyle: { color: '#fff', fontSize: 12 },
itemWidth: 12,
2025-10-17 14:57:09 +08:00
itemHeight: 8,
2026-01-21 10:34:06 +08:00
itemGap: 10,
formatter: function(name) {
const item = option.series[0].data.find(d => d.name === name);
return name + ' ' + (item ? item.value + '%' : '');
}
2025-10-17 14:57:09 +08:00
},
series: [
{
2026-01-21 10:34:06 +08:00
name: '风险分析',
type: 'pie',
radius: ['40%', '70%'],
center: ['35%', '50%'],
2025-10-17 14:57:09 +08:00
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 5,
2026-01-21 10:34:06 +08:00
borderColor: 'rgba(13, 34, 37, 0.8)',
borderWidth: 2
2025-10-17 14:57:09 +08:00
},
label: {
2026-01-21 10:34:06 +08:00
show: false
2025-10-17 14:57:09 +08:00
},
emphasis: {
label: {
2026-01-21 10:34:06 +08:00
show: true,
fontSize: 14,
fontWeight: 'bold',
color: '#fff'
}
2025-10-17 14:57:09 +08:00
},
2026-01-21 10:34:06 +08:00
data: [
{ value: 36, name: '二级风险', itemStyle: { color: '#4A90E2' } },
{ value: 36, name: '三级风险', itemStyle: { color: '#1CFFA3' } },
{ value: 15.6, name: '四级风险', itemStyle: { color: '#00FEFC' } },
{ value: 8.4, name: '五级风险', itemStyle: { color: '#FF9C65' } }
]
}
2025-10-17 14:57:09 +08:00
],
2026-01-21 10:34:06 +08:00
graphic: [
{
type: 'text',
left: '35%',
top: '75%',
style: {
text: '今日风险数量 60个',
textAlign: 'center',
fill: '#fff',
fontSize: 13
}
}
]
2025-10-17 14:57:09 +08:00
};
2026-01-21 10:34:06 +08:00
riskAnalysisChart.setOption(option);
2025-10-17 14:57:09 +08:00
}
2026-01-21 10:34:06 +08:00
// 初始化土建专业折线图
function initCivilSpecialtyChart() {
civilSpecialtyChart = echarts.init(document.getElementById('civilSpecialtyChart'));
const dates = ['XXXX-XX-01', 'XXXX-XX-02', 'XXXX-XX-03', 'XXXX-XX-04', 'XXXX-XX-05', 'XXXX-XX-06', 'XXXX-XX-07', 'XXXX-XX-08'];
const planned = [8, 9, 10, 12, 11, 13, 12, 14];
const actual = [6, 7, 8, 6, 9, 10, 11, 12];
2025-10-28 09:56:57 +08:00
2025-10-18 16:58:04 +08:00
const option = {
2025-10-17 14:57:09 +08:00
tooltip: {
2026-01-21 10:34:06 +08:00
trigger: 'axis',
axisPointer: { type: 'line' },
backgroundColor: 'rgba(19, 51, 55, 0.9)',
borderColor: 'rgba(0, 254, 252, 0.5)',
2025-10-17 14:57:09 +08:00
borderWidth: 1,
2026-01-21 10:34:06 +08:00
textStyle: { color: '#fff' },
formatter: function (params) {
if (!params || !params.length) return '';
let html = `<div style="font-weight:bold;margin-bottom:6px;">${params[0].name}</div>`;
params.forEach(p => {
html += `
<div style="display:flex;align-items:center;margin-bottom:2px;">
<span style="display:inline-block;width:10px;height:10px;background:${p.color};margin-right:8px;border-radius:2px;"></span>
<span style="color:#B9D6D9;margin-right:6px;">${p.seriesName}</span>
<span style="color:#fff;margin-left:auto;font-weight:bold;">${p.value}</span>
</div>`;
});
return html;
}
2025-10-17 14:57:09 +08:00
},
legend: {
2026-01-21 10:34:06 +08:00
data: ['计划', '实际'],
top: 8,
right: 20,
textStyle: { color: '#fff', fontSize: 12 },
itemWidth: 12,
itemHeight: 8,
itemGap: 15
2025-10-17 14:57:09 +08:00
},
grid: {
2026-01-21 10:34:06 +08:00
top: '20%',
left: '8%',
right: '6%',
bottom: '15%'
},
xAxis: {
type: 'category',
data: dates,
axisLabel: { color: '#fff', fontSize: 10 },
axisLine: { lineStyle: { color: '#5A6E71' } },
axisTick: { show: false }
},
yAxis: {
type: 'value',
min: 0,
max: 20,
axisLabel: { color: '#fff', fontSize: 10 },
axisLine: { lineStyle: { color: '#5A6E71' } },
splitLine: {
lineStyle: { color: 'rgba(90, 110, 113, 0.3)', type: 'dashed' }
}
2025-10-17 14:57:09 +08:00
},
2025-10-18 16:58:04 +08:00
series: [
{
2026-01-21 10:34:06 +08:00
name: '计划',
type: 'line',
data: planned,
2025-10-18 16:58:04 +08:00
smooth: true,
2026-01-21 10:34:06 +08:00
lineStyle: { width: 2, color: '#00FEFC' },
itemStyle: { color: '#00FEFC' },
2025-10-18 16:58:04 +08:00
areaStyle: {
2026-01-21 10:34:06 +08:00
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(0, 254, 252, 0.3)' },
{ offset: 1, color: 'rgba(0, 254, 252, 0.05)' }
])
}
2025-10-17 14:57:09 +08:00
},
2025-10-18 16:58:04 +08:00
{
2026-01-21 10:34:06 +08:00
name: '实际',
type: 'line',
data: actual,
2025-10-18 16:58:04 +08:00
smooth: true,
2026-01-21 10:34:06 +08:00
lineStyle: { width: 2, color: '#1CFFA3' },
itemStyle: { color: '#1CFFA3' },
2025-10-18 16:58:04 +08:00
areaStyle: {
2026-01-21 10:34:06 +08:00
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(28, 255, 163, 0.3)' },
{ offset: 1, color: 'rgba(28, 255, 163, 0.05)' }
])
}
}
]
2025-10-17 14:57:09 +08:00
};
2026-01-21 10:34:06 +08:00
civilSpecialtyChart.setOption(option);
2025-10-17 14:57:09 +08:00
}
2026-01-21 10:34:06 +08:00
// 初始化电气专业折线图
function initElectricalSpecialtyChart() {
electricalSpecialtyChart = echarts.init(document.getElementById('electricalSpecialtyChart'));
const dates = ['XXXX-XX-01', 'XXXX-XX-02', 'XXXX-XX-03', 'XXXX-XX-04', 'XXXX-XX-05', 'XXXX-XX-06', 'XXXX-XX-07', 'XXXX-XX-08'];
const planned = [8, 9, 10, 12, 11, 13, 12, 14];
const actual = [6, 7, 8, 6, 9, 10, 11, 12];
2025-10-18 16:58:04 +08:00
const option = {
2025-10-17 14:57:09 +08:00
tooltip: {
2026-01-21 10:34:06 +08:00
trigger: 'axis',
axisPointer: { type: 'line' },
backgroundColor: 'rgba(19, 51, 55, 0.9)',
borderColor: 'rgba(0, 254, 252, 0.5)',
2025-10-17 14:57:09 +08:00
borderWidth: 1,
2026-01-21 10:34:06 +08:00
textStyle: { color: '#fff' },
formatter: function (params) {
if (!params || !params.length) return '';
let html = `<div style="font-weight:bold;margin-bottom:6px;">${params[0].name}</div>`;
params.forEach(p => {
html += `
<div style="display:flex;align-items:center;margin-bottom:2px;">
<span style="display:inline-block;width:10px;height:10px;background:${p.color};margin-right:8px;border-radius:2px;"></span>
<span style="color:#B9D6D9;margin-right:6px;">${p.seriesName}</span>
<span style="color:#fff;margin-left:auto;font-weight:bold;">${p.value}</span>
</div>`;
});
return html;
}
2025-10-17 14:57:09 +08:00
},
legend: {
2026-01-21 10:34:06 +08:00
data: ['计划', '实际'],
top: 8,
right: 20,
textStyle: { color: '#fff', fontSize: 12 },
itemWidth: 12,
itemHeight: 8,
itemGap: 15
2025-10-17 14:57:09 +08:00
},
grid: {
2026-01-21 10:34:06 +08:00
top: '20%',
left: '8%',
right: '6%',
bottom: '15%'
},
xAxis: {
type: 'category',
data: dates,
axisLabel: { color: '#fff', fontSize: 10 },
axisLine: { lineStyle: { color: '#5A6E71' } },
axisTick: { show: false }
},
yAxis: {
type: 'value',
min: 0,
max: 20,
axisLabel: { color: '#fff', fontSize: 10 },
axisLine: { lineStyle: { color: '#5A6E71' } },
splitLine: {
lineStyle: { color: 'rgba(90, 110, 113, 0.3)', type: 'dashed' }
}
2025-10-17 14:57:09 +08:00
},
2025-10-18 16:58:04 +08:00
series: [
{
2026-01-21 10:34:06 +08:00
name: '计划',
type: 'line',
data: planned,
2025-10-18 16:58:04 +08:00
smooth: true,
2026-01-21 10:34:06 +08:00
lineStyle: { width: 2, color: '#00FEFC' },
itemStyle: { color: '#00FEFC' },
2025-10-18 16:58:04 +08:00
areaStyle: {
2026-01-21 10:34:06 +08:00
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(0, 254, 252, 0.3)' },
{ offset: 1, color: 'rgba(0, 254, 252, 0.05)' }
])
}
2025-10-17 14:57:09 +08:00
},
2025-10-18 16:58:04 +08:00
{
2026-01-21 10:34:06 +08:00
name: '实际',
type: 'line',
data: actual,
2025-10-18 16:58:04 +08:00
smooth: true,
2026-01-21 10:34:06 +08:00
lineStyle: { width: 2, color: '#1CFFA3' },
itemStyle: { color: '#1CFFA3' },
2025-10-18 16:58:04 +08:00
areaStyle: {
2026-01-21 10:34:06 +08:00
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(28, 255, 163, 0.3)' },
{ offset: 1, color: 'rgba(28, 255, 163, 0.05)' }
])
}
}
]
2025-10-17 14:57:09 +08:00
};
2026-01-21 10:34:06 +08:00
electricalSpecialtyChart.setOption(option);
2025-10-17 14:57:09 +08:00
}
2026-01-21 10:34:06 +08:00
// 初始化项目支出趋势图表
function initExpenditureTrendChart() {
expenditureTrendChart = echarts.init(document.getElementById('expenditureTrendChart'));
const dates = ['XXXX-XX-01', 'XXXX-XX-02', 'XXXX-XX-03', 'XXXX-XX-04', 'XXXX-XX-05', 'XXXX-XX-06', 'XXXX-XX-07', 'XXXX-XX-08'];
const expenditure = [48, 45, 42, 32, 35, 38, 30, 28];
2025-10-17 14:57:09 +08:00
2025-10-18 16:58:04 +08:00
const option = {
2025-10-17 14:57:09 +08:00
tooltip: {
2026-01-21 10:34:06 +08:00
trigger: 'axis',
axisPointer: { type: 'line' },
backgroundColor: 'rgba(19, 51, 55, 0.9)',
borderColor: 'rgba(0, 254, 252, 0.5)',
2025-10-17 14:57:09 +08:00
borderWidth: 1,
2026-01-21 10:34:06 +08:00
textStyle: { color: '#fff' }
2025-10-17 14:57:09 +08:00
},
grid: {
2026-01-21 10:34:06 +08:00
top: '15%',
left: '8%',
right: '6%',
bottom: '15%'
2025-10-17 14:57:09 +08:00
},
2026-01-21 10:34:06 +08:00
xAxis: {
type: 'category',
data: dates,
axisLabel: { color: '#fff', fontSize: 11 },
axisLine: { lineStyle: { color: '#5A6E71' } },
axisTick: { show: false }
},
yAxis: {
type: 'value',
min: 0,
max: 60,
interval: 10,
axisLabel: { color: '#fff', fontSize: 11 },
axisLine: { lineStyle: { color: '#5A6E71' } },
splitLine: {
lineStyle: { color: 'rgba(90, 110, 113, 0.3)', type: 'dashed' }
}
},
series: [
2025-10-18 16:58:04 +08:00
{
2026-01-21 10:34:06 +08:00
type: 'line',
data: expenditure,
smooth: true,
lineStyle: { width: 2, color: '#00FEFC' },
itemStyle: { color: '#00FEFC' },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(0, 254, 252, 0.4)' },
{ offset: 1, color: 'rgba(0, 254, 252, 0.05)' }
])
}
}
]
};
2025-10-21 13:38:20 +08:00
2026-01-21 10:34:06 +08:00
expenditureTrendChart.setOption(option);
}
// 初始化项目支出占比图表(环形图)
function initExpenditureProportionChart() {
expenditureProportionChart = echarts.init(document.getElementById('expenditureProportionChart'));
const option = {
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(19, 51, 55, 0.9)',
borderColor: 'rgba(0, 254, 252, 0.5)',
borderWidth: 1,
textStyle: { color: '#fff' },
formatter: '{b}: {d}%'
},
legend: {
bottom: 10,
left: 'center',
textStyle: { color: '#fff', fontSize: 12 },
itemWidth: 12,
itemHeight: 8,
itemGap: 15
},
2025-10-18 16:58:04 +08:00
series: [
{
2026-01-21 10:34:06 +08:00
name: '支出占比',
type: 'pie',
radius: ['50%', '70%'],
center: ['50%', '45%'],
avoidLabelOverlap: false,
2025-10-18 16:58:04 +08:00
itemStyle: {
2026-01-21 10:34:06 +08:00
borderRadius: 5,
borderColor: 'rgba(13, 34, 37, 0.8)',
borderWidth: 2
2025-10-18 16:58:04 +08:00
},
2026-01-21 10:34:06 +08:00
label: {
2025-10-18 16:58:04 +08:00
show: true,
2026-01-21 10:34:06 +08:00
formatter: '{b}\n{d}%',
color: '#fff',
fontSize: 12,
fontWeight: 'bold'
2025-10-18 16:58:04 +08:00
},
2026-01-21 10:34:06 +08:00
emphasis: {
label: {
show: true,
fontSize: 14,
fontWeight: 'bold'
}
2025-10-18 16:58:04 +08:00
},
2026-01-21 10:34:06 +08:00
data: [
{ value: 20, name: '支出', itemStyle: { color: '#00FEFC' } },
{ value: 80, name: '预算剩余', itemStyle: { color: '#FFFFFF' } }
]
}
]
2025-10-17 14:57:09 +08:00
};
2026-01-21 10:34:06 +08:00
expenditureProportionChart.setOption(option);
2025-10-17 14:57:09 +08:00
}
2026-01-21 10:34:06 +08:00
// 初始化项目风险图表
function initProjectRiskChart() {
projectRiskChart = echarts.init(document.getElementById('projectRiskChart'));
2025-10-17 14:57:09 +08:00
const option = {
tooltip: {
2026-01-21 10:34:06 +08:00
trigger: 'item',
backgroundColor: 'rgba(19, 51, 55, 0.9)',
borderColor: 'rgba(0, 254, 252, 0.5)',
2025-10-17 14:57:09 +08:00
borderWidth: 1,
2026-01-21 10:34:06 +08:00
textStyle: { color: '#fff' },
formatter: '{b}: {c}个 ({d}%)'
2025-10-17 14:57:09 +08:00
},
legend: {
2026-01-21 10:34:06 +08:00
orient: 'vertical',
right: 10,
top: 'center',
textStyle: { color: '#fff', fontSize: 12 },
itemWidth: 12,
2025-10-17 14:57:09 +08:00
itemHeight: 8,
2026-01-21 10:34:06 +08:00
itemGap: 10,
formatter: function(name) {
const item = option.series[0].data.find(d => d.name === name);
return name + ' ' + (item ? item.value + '%' : '');
}
2025-10-17 14:57:09 +08:00
},
series: [
{
2026-01-21 10:34:06 +08:00
name: '项目风险',
type: 'pie',
radius: ['40%', '70%'],
center: ['35%', '50%'],
2025-10-17 14:57:09 +08:00
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 5,
2026-01-21 10:34:06 +08:00
borderColor: 'rgba(13, 34, 37, 0.8)',
borderWidth: 2
2025-10-17 14:57:09 +08:00
},
label: {
2026-01-21 10:34:06 +08:00
show: false
2025-10-17 14:57:09 +08:00
},
emphasis: {
label: {
2026-01-21 10:34:06 +08:00
show: true,
fontSize: 14,
fontWeight: 'bold',
color: '#fff'
}
2025-10-17 14:57:09 +08:00
},
2026-01-21 10:34:06 +08:00
data: [
{ value: 60, name: '二级风险', itemStyle: { color: '#4A90E2' } },
{ value: 60, name: '三级风险', itemStyle: { color: '#1CFFA3' } },
{ value: 26, name: '四级风险', itemStyle: { color: '#00FEFC' } },
{ value: 14, name: '五级风险', itemStyle: { color: '#FF9C65' } }
]
}
2025-10-17 14:57:09 +08:00
],
2026-01-21 10:34:06 +08:00
graphic: [
{
type: 'text',
left: '35%',
top: '75%',
style: {
text: '总风险数量 60个',
textAlign: 'center',
fill: '#fff',
fontSize: 13
}
}
]
2025-10-17 14:57:09 +08:00
};
2026-01-21 10:34:06 +08:00
projectRiskChart.setOption(option);
}
// 初始化分析预警表格
function initWarningTable() {
const warnings = [
{ id: '01', time: 'XXXX-XX-XX', type: '进度', content: 'XXXXXXXXXXXXXX', action: '制定策略' },
{ id: '02', time: 'XXXX-XX-XX', type: '进度', content: 'XXXXXXXXXXXXXX', action: '制定策略' },
{ id: '03', time: 'XXXX-XX-XX', type: '成本', content: 'XXXXXXXXXXXXXX', action: '制定策略' },
{ id: '04', time: 'XXXX-XX-XX', type: '风险', content: 'XXXXXXXXXXXXXX', action: '制定策略' },
{ id: '05', time: 'XXXX-XX-XX', type: '进度', content: 'XXXXXXXXXXXXXX', action: '制定策略' }
];
renderWarningTable(warnings);
}
// 渲染分析预警表格
function renderWarningTable(warnings) {
const tbody = $('#warningTableBody');
tbody.empty();
warnings.forEach(warning => {
const row = `
<tr>
<td>${warning.id}</td>
<td>${warning.time}</td>
<td>${warning.type}</td>
<td>${warning.content}</td>
<td><span class="warning-action">${warning.action}</span></td>
</tr>
`;
tbody.append(row);
2025-10-17 14:57:09 +08:00
});
}
2026-01-21 10:34:06 +08:00
// 筛选分析预警
let currentWarningFilter = 'all';
function filterWarning(type) {
currentWarningFilter = type;
// 更新标签状态
$('.warning-tab').removeClass('active');
$(`.warning-tab[data-type="${type}"]`).addClass('active');
// 重新渲染表格
const allWarnings = [
{ id: '01', time: 'XXXX-XX-XX', type: '进度', content: 'XXXXXXXXXXXXXX', action: '制定策略' },
{ id: '02', time: 'XXXX-XX-XX', type: '进度', content: 'XXXXXXXXXXXXXX', action: '制定策略' },
{ id: '03', time: 'XXXX-XX-XX', type: '成本', content: 'XXXXXXXXXXXXXX', action: '制定策略' },
{ id: '04', time: 'XXXX-XX-XX', type: '风险', content: 'XXXXXXXXXXXXXX', action: '制定策略' },
{ id: '05', time: 'XXXX-XX-XX', type: '进度', content: 'XXXXXXXXXXXXXX', action: '制定策略' }
];
const filteredWarnings = type === 'all'
? allWarnings
: allWarnings.filter(item => item.type === type);
renderWarningTable(filteredWarnings);
}
// 更新图表
function updateCharts() {
// 这里可以重新加载图表数据
// 实际应该从API获取数据并更新图表
if (riskAnalysisChart) riskAnalysisChart.resize();
if (civilSpecialtyChart) civilSpecialtyChart.resize();
if (electricalSpecialtyChart) electricalSpecialtyChart.resize();
if (expenditureTrendChart) expenditureTrendChart.resize();
if (expenditureProportionChart) expenditureProportionChart.resize();
if (projectRiskChart) projectRiskChart.resize();
}