hb_zhgd_screen/js/pages/dataAnalysisOctober/projectManagement.js

1090 lines
36 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 项目管理分析页面
let expenditureTrendChart = null;
let expenditureProportionChart = null;
let projectRiskChart = null;
let riskAnalysisChart = null;
let civilSpecialtyChart = null;
let electricalSpecialtyChart = null;
let bidCode = parent.parent.$('#bidPro').val();
// 获取当月第一天和最后一天
function getCurrentMonthRange() {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth();
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
const startDate = formatDate(firstDay);
const endDate = formatDate(lastDay);
return { startDate, endDate };
}
const currentMonth = getCurrentMonthRange();
let queryParams = {
projectId: bidCode,
startTime: currentMonth.startDate,
endTime: currentMonth.endDate,
};
// 页面初始化
$(document).ready(function () {
// 等待layui加载完成
layui.use(['laydate', 'layer'], function () {
initDateRange();
// 初始化项目信息模块的图表
initRiskAnalysisChart();
initCivilSpecialtyChart();
initElectricalSpecialtyChart();
// 初始化其他模块的图表
initExpenditureTrendChart();
initExpenditureProportionChart();
initProjectRiskChart();
initWarningTable();
// 页面初始化时,按默认当月日期加载一次接口数据
refreshAllModules();
// 窗口大小改变时重新调整图表
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));
});
});
// 防抖函数
function debounce(fn, delay) {
let t = null;
return function () {
clearTimeout(t);
t = setTimeout(() => fn.apply(this, arguments), delay);
};
}
// 获取左上角工序进度
function getProcessProgress() {
const url = commonUrl + 'screen/largeScreen/sjNewProManage/getProProgress'
+ '?bidCode=' + (bidCode || '')
+ '&startTime=' + (queryParams.startTime || '')
+ '&endTime=' + (queryParams.endTime || '');
ajaxRequestGet(url, 'GET', true, function () {
}, function (result) {
if (result && result.data) {
updateProcessProgress(result.data);
} else if (result) {
updateProcessProgress(result);
}
}, aqEnnable);
}
// 获取左上角风险分析饼图数据
function getRiskAnalysisChartData() {
const url = commonUrl + 'screen/largeScreen/sjNewProManage/getRiskData'
+ '?bidCode=' + (bidCode || '')
+ '&startTime=' + (queryParams.startTime || '')
+ '&endTime=' + (queryParams.endTime || '');
ajaxRequestGet(url, 'GET', true, function () {
}, function (result) {
if (result) {
updateRiskAnalysisChart(result);
} else if (result && result.data) {
updateRiskAnalysisChart(result.data);
}
}, aqEnnable);
}
// 获取右上侧折线图数据
// 获取土建和电气专业图表数据
function getLineChartData() {
const url = commonUrl + 'screen/largeScreen/sjNewProManage/getProgressList'
+ '?bidCode=' + (bidCode || '')
+ '&startTime=' + (queryParams.startTime || '')
+ '&endTime=' + (queryParams.endTime || '');
ajaxRequestGet(url, 'GET', true, function () {
}, function (result) {
if (result && result.data) {
updateSpecialtyCharts(result.data);
} else if (result) {
updateSpecialtyCharts(result);
}
}, aqEnnable);
}
// 获取项目支出趋势和项目支出占比
function getExpenditureTrendAndProportion() {
const url = commonUrl + 'screen/largeScreen/sjNewProManage/getProjectCost'
+ '?bidCode=' + (bidCode || '')
+ '&startTime=' + (queryParams.startTime || '')
+ '&endTime=' + (queryParams.endTime || '');
ajaxRequestGet(url, 'GET', true, function () {
},
function (result) {
if (result && result.data) {
updateExpenditureTrendChart(result.data);
updateExpenditureProportionChart(result.data);
} else if (result) {
updateExpenditureTrendChart(result);
updateExpenditureProportionChart(result);
}
},
aqEnnable);
}
// 获取项目风险饼图
function getProjectRiskChartData() {
const url = commonUrl + 'screen/largeScreen/sjNewProManage/getProjectRisk'
+ '?bidCode=' + (bidCode || '')
+ '&startTime=' + (queryParams.startTime || '')
+ '&endTime=' + (queryParams.endTime || '');
ajaxRequestGet(url, 'GET', true, function () {
}, function (result) {
if (result && result.data) {
updateProjectRiskChart(result.data);
} else if (result) {
updateProjectRiskChart(result.data);
}
},
aqEnnable);
}
// 获取分析预警
function getAnalysisWarning(txType) {
// txType: "全部"传空字符串,其他传对应的中文
const txTypeParam = txType === 'all' ? '' : (txType === 'progress' ? '进度' : txType === 'cost' ? '成本' : txType === 'risk' ? '风险' : '');
const url = commonUrl + 'screen/largeScreen/sjNewProManage/getWarnList'
+ '?bidCode=' + (bidCode || '')
+ '&startTime=' + (queryParams.startTime || '')
+ '&endTime=' + (queryParams.endTime || '')
+ (txTypeParam ? '&txType=' + txTypeParam : '');
ajaxRequestGet(url, 'GET', true, function () {
}, function (result) {
console.log(result, '分析预警');
if (result && Array.isArray(result)) {
updateWarningTable(result);
} else if (result && result.data && Array.isArray(result.data)) {
updateWarningTable(result.data);
} else if (result && result.rows && Array.isArray(result.rows)) {
updateWarningTable(result.rows);
}
}, aqEnnable);
}
// 更新工序进度数据
function updateProcessProgress(data) {
if (!data) return;
// 土建工序进度
// bdGx: 本段工序(土建总工序)
// bdYwcGx: 本段已完成工序(土建已完成)
const civilTotal = data.bdGx || '0';
const civilCompleted = data.bdYwcGx || '0';
$('#civilTotal').text(civilTotal);
$('#civilCompleted').text(civilCompleted);
// 电气工序进度
// dqGx: 当前工序(电气总工序)
// dqYwcGx: 当前已完成工序(电气已完成)
const electricalTotal = data.dqGx || '0';
const electricalCompleted = data.dqYwcGx || '0';
$('#electricalTotal').text(electricalTotal);
$('#electricalCompleted').text(electricalCompleted);
}
// 刷新所有模块数据
function refreshAllModules() {
// 调用工序进度接口
getProcessProgress();
// 调用风险分析饼图数据接口
getRiskAnalysisChartData();
// 调用土建和电气专业图表数据接口
getLineChartData();
// 调用项目支出趋势和项目支出占比接口
getExpenditureTrendAndProportion();
// 调用项目风险饼图接口
getProjectRiskChartData();
// 调用分析预警接口
getAnalysisWarning(currentWarningFilter);
}
// 初始化日期范围选择器(逻辑与工程质量分析保持一致)
function initDateRange() {
const laydate = layui.laydate;
// 初始显示为当月范围
$('#dateRange').val(queryParams.startTime + ' ~ ' + queryParams.endTime);
laydate.render({
elem: '#dateRange',
type: 'date',
range: true,
format: 'yyyy-MM-dd',
theme: 'dark',
// laydate 内部使用 "起始 - 结束" 作为分隔符
value: queryParams.startTime + ' - ' + queryParams.endTime,
done: function (value) {
const resetToCurrentMonth = function () {
const cur = getCurrentMonthRange();
queryParams.startTime = cur.startDate;
queryParams.endTime = cur.endDate;
$('#dateRange').val(cur.startDate + ' ~ ' + cur.endDate);
refreshAllModules();
};
if (value && value.trim() !== '') {
const dates = value.split(' - ');
if (dates.length === 2) {
const startDate = dates[0].trim();
const endDateStr = dates[1].trim();
// 回显到输入框(使用 ~,与质量分析一致)
$('#dateRange').val(startDate + ' ~ ' + endDateStr);
// 更新查询参数
queryParams.startTime = startDate;
queryParams.endTime = endDateStr;
// 日期变化后,重新拉取接口数据
refreshAllModules();
} else {
resetToCurrentMonth();
}
} else {
resetToCurrentMonth();
}
}
});
}
// 格式化日期
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;
}
// 查询数据
function queryData() {
const dateRange = $('#dateRange').val();
if (!dateRange) {
layui.layer.msg('请选择日期范围', { icon: 0 });
return;
}
// 点击查询按钮时,基于当前日期范围刷新接口数据
refreshAllModules();
layui.layer.msg('查询成功', { icon: 1, time: 1000 });
}
// 加载数据
function loadData(dateRange) {
// 模拟数据加载
// 实际应该调用API
updateCharts();
}
// 初始化风险分析饼图
function initRiskAnalysisChart() {
riskAnalysisChart = echarts.init(document.getElementById('riskAnalysisChart'));
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}: {c}个 ({d}%)'
},
legend: {
orient: 'vertical',
right: 10,
top: 'center',
textStyle: { color: '#fff', fontSize: 12 },
itemWidth: 12,
itemHeight: 8,
itemGap: 10,
formatter: function (name) {
const item = option.series[0].data.find(d => d.name === name);
return name + ' ' + (item ? item.value + '%' : '');
}
},
series: [
{
name: '风险分析',
type: 'pie',
radius: ['40%', '70%'],
center: ['35%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 5,
borderColor: 'rgba(13, 34, 37, 0.8)',
borderWidth: 2
},
label: {
show: false
},
emphasis: {
label: {
show: true,
fontSize: 14,
fontWeight: 'bold',
color: '#fff'
}
},
data: [
{ value: 0, name: '二级风险', itemStyle: { color: '#4A90E2' } },
{ value: 0, name: '三级风险', itemStyle: { color: '#1CFFA3' } },
{ value: 0, name: '四级风险', itemStyle: { color: '#00FEFC' } },
{ value: 0, name: '五级风险', itemStyle: { color: '#FF9C65' } }
]
}
],
graphic: [
{
type: 'text',
left: '35%',
top: '85%',
style: {
text: '今日风险数量 0个',
textAlign: 'center',
fill: '#fff',
fontSize: 13
}
}
]
};
riskAnalysisChart.setOption(option);
}
// 更新风险分析饼图数据
function updateRiskAnalysisChart(data) {
if (!riskAnalysisChart || !data) return;
// 获取各风险等级的数量
const twoRisk = parseInt(data.twoRisk || 0);
const threeRisk = parseInt(data.threeRisk || 0);
const fourRisk = parseInt(data.fourRisk || 0);
const fiveRisk = parseInt(data.fiveRisk || 0);
// 计算总风险数量
const totalRisk = twoRisk + threeRisk + fourRisk + fiveRisk;
// 计算百分比
let twoRiskPercent = 0;
let threeRiskPercent = 0;
let fourRiskPercent = 0;
let fiveRiskPercent = 0;
if (totalRisk > 0) {
twoRiskPercent = (twoRisk / totalRisk * 100).toFixed(1);
threeRiskPercent = (threeRisk / totalRisk * 100).toFixed(1);
fourRiskPercent = (fourRisk / totalRisk * 100).toFixed(1);
fiveRiskPercent = (fiveRisk / totalRisk * 100).toFixed(1);
}
// 更新图表数据
riskAnalysisChart.setOption({
series: [
{
data: [
{ value: parseFloat(twoRiskPercent), name: '二级风险', itemStyle: { color: '#4A90E2' } },
{ value: parseFloat(threeRiskPercent), name: '三级风险', itemStyle: { color: '#1CFFA3' } },
{ value: parseFloat(fourRiskPercent), name: '四级风险', itemStyle: { color: '#00FEFC' } },
{ value: parseFloat(fiveRiskPercent), name: '五级风险', itemStyle: { color: '#FF9C65' } }
]
}
],
graphic: [
{
type: 'text',
left: '35%',
top: '85%',
style: {
text: '今日风险数量 ' + totalRisk + '个',
textAlign: 'center',
fill: '#fff',
fontSize: 13
}
}
]
});
}
// 初始化土建专业双柱状图
function initCivilSpecialtyChart() {
civilSpecialtyChart = echarts.init(document.getElementById('civilSpecialtyChart'));
const option = {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
backgroundColor: 'rgba(19, 51, 55, 0.9)',
borderColor: 'rgba(0, 254, 252, 0.5)',
borderWidth: 1,
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;
}
},
legend: {
data: ['计划进度', '实际进度'],
top: 8,
right: 20,
textStyle: { color: '#fff', fontSize: 12 },
itemWidth: 12,
itemHeight: 8,
itemGap: 15
},
grid: {
top: '20%',
left: '8%',
right: '6%',
bottom: '15%'
},
xAxis: {
type: 'category',
data: [],
axisLabel: { color: '#fff', fontSize: 10, rotate: 0 },
axisLine: { lineStyle: { color: '#5A6E71' } },
axisTick: { show: false }
},
yAxis: {
type: 'value',
min: 0,
max: 100,
axisLabel: { color: '#fff', fontSize: 10, formatter: '{value}%' },
axisLine: { lineStyle: { color: '#5A6E71' } },
splitLine: {
lineStyle: { color: 'rgba(90, 110, 113, 0.3)', type: 'dashed' }
}
},
series: [
{
name: '计划进度',
type: 'bar',
data: [],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#4A90E2' },
{ offset: 1, color: '#357ABD' }
])
},
barWidth: '30%'
},
{
name: '实际进度',
type: 'bar',
data: [],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#1CFFA3' },
{ offset: 1, color: '#19CC8A' }
])
},
barWidth: '30%'
}
]
};
civilSpecialtyChart.setOption(option);
}
// 初始化电气专业双柱状图
function initElectricalSpecialtyChart() {
electricalSpecialtyChart = echarts.init(document.getElementById('electricalSpecialtyChart'));
const option = {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
backgroundColor: 'rgba(19, 51, 55, 0.9)',
borderColor: 'rgba(0, 254, 252, 0.5)',
borderWidth: 1,
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;
}
},
legend: {
data: ['计划进度', '实际进度'],
top: 8,
right: 20,
textStyle: { color: '#fff', fontSize: 12 },
itemWidth: 12,
itemHeight: 8,
itemGap: 15
},
grid: {
top: '20%',
left: '8%',
right: '6%',
bottom: '15%'
},
xAxis: {
type: 'category',
data: [],
axisLabel: { color: '#fff', fontSize: 10, rotate: 0 },
axisLine: { lineStyle: { color: '#5A6E71' } },
axisTick: { show: false }
},
yAxis: {
type: 'value',
min: 0,
max: 100,
axisLabel: { color: '#fff', fontSize: 10, formatter: '{value}%' },
axisLine: { lineStyle: { color: '#5A6E71' } },
splitLine: {
lineStyle: { color: 'rgba(90, 110, 113, 0.3)', type: 'dashed' }
}
},
series: [
{
name: '计划进度',
type: 'bar',
data: [],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#4A90E2' },
{ offset: 1, color: '#357ABD' }
])
},
barWidth: '30%'
},
{
name: '实际进度',
type: 'bar',
data: [],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#1CFFA3' },
{ offset: 1, color: '#19CC8A' }
])
},
barWidth: '30%'
}
]
};
electricalSpecialtyChart.setOption(option);
}
// 更新土建和电气专业图表数据
function updateSpecialtyCharts(data) {
if (!data) return;
// 更新土建专业图表
if (civilSpecialtyChart) {
const bdGx = data.bdGx || [];
const bdPlanProgress = (data.bdPlanProgress || []).map(val => parseFloat(val) || 0);
const bdProgress = (data.bdProgress || []).map(val => parseFloat(val) || 0);
civilSpecialtyChart.setOption({
xAxis: {
data: bdGx
},
series: [
{
name: '计划进度',
data: bdPlanProgress
},
{
name: '实际进度',
data: bdProgress
}
]
});
}
// 更新电气专业图表
if (electricalSpecialtyChart) {
const dqGx = data.dqGx || [];
const dqPlanProgress = (data.dqPlanProgress || []).map(val => parseFloat(val) || 0);
const dqProgress = (data.dqProgress || []).map(val => parseFloat(val) || 0);
electricalSpecialtyChart.setOption({
xAxis: {
data: dqGx
},
series: [
{
name: '计划进度',
data: dqPlanProgress
},
{
name: '实际进度',
data: dqProgress
}
]
});
}
}
// 初始化项目支出趋势图表
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];
const option = {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'line' },
backgroundColor: 'rgba(19, 51, 55, 0.9)',
borderColor: 'rgba(0, 254, 252, 0.5)',
borderWidth: 1,
textStyle: { color: '#fff' }
},
grid: {
top: '15%',
left: '8%',
right: '6%',
bottom: '15%'
},
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: [
{
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)' }
])
}
}
]
};
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
},
series: [
{
name: '支出占比',
type: 'pie',
radius: ['50%', '70%'],
center: ['50%', '45%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 5,
borderColor: 'rgba(13, 34, 37, 0.8)',
borderWidth: 2
},
label: {
show: true,
formatter: '{b}\n{d}%',
color: '#fff',
fontSize: 12,
fontWeight: 'bold'
},
emphasis: {
label: {
show: true,
fontSize: 14,
fontWeight: 'bold'
}
},
data: [
{ value: 0, name: '支出', itemStyle: { color: '#00FEFC' } },
{ value: 0, name: '预算剩余', itemStyle: { color: '#FFFFFF' } }
]
}
]
};
expenditureProportionChart.setOption(option);
}
// 更新项目支出趋势图表
function updateExpenditureTrendChart(data) {
if (!expenditureTrendChart || !data) return;
// 格式化时间列表:从 "2026-01-01" 转换为 "01-01"
const timeList = (data.timeList || []).map(dateStr => {
if (!dateStr) return '';
const parts = dateStr.split('-');
if (parts.length >= 2) {
return parts[1] + '-' + parts[2];
}
return dateStr;
});
// 转换数值列表为数字
const valueList = (data.valueList || []).map(val => parseFloat(val) || 0);
// 确保两个数组长度一致,取最小长度
const minLength = Math.min(timeList.length, valueList.length);
const finalTimeList = timeList.slice(0, minLength);
const finalValueList = valueList.slice(0, minLength);
// 计算最大值用于设置y轴范围
const maxValue = finalValueList.length > 0 ? Math.max(...finalValueList, 0) : 0;
const yAxisMax = maxValue > 0 ? Math.ceil(maxValue * 1.2) : 100;
expenditureTrendChart.setOption({
xAxis: {
data: finalTimeList
},
yAxis: {
max: yAxisMax,
interval: Math.ceil(yAxisMax / 5)
},
series: [
{
data: finalValueList
}
]
});
}
// 更新项目支出占比图表
function updateExpenditureProportionChart(data) {
if (!expenditureProportionChart || !data) return;
// allCost 是预算proCount 是支出
const budget = parseFloat(data.allCost || 0);
const expenditure = parseFloat(data.proCount || 0);
const remaining = budget - expenditure;
// 计算百分比
let expenditurePercent = 0;
let remainingPercent = 0;
if (budget > 0) {
expenditurePercent = (expenditure / budget * 100).toFixed(1);
remainingPercent = (remaining / budget * 100).toFixed(1);
}
expenditureProportionChart.setOption({
series: [
{
data: [
{
value: parseFloat(expenditurePercent),
name: '支出',
itemStyle: { color: '#00FEFC' }
},
{
value: parseFloat(remainingPercent),
name: '预算剩余',
itemStyle: { color: '#FFFFFF' }
}
]
}
]
});
}
// 初始化项目风险图表
function initProjectRiskChart() {
projectRiskChart = echarts.init(document.getElementById('projectRiskChart'));
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: function (params) {
// params.data 包含数据项的所有信息,包括自定义属性
const actualCount = params.data.actualCount || 0;
const percent = params.percent || 0;
return params.name + ': ' + actualCount + '个 (' + percent.toFixed(1) + '%)';
}
},
legend: {
orient: 'vertical',
right: 10,
top: 'center',
textStyle: { color: '#fff', fontSize: 12 },
itemWidth: 12,
itemHeight: 8,
itemGap: 10,
formatter: function (name) {
// 获取当前图表的数据
const currentOption = projectRiskChart.getOption();
const seriesData = currentOption.series && currentOption.series[0] && currentOption.series[0].data;
if (seriesData) {
const item = seriesData.find(d => d.name === name);
if (item) {
const actualCount = item.actualCount || 0;
const percent = item.value || 0;
return name + ' ' + actualCount + '个 ' + percent.toFixed(1) + '%';
}
}
return name;
}
},
series: [
{
name: '项目风险',
type: 'pie',
roseType: 'area',
radius: ['10%', '70%'],
center: ['35%', '45%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 5,
borderColor: 'rgba(13, 34, 37, 0.8)',
borderWidth: 2
},
label: {
show: false
},
emphasis: {
label: {
show: true,
fontSize: 14,
fontWeight: 'bold',
color: '#fff'
},
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 254, 252, 0.5)'
}
},
data: [
{ value: 0, actualCount: 0, name: '二级风险', itemStyle: { color: '#4A90E2' } },
{ value: 0, actualCount: 0, name: '三级风险', itemStyle: { color: '#1CFFA3' } },
{ value: 0, actualCount: 0, name: '四级风险', itemStyle: { color: '#00FEFC' } },
{ value: 0, actualCount: 0, name: '五级风险', itemStyle: { color: '#FF9C65' } }
]
}
]
};
projectRiskChart.setOption(option);
}
// 更新项目风险图表
function updateProjectRiskChart(data) {
if (!projectRiskChart || !data) return;
// 获取各风险等级的数量
const twoRisk = parseInt(data.twoRisk || 0);
const threeRisk = parseInt(data.threeRisk || 0);
const fourRisk = parseInt(data.fourRisk || 0);
const fiveRisk = parseInt(data.fiveRisk || 0);
// 计算总风险数量(用于显示)
const allRisk = parseInt(data.allRisk || 0);
// 计算各风险等级的总数(用于计算百分比)
const totalRiskCount = twoRisk + threeRisk + fourRisk + fiveRisk;
// 计算百分比
let twoRiskPercent = 0;
let threeRiskPercent = 0;
let fourRiskPercent = 0;
let fiveRiskPercent = 0;
if (totalRiskCount > 0) {
twoRiskPercent = (twoRisk / totalRiskCount * 100).toFixed(1);
threeRiskPercent = (threeRisk / totalRiskCount * 100).toFixed(1);
fourRiskPercent = (fourRisk / totalRiskCount * 100).toFixed(1);
fiveRiskPercent = (fiveRisk / totalRiskCount * 100).toFixed(1);
}
// 更新图表数据,同时保存实际数量和百分比
projectRiskChart.setOption({
series: [
{
data: [
{
value: parseFloat(twoRiskPercent),
actualCount: twoRisk,
name: '二级风险',
itemStyle: { color: '#4A90E2' }
},
{
value: parseFloat(threeRiskPercent),
actualCount: threeRisk,
name: '三级风险',
itemStyle: { color: '#1CFFA3' }
},
{
value: parseFloat(fourRiskPercent),
actualCount: fourRisk,
name: '四级风险',
itemStyle: { color: '#00FEFC' }
},
{
value: parseFloat(fiveRiskPercent),
actualCount: fiveRisk,
name: '五级风险',
itemStyle: { color: '#FF9C65' }
}
]
}
]
});
// 更新总风险数量显示
$('#totalRiskDisplay').text('总风险数量:' + allRisk + '个');
// 更新右侧统计数据
const xhRisk = data.xhRisk || '0';
const lastRiskNum = data.lastRiskNum || '0';
const lastTwoRisk = data.lastTwoRisk || '0';
$('#closedRisk').text(xhRisk);
$('#remainingRisk').text(lastRiskNum);
$('#remainingLevel2Risk').text(lastTwoRisk);
}
// 初始化分析预警表格
function initWarningTable() {
// 初始化时调用接口获取数据
getAnalysisWarning(currentWarningFilter);
}
// 更新分析预警表格数据
function updateWarningTable(data) {
if (!data || !Array.isArray(data)) {
$('#warningTableBody').html('<tr><td colspan="5" style="text-align:center;color:rgba(255,255,255,0.5);">暂无预警</td></tr>');
return;
}
renderWarningTable(data);
}
// 渲染分析预警表格
function renderWarningTable(warnings) {
const tbody = $('#warningTableBody');
tbody.empty();
if (!warnings || warnings.length === 0) {
tbody.html('<tr><td colspan="5" style="text-align:center;color:rgba(255,255,255,0.5);">暂无预警</td></tr>');
return;
}
warnings.forEach((warning, index) => {
// 根据接口返回的字段映射
const id = (index + 1).toString().padStart(2, '0');
const time = warning.txTime || warning.warnTime || warning.time || '';
const type = warning.txType || warning.warnType || warning.type || '';
const content = warning.content || warning.warnContent || '';
const action = warning.action || '制定策略';
const row = `
<tr>
<td>${id}</td>
<td>${time}</td>
<td>${type}</td>
<td>${content}</td>
<td><span class="warning-action">${action}</span></td>
</tr>
`;
tbody.append(row);
});
}
// 筛选分析预警
let currentWarningFilter = 'all';
function filterWarning(type) {
currentWarningFilter = type;
// 更新标签状态
$('.warning-tab').removeClass('active');
$(`.warning-tab[data-type="${type}"]`).addClass('active');
// 重新调用接口获取筛选后的数据
getAnalysisWarning(type);
}
// 更新图表
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();
}