hb_zhgd_screen/js/pages/dataAnalysisOctober/projectManagement.js

628 lines
21 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;
// 页面初始化
$(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));
});
});
// 防抖函数
function debounce(fn, delay) {
let t = null;
return function () {
clearTimeout(t);
t = setTimeout(() => fn.apply(this, arguments), delay);
};
}
// 初始化日期范围选择器
function initDateRange() {
const laydate = layui.laydate;
laydate.render({
elem: '#dateRange',
type: 'date',
range: true,
format: 'yyyy-MM-dd',
value: getDefaultDateRange(),
done: function (value) {
// 日期选择完成后的回调
}
});
}
// 获取默认日期范围最近7天
function getDefaultDateRange() {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 6);
return formatDate(start) + ' ~ ' + formatDate(end);
}
// 格式化日期
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;
}
// 这里可以调用API获取数据
// 目前使用模拟数据
loadData(dateRange);
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: 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' } }
]
}
],
graphic: [
{
type: 'text',
left: '35%',
top: '75%',
style: {
text: '今日风险数量 60个',
textAlign: 'center',
fill: '#fff',
fontSize: 13
}
}
]
};
riskAnalysisChart.setOption(option);
}
// 初始化土建专业折线图
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];
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' },
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: 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' }
}
},
series: [
{
name: '计划',
type: 'line',
data: planned,
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.3)' },
{ offset: 1, color: 'rgba(0, 254, 252, 0.05)' }
])
}
},
{
name: '实际',
type: 'line',
data: actual,
smooth: true,
lineStyle: { width: 2, color: '#1CFFA3' },
itemStyle: { color: '#1CFFA3' },
areaStyle: {
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)' }
])
}
}
]
};
civilSpecialtyChart.setOption(option);
}
// 初始化电气专业折线图
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];
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' },
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: 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' }
}
},
series: [
{
name: '计划',
type: 'line',
data: planned,
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.3)' },
{ offset: 1, color: 'rgba(0, 254, 252, 0.05)' }
])
}
},
{
name: '实际',
type: 'line',
data: actual,
smooth: true,
lineStyle: { width: 2, color: '#1CFFA3' },
itemStyle: { color: '#1CFFA3' },
areaStyle: {
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)' }
])
}
}
]
};
electricalSpecialtyChart.setOption(option);
}
// 初始化项目支出趋势图表
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: 20, name: '支出', itemStyle: { color: '#00FEFC' } },
{ value: 80, name: '预算剩余', itemStyle: { color: '#FFFFFF' } }
]
}
]
};
expenditureProportionChart.setOption(option);
}
// 初始化项目风险图表
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: '{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: 60, name: '二级风险', itemStyle: { color: '#4A90E2' } },
{ value: 60, name: '三级风险', itemStyle: { color: '#1CFFA3' } },
{ value: 26, name: '四级风险', itemStyle: { color: '#00FEFC' } },
{ value: 14, name: '五级风险', itemStyle: { color: '#FF9C65' } }
]
}
],
graphic: [
{
type: 'text',
left: '35%',
top: '75%',
style: {
text: '总风险数量 60个',
textAlign: 'center',
fill: '#fff',
fontSize: 13
}
}
]
};
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);
});
}
// 筛选分析预警
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();
}