hb_zhgd_screen/js/pages/dataAnalysisOctober/projectProgress.js

1573 lines
54 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 table, layer, form, laydate;
let civilProgressChart = null;
let electricalProgressChart = null;
let civilProgressBarChart = null;
let electricalProgressBarChart = null;
let modalProcessKeyword = '';
let currentGxType = 1; // 当前弹框的工序类型1=土建2=电气
// 工程ID & 日期范围(参考 proQualityAnalysis.js
let bidCode = parent.parent.$('#bidPro').val();
// 获取当天日期
function getTodayDate() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return year + '-' + month + '-' + day;
}
// 获取当月第一天和最后一天(保留用于其他功能)
function getCurrentMonthRange() {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth();
// 当月第一天
const firstDay = new Date(year, month, 1);
const startDate = year + '-' + String(month + 1).padStart(2, '0') + '-' + String(firstDay.getDate()).padStart(2, '0');
// 当月最后一天
const lastDay = new Date(year, month + 1, 0);
const endDate = year + '-' + String(month + 1).padStart(2, '0') + '-' + String(lastDay.getDate()).padStart(2, '0');
return { startDate, endDate };
}
const today = getTodayDate();
let queryParams = {
projectId: bidCode,
startTestDay: today,
endTestDay: today,
};
layui.use(["layer", "table", "form", "laydate"], function () {
layer = layui.layer;
table = layui.table;
form = layui.form;
laydate = layui.laydate;
// 响应成功后的拦截器
$.ajaxSetup({
beforeSend: function (xhr, options) {
var originalSuccess = options.success;
options.success = function (data, textStatus, jqXhr) {
data = modifyResponseData(data);
originalSuccess.apply(this, arguments);
};
},
});
// 初始化页面
initPage();
// 初始化日期范围选择器
initDateRangePicker();
// 首次加载时刷新各模块数据
refreshAllModules();
// 为统计卡片添加点击事件
initStatItemClickEvents();
// 响应式调整
window.addEventListener('resize', debounce(function () {
handleResize();
}, 300));
});
// 获取第一行整体进度(分别查询土建/电气,带工程和时间参数)
function getFirstRowProgress() {
const baseUrl = commonUrl + "screen/project/constructionProgress/summary";
// 土建专业gxType=1
const civilUrl = baseUrl
+ "?projectId=" + (bidCode || '')
+ "&startTestDay=" + ''
+ "&endTestDay=" + ''
+ "&gxType=1";
ajaxRequestGet(
civilUrl,
"GET",
true,
function () { },
function (result) {
console.log(result, "第一行整体进度-土建");
const data = result && (result.data || (Array.isArray(result.rows) ? result.rows[0] : null));
if (!data) return;
updateCivilFirstRowProgress(data);
},
aqEnnable
);
// 电气专业gxType=2
const electricalUrl = baseUrl
+ "?projectId=" + (bidCode || '')
+ "&startTestDay=" + (queryParams.startTestDay || '')
+ "&endTestDay=" + (queryParams.endTestDay || '')
+ "&gxType=2";
ajaxRequestGet(
electricalUrl,
"GET",
true,
function () { },
function (result) {
console.log(result, "第一行整体进度-电气");
const data = result && (result.data || (Array.isArray(result.rows) ? result.rows[0] : null));
if (!data) return;
updateElectricalFirstRowProgress(data);
},
aqEnnable
);
}
// 获取第三行进度偏差分析
function getThirdRowProgressDeviation() {
// 分页参数pageNum=1pageSize=9999
const url =
commonUrl +
"screen/project/constructionProgress/list"
+ "?projectId=" + (bidCode || '')
+ "&startTestDay=" + ''
+ "&endTestDay=" + ''
+ "&pageNum=1&pageSize=9999";
ajaxRequestGet(
url,
"GET",
true,
function () { },
function (result) {
console.log(result, "第三行进度偏差分析");
const list = (result && Array.isArray(result.rows)) ? result.rows : [];
// 工具方法:将进度值转为数字(去掉%等字符)
function parseProgress(val) {
if (val === null || val === undefined || val === '') return NaN;
const str = String(val).replace('%', '').trim();
const num = parseFloat(str);
return isNaN(num) ? NaN : num;
}
// 根据 gxType 分组1=土建专业左侧2=电气专业(右侧)
const civilList = [];
const electricalList = [];
list.forEach(function (item) {
// 直接使用接口返回的 analysis 字段,不再进行计算
// analysis 字段由后端返回,前端直接使用
const gxType = item.gxType != null ? String(item.gxType) : '';
if (gxType === '1') {
civilList.push(item);
} else if (gxType === '2') {
electricalList.push(item);
}
});
// 为表格增加序号字段,便于显示
civilList.forEach(function (item, index) {
item.serialNumber = index + 1;
});
electricalList.forEach(function (item, index) {
item.serialNumber = index + 1;
});
// 回显到第三行两个表格
if (table) {
table.reload('civilDeviationTable', {
data: civilList
});
table.reload('electricalDeviationTable', {
data: electricalList
});
}
// 中间两个柱状图:根据接口数据回显,最多只取前 12 条
const civilForChart = civilList.length > 12 ? civilList.slice(0, 12) : civilList;
const electricalForChart = electricalList.length > 12 ? electricalList.slice(0, 12) : electricalList;
if (civilProgressBarChart && civilForChart.length) {
const civilCategories = civilForChart.map(function (item) {
return item.gx || item.processName || item.name || '';
});
const civilPlannedData = civilForChart.map(function (item) {
return parseProgress(item.sjprogress) || 0;
});
const civilActualData = civilForChart.map(function (item) {
return parseProgress(item.progress) || 0;
});
// 动态计算yAxis的最大值取计划进度和实际进度的最大值向上取整到10的倍数最小为20
const maxDataValue = Math.max(
...civilPlannedData,
...civilActualData
);
const yAxisMax = Math.max(20, Math.ceil(maxDataValue / 10) * 10);
civilProgressBarChart.setOption({
xAxis: { data: civilCategories },
yAxis: { max: yAxisMax },
series: [
{ name: '计划', data: civilPlannedData },
{ name: '实际', data: civilActualData }
]
});
}
if (electricalProgressBarChart && electricalForChart.length) {
const electricalCategories = electricalForChart.map(function (item) {
return item.gx || item.processName || item.name || '';
});
const electricalPlannedData = electricalForChart.map(function (item) {
return parseProgress(item.sjprogress) || 0;
});
const electricalActualData = electricalForChart.map(function (item) {
return parseProgress(item.progress) || 0;
});
// 动态计算yAxis的最大值取计划进度和实际进度的最大值向上取整到10的倍数最小为20
const maxDataValue = Math.max(
...electricalPlannedData,
...electricalActualData
);
const yAxisMax = Math.max(20, Math.ceil(maxDataValue / 10) * 10);
electricalProgressBarChart.setOption({
xAxis: { data: electricalCategories },
yAxis: { max: yAxisMax },
series: [
{ name: '计划', data: electricalPlannedData },
{ name: '实际', data: electricalActualData }
]
});
}
},
aqEnnable
);
}
// 防抖函数
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 处理窗口大小变化
function handleResize() {
if (civilProgressChart) {
civilProgressChart.resize();
}
if (electricalProgressChart) {
electricalProgressChart.resize();
}
if (civilProgressBarChart) {
civilProgressBarChart.resize();
}
if (electricalProgressBarChart) {
electricalProgressBarChart.resize();
}
if (table) {
table.resize('civilDeviationTable');
table.resize('electricalDeviationTable');
}
}
// 初始化页面
function initPage() {
initCivilProgressChart();
initElectricalProgressChart();
initCivilProgressBarChart();
initElectricalProgressBarChart();
initCivilDeviationTable();
initElectricalDeviationTable();
}
// 初始化日期范围选择器(顶部日期栏)
function initDateRangePicker() {
// 设置初始显示值为当天范围
const initialValue = queryParams.startTestDay + ' ~ ' + queryParams.endTestDay;
$('#dateRange').val(initialValue);
// 使用范围选择器,单个输入框显示日期范围
laydate.render({
elem: '#dateRange',
type: 'date',
range: true, // 启用范围选择
format: 'yyyy-MM-dd',
theme: 'dark',
// 默认值使用当天范围
value: queryParams.startTestDay + ' - ' + queryParams.endTestDay,
done: function (value, date, endDate) {
// 重置为当天日期的函数
const resetToToday = function () {
const today = getTodayDate();
queryParams.startTestDay = today;
queryParams.endTestDay = today;
$('#dateRange').val(today + ' ~ ' + today);
refreshAllModules();
};
if (value && value.trim() !== '') {
const dates = value.split(' - ');
if (dates.length === 2) {
const startDate = dates[0].trim();
const endDateStr = dates[1].trim();
// 在单个输入框中显示日期范围格式2026-01-15 ~ 2026-01-15
$('#dateRange').val(startDate + ' ~ ' + endDateStr);
// 更新查询参数
queryParams.startTestDay = startDate;
queryParams.endTestDay = endDateStr;
// 日期变化后,重新调用所有模块接口
refreshAllModules();
} else {
// 如果格式不正确,重置为当天日期
resetToToday();
}
} else {
// 清空时,重置为当天日期
resetToToday();
}
}
});
}
// 刷新当前页面所有需要接口的数据
function refreshAllModules() {
// 第一行整体进度(两个环形图 + 数字卡片)
getFirstRowProgress();
// 第三行进度偏差 + 中间两个柱状图
getThirdRowProgressDeviation();
}
// 初始化土建专业工序整体进度环形图
function initCivilProgressChart() {
const chartDom = document.getElementById('civilProgressChart');
if (!chartDom) return;
if (civilProgressChart) {
civilProgressChart.dispose();
}
civilProgressChart = echarts.init(chartDom);
// 模拟数据 - 后续替换为真实接口数据
const progressRate = 50.00;
const remainingRate = 50.00;
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(13, 34, 37, 0.95)',
borderColor: 'rgba(6, 189, 221, 0.8)',
borderWidth: 1,
borderRadius: 4,
padding: [12, 16],
position: 'right',
textStyle: {
color: '#FFFFFF',
fontSize: 12
},
extraCssText: 'z-index: 9999999999;',
formatter: function (params) {
const name = params.name;
const value = params.value;
const color = params.color;
return `
<div style="font-weight: bold; margin-bottom: 10px; color: #FFFFFF; font-size: 13px;">土建专业工序整体进度</div>
<div style="display: flex; align-items: center; min-width: 150px;">
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: ${color}; margin-right: 10px; flex-shrink: 0;"></span>
<span style="color: #B9D6D9; margin-right: 10px;">${name}</span>
<span style="color: #FFFFFF; margin-left: auto; font-weight: bold;">${value.toFixed(2)}%</span>
</div>
`;
}
},
series: [
// // 外层圆环
// {
// name: '外层圆环',
// type: 'pie',
// radius: ['94%', '97%'],
// center: ['50%', '50%'],
// avoidLabelOverlap: false,
// silent: true,
// itemStyle: {
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
// offset: 0,
// color: 'rgba(255, 255, 255, 0.9)'
// }, {
// offset: 1,
// color: 'rgba(0, 255, 212, 0.8)'
// }]),
// borderRadius: 2,
// borderColor: 'transparent',
// borderWidth: 0,
// shadowBlur: 8,
// shadowColor: 'rgba(0, 255, 212, 0.6)'
// },
// label: {
// show: false
// },
// labelLine: {
// show: false
// },
// data: [{
// value: 100,
// name: ''
// }]
// },
// 内层圆环
{
name: '土建专业工序整体进度',
type: 'pie',
radius: ['60%', '80%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
gap: 3,
itemStyle: {
borderRadius: 5,
borderColor: 'transparent',
borderWidth: 0
},
label: {
show: true,
position: 'center',
formatter: function () {
return '{a|50}{b|%}';
},
rich: {
a: {
fontSize: 32,
fontWeight: 'bold',
color: '#FFFFFF',
lineHeight: 40
},
b: {
fontSize: 20,
fontWeight: 'bold',
color: '#FFFFFF',
lineHeight: 40
}
}
},
emphasis: {
itemStyle: {
borderRadius: 6,
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
},
label: {
rich: {
a: {
fontSize: 36,
fontWeight: 'bold',
color: '#FFFFFF'
},
b: {
fontSize: 22,
fontWeight: 'bold',
color: '#FFFFFF'
}
}
}
},
labelLine: {
show: false
},
data: [
{
value: progressRate,
name: '已完成',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: '#00FFD4'
}, {
offset: 0.5,
color: '#00D4FF'
}, {
offset: 1,
color: '#00A8FF'
}])
}
},
{
value: remainingRate,
name: '未完成',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(238, 248, 245, 1)'
}, {
offset: 1,
color: 'rgba(238, 248, 245, 0.8)'
}])
}
}
]
}
]
};
civilProgressChart.setOption(option);
// 添加点击事件监听
civilProgressChart.on('click', function (params) {
openProcessDetailModal(1, '土建专业工序详情');
});
}
// 更新土建专业第一行整体进度(环形图 + 数字卡片)
function updateCivilFirstRowProgress(data) {
if (!civilProgressChart) return;
const totalCount = Number(data.totalCount) || 0;
const finishedCount = Number(data.finishedCount) || 0;
const abnormalCount = Number(data.abnormalCount) || 0;
// 通过总工序和已完成工序计算完成率
let overallProgress = 0;
if (totalCount > 0) {
overallProgress = (finishedCount / totalCount) * 100;
}
// 进度百分比限制在 0-100 之间
overallProgress = Math.max(0, Math.min(100, overallProgress));
const remainingRate = Math.max(0, 100 - overallProgress);
// 更新顶部统计数字
const totalEl = document.getElementById('civilTotalProcesses');
const finishedEl = document.getElementById('civilCompletedProcesses');
const abnormalEl = document.getElementById('civilAbnormalProcesses');
if (totalEl) totalEl.textContent = totalCount;
if (finishedEl) finishedEl.textContent = finishedCount;
if (abnormalEl) abnormalEl.textContent = abnormalCount;
// 更新环形图数据和中心文案
civilProgressChart.setOption({
series: [{
name: '土建专业工序整体进度',
label: {
formatter: '{a|' + overallProgress.toFixed(2) + '}{b|%}'
},
data: [
{ value: overallProgress, name: '已完成' },
{ value: remainingRate, name: '未完成' }
]
}]
});
}
// 初始化土建专业进度柱状图(中间柱状图)
function initCivilProgressBarChart() {
const chartDom = document.getElementById('civilProgressBarChart');
if (!chartDom) return;
if (civilProgressBarChart) {
civilProgressBarChart.dispose();
}
civilProgressBarChart = echarts.init(chartDom);
// 模拟数据(后续可由接口覆盖)
const categories = ['配电装置楼', '屋外场地', '围墙及大门', '辅助用房', '场坪工程', '系统构筑物', '站内外道路'];
const plannedData = [10, 12, 9, 11, 8, 13, 10];
const actualData = [6, 9, 7, 8, 6, 10, 7];
// 颜色配置 - 计划(绿色/青色系,左侧较高柱体)
const colorArr1 = ["#007A65", "#00A382", "#00D4AE", "#00FEFC"];
// 颜色配置 - 实际(青色/浅蓝色系,右侧较短柱体)
const colorArr2 = ["#006F85", "#0090A8", "#00C2DD", "#00EFFF"];
// 计划柱体垂直渐变(从上到下:亮到暗)
var color1 = {
type: "linear",
x: 0,
x2: 0,
y: 0,
y2: 1,
colorStops: [
{ offset: 0, color: colorArr1[3] }, // 顶部最亮
{ offset: 0.3, color: colorArr1[2] }, // 上部分
{ offset: 0.7, color: colorArr1[1] }, // 下部分
{ offset: 1, color: colorArr1[0] } // 底部较暗
]
};
// 实际柱体垂直渐变(从上到下:亮到暗)
var color2 = {
type: "linear",
x: 0,
x2: 0,
y: 0,
y2: 1,
colorStops: [
{ offset: 0, color: colorArr2[3] }, // 顶部最亮
{ offset: 0.3, color: colorArr2[2] }, // 上部分
{ offset: 0.7, color: colorArr2[1] }, // 下部分
{ offset: 1, color: colorArr2[0] } // 底部较暗
]
};
var barWidth = 18;
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
backgroundColor: 'rgba(13, 34, 37, 0.95)',
borderColor: 'rgba(6, 189, 221, 0.8)',
borderWidth: 1,
borderRadius: 4,
padding: [12, 16],
textStyle: {
color: '#FFFFFF',
fontSize: 12
},
extraCssText: 'z-index:9999999999;',
formatter: function (params) {
if (!params || !params.length) return '';
const name = params[0].name || '';
let html = `<div style="font-weight:bold;margin-bottom:8px;color:#FFFFFF;font-size:13px;">${name}</div>`;
params.forEach(function (item) {
// 只显示bar类型的tooltip不显示pictorialBar
if (item.componentSubType === "bar") {
const color = item.color;
html += `
<div style="display:flex;align-items:center;margin-bottom:4px;min-width:140px;">
<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${color};margin-right:8px;flex-shrink:0;"></span>
<span style="color:#B9D6D9;margin-right:6px;">${item.seriesName}</span>
<span style="color:#FFFFFF;margin-left:auto;font-weight:bold;">${item.value}</span>
</div>
`;
}
});
return html;
}
},
legend: {
data: ['计划', '实际'],
top: 8,
right: 20,
textStyle: {
color: '#FFFFFF',
fontSize: 12
},
itemWidth: 12,
itemHeight: 12
},
grid: {
left: '6%',
right: '4%',
bottom: '10%',
top: '22%',
containLabel: true
},
xAxis: {
type: 'category',
data: categories,
axisLabel: {
color: '#FFFFFF',
fontSize: 12
},
axisLine: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.3)',
type: 'dashed'
}
},
axisTick: { show: false }
},
yAxis: {
type: 'value',
min: 0,
max: 20,
axisLabel: {
color: '#FFFFFF',
fontSize: 12
},
axisLine: {
show: false
},
splitLine: {
lineStyle: {
color: 'rgba(0, 255, 255, 0.2)',
type: 'dashed',
opacity: 0.3
}
},
axisTick: {
show: false
}
},
series: [
{
z: 1,
name: '计划',
type: 'bar',
data: plannedData,
barWidth: barWidth,
barGap: '0%',
itemStyle: {
color: color1
}
},
{
z: 3,
name: '计划',
type: 'pictorialBar',
symbolPosition: 'end',
data: plannedData,
symbol: 'diamond',
symbolOffset: ['-75%', '-60%'],
symbolSize: [18, 12],
itemStyle: {
borderWidth: 2,
borderColor: 'rgba(0, 254, 252, 0.5)',
color: colorArr1[3] // 使用最亮的颜色作为钻石顶部
},
tooltip: {
show: false
}
},
{
z: 1,
name: '实际',
type: 'bar',
data: actualData,
barWidth: barWidth,
barGap: '50%',
itemStyle: {
color: color2
}
},
{
z: 3,
name: '实际',
type: 'pictorialBar',
symbolPosition: 'end',
data: actualData,
symbol: 'diamond',
symbolOffset: ['75%', '-60%'],
symbolSize: [18, 12],
itemStyle: {
borderWidth: 2,
borderColor: 'rgba(0, 239, 255, 0.5)',
color: colorArr2[3] // 使用最亮的颜色作为钻石顶部
},
tooltip: {
show: false
}
}
]
};
civilProgressBarChart.setOption(option);
}
// 初始化电气专业进度柱状图(中间柱状图)
function initElectricalProgressBarChart() {
const chartDom = document.getElementById('electricalProgressBarChart');
if (!chartDom) return;
if (electricalProgressBarChart) {
electricalProgressBarChart.dispose();
}
electricalProgressBarChart = echarts.init(chartDom);
// 模拟数据(后续可由接口覆盖)
const categories = ['变压器安装', '控制及直流系统', 'GIS配电装置', '10kV站用配电', '户外照明安装'];
const plannedData = [16, 15, 18, 14, 12];
const actualData = [12, 13, 15, 11, 10];
// 颜色配置 - 计划(绿色/青色系,左侧较高柱体)
const colorArr1 = ["#007A65", "#00A382", "#00D4AE", "#00FEFC"];
// 颜色配置 - 实际(青色/浅蓝色系,右侧较短柱体)
const colorArr2 = ["#006F85", "#0090A8", "#00C2DD", "#00EFFF"];
// 计划柱体垂直渐变(从上到下:亮到暗)
var color1 = {
type: "linear",
x: 0,
x2: 0,
y: 0,
y2: 1,
colorStops: [
{ offset: 0, color: colorArr1[3] }, // 顶部最亮
{ offset: 0.3, color: colorArr1[2] }, // 上部分
{ offset: 0.7, color: colorArr1[1] }, // 下部分
{ offset: 1, color: colorArr1[0] } // 底部较暗
]
};
// 实际柱体垂直渐变(从上到下:亮到暗)
var color2 = {
type: "linear",
x: 0,
x2: 0,
y: 0,
y2: 1,
colorStops: [
{ offset: 0, color: colorArr2[3] }, // 顶部最亮
{ offset: 0.3, color: colorArr2[2] }, // 上部分
{ offset: 0.7, color: colorArr2[1] }, // 下部分
{ offset: 1, color: colorArr2[0] } // 底部较暗
]
};
var barWidth = 18;
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
backgroundColor: 'rgba(13, 34, 37, 0.95)',
borderColor: 'rgba(6, 189, 221, 0.8)',
borderWidth: 1,
borderRadius: 4,
padding: [12, 16],
textStyle: {
color: '#FFFFFF',
fontSize: 12
},
extraCssText: 'z-index:9999999999;',
formatter: function (params) {
if (!params || !params.length) return '';
const name = params[0].name || '';
let html = `<div style="font-weight:bold;margin-bottom:8px;color:#FFFFFF;font-size:13px;">${name}</div>`;
params.forEach(function (item) {
// 只显示bar类型的tooltip不显示pictorialBar
if (item.componentSubType === "bar") {
const color = item.color;
html += `
<div style="display:flex;align-items:center;margin-bottom:4px;min-width:140px;">
<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${color};margin-right:8px;flex-shrink:0;"></span>
<span style="color:#B9D6D9;margin-right:6px;">${item.seriesName}</span>
<span style="color:#FFFFFF;margin-left:auto;font-weight:bold;">${item.value}</span>
</div>
`;
}
});
return html;
}
},
legend: {
data: ['计划', '实际'],
top: 8,
right: 20,
textStyle: {
color: '#FFFFFF',
fontSize: 12
},
itemWidth: 12,
itemHeight: 12
},
grid: {
left: '6%',
right: '4%',
bottom: '10%',
top: '22%',
containLabel: true
},
xAxis: {
type: 'category',
data: categories,
axisLabel: {
color: '#FFFFFF',
fontSize: 12
},
axisLine: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.3)',
type: 'dashed'
}
},
axisTick: { show: false }
},
yAxis: {
type: 'value',
min: 0,
max: 20,
axisLabel: {
color: '#FFFFFF',
fontSize: 12
},
axisLine: {
show: false
},
splitLine: {
lineStyle: {
color: 'rgba(0, 255, 255, 0.2)',
type: 'dashed',
opacity: 0.3
}
},
axisTick: {
show: false
}
},
series: [
{
z: 1,
name: '计划',
type: 'bar',
data: plannedData,
barWidth: barWidth,
barGap: '0%',
itemStyle: {
color: color1
}
},
{
z: 3,
name: '计划',
type: 'pictorialBar',
symbolPosition: 'end',
data: plannedData,
symbol: 'diamond',
symbolOffset: ['-75%', '-60%'],
symbolSize: [18, 12],
itemStyle: {
borderWidth: 2,
borderColor: 'rgba(0, 254, 252, 0.5)',
color: colorArr1[3] // 使用最亮的颜色作为钻石顶部
},
tooltip: {
show: false
}
},
{
z: 1,
name: '实际',
type: 'bar',
data: actualData,
barWidth: barWidth,
barGap: '50%',
itemStyle: {
color: color2
}
},
{
z: 3,
name: '实际',
type: 'pictorialBar',
symbolPosition: 'end',
data: actualData,
symbol: 'diamond',
symbolOffset: ['75%', '-60%'],
symbolSize: [18, 12],
itemStyle: {
borderWidth: 2,
borderColor: 'rgba(0, 239, 255, 0.5)',
color: colorArr2[3] // 使用最亮的颜色作为钻石顶部
},
tooltip: {
show: false
}
}
]
};
electricalProgressBarChart.setOption(option);
}
// 初始化电气专业工序整体进度环形图
function initElectricalProgressChart() {
const chartDom = document.getElementById('electricalProgressChart');
if (!chartDom) return;
if (electricalProgressChart) {
electricalProgressChart.dispose();
}
electricalProgressChart = echarts.init(chartDom);
// 模拟数据 - 后续替换为真实接口数据
const progressRate = 50.00;
const remainingRate = 50.00;
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(13, 34, 37, 0.95)',
borderColor: 'rgba(6, 189, 221, 0.8)',
borderWidth: 1,
borderRadius: 4,
padding: [12, 16],
position: 'right',
textStyle: {
color: '#FFFFFF',
fontSize: 12
},
extraCssText: 'z-index: 9999999999;',
formatter: function (params) {
const name = params.name;
const value = params.value;
const color = params.color;
return `
<div style="font-weight: bold; margin-bottom: 10px; color: #FFFFFF; font-size: 13px;">变电专业工序整体进度</div>
<div style="display: flex; align-items: center; min-width: 150px;">
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: ${color}; margin-right: 10px; flex-shrink: 0;"></span>
<span style="color: #B9D6D9; margin-right: 10px;">${name}</span>
<span style="color: #FFFFFF; margin-left: auto; font-weight: bold;">${value.toFixed(2)}%</span>
</div>
`;
}
},
series: [
// // 外层圆环
// {
// name: '外层圆环',
// type: 'pie',
// radius: ['94%', '97%'],
// center: ['50%', '50%'],
// avoidLabelOverlap: false,
// silent: true,
// itemStyle: {
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
// offset: 0,
// color: 'rgba(255, 255, 255, 0.9)'
// }, {
// offset: 1,
// color: 'rgba(0, 255, 212, 0.8)'
// }]),
// borderRadius: 2,
// borderColor: 'transparent',
// borderWidth: 0,
// shadowBlur: 8,
// shadowColor: 'rgba(0, 255, 212, 0.6)'
// },
// label: {
// show: false
// },
// labelLine: {
// show: false
// },
// data: [{
// value: 100,
// name: ''
// }]
// },
// 内层圆环
{
name: '变电专业工序整体进度',
type: 'pie',
radius: ['60%', '80%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
gap: 3,
itemStyle: {
borderRadius: 5,
borderColor: 'transparent',
borderWidth: 0
},
label: {
show: true,
position: 'center',
formatter: function () {
return '{a|50}{b|%}';
},
rich: {
a: {
fontSize: 32,
fontWeight: 'bold',
color: '#FFFFFF',
lineHeight: 40
},
b: {
fontSize: 20,
fontWeight: 'bold',
color: '#FFFFFF',
lineHeight: 40
}
}
},
emphasis: {
itemStyle: {
borderRadius: 6,
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
},
label: {
rich: {
a: {
fontSize: 36,
fontWeight: 'bold',
color: '#FFFFFF'
},
b: {
fontSize: 22,
fontWeight: 'bold',
color: '#FFFFFF'
}
}
}
},
labelLine: {
show: false
},
data: [
{
value: progressRate,
name: '已完成',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: '#00FFD4'
}, {
offset: 0.5,
color: '#00D4FF'
}, {
offset: 1,
color: '#00A8FF'
}])
}
},
{
value: remainingRate,
name: '未完成',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(238, 248, 245, 1)'
}, {
offset: 1,
color: 'rgba(238, 248, 245, 0.8)'
}])
}
}
]
}
]
};
electricalProgressChart.setOption(option);
// 添加点击事件监听
electricalProgressChart.on('click', function (params) {
openProcessDetailModal(2, '电气专业工序详情');
});
}
// 更新电气专业第一行整体进度(环形图 + 数字卡片)
function updateElectricalFirstRowProgress(data) {
if (!electricalProgressChart) return;
const totalCount = Number(data.totalCount) || 0;
const finishedCount = Number(data.finishedCount) || 0;
const abnormalCount = Number(data.abnormalCount) || 0;
// 通过总工序和已完成工序计算完成率
let overallProgress = 0;
if (totalCount > 0) {
overallProgress = (finishedCount / totalCount) * 100;
}
// 进度百分比限制在 0-100 之间
overallProgress = Math.max(0, Math.min(100, overallProgress));
const remainingRate = Math.max(0, 100 - overallProgress);
// 更新顶部统计数字
const totalEl = document.getElementById('electricalTotalProcesses');
const finishedEl = document.getElementById('electricalCompletedProcesses');
const abnormalEl = document.getElementById('electricalAbnormalProcesses');
if (totalEl) totalEl.textContent = totalCount;
if (finishedEl) finishedEl.textContent = finishedCount;
if (abnormalEl) abnormalEl.textContent = abnormalCount;
// 更新环形图数据和中心文案
electricalProgressChart.setOption({
series: [{
name: '变电专业工序整体进度',
label: {
formatter: '{a|' + overallProgress.toFixed(2) + '}{b|%}'
},
data: [
{ value: overallProgress, name: '已完成' },
{ value: remainingRate, name: '未完成' }
]
}]
});
}
// 初始化异常工序预警列表
function initWarningList() {
// 生成模拟警告数据
const warnings = [];
const teams = ['施工队A', '施工队B', '施工队C', '施工队D', '施工队E'];
const processes = ['钢筋绑扎工序', '混凝土浇筑工序', '模板安装工序', '钢筋焊接工序', '混凝土养护工序'];
const messages = ['进度滞后', '质量异常', '人员不足', '设备故障', '材料短缺'];
const today = new Date();
for (let i = 0; i < 15; i++) {
const date = new Date(today);
date.setDate(date.getDate() - Math.floor(i / 3));
const dateStr = date.getFullYear() + '-' +
String(date.getMonth() + 1).padStart(2, '0') + '-' +
String(date.getDate()).padStart(2, '0');
warnings.push({
date: dateStr,
team: teams[Math.floor(Math.random() * teams.length)],
process: processes[Math.floor(Math.random() * processes.length)],
message: messages[Math.floor(Math.random() * messages.length)]
});
}
const warningItemsHtml = warnings.map(function (warning) {
const fullText = `${warning.date} ${warning.team} ${warning.process}${warning.message}`;
return `
<div class="warning-item">
<div class="warning-icon"></div>
<div class="warning-text" title="${fullText}">
${fullText}
</div>
</div>
`;
}).join('');
const $warningList = $('#civilWarningList');
$warningList.html(warningItemsHtml);
setTimeout(function () {
const $container = $warningList.parent();
const contentHeight = $warningList[0].scrollHeight;
const containerHeight = $container.height();
if (contentHeight > containerHeight) {
const duplicatedHtml = warningItemsHtml + warningItemsHtml; // 2 copies
$warningList.html(duplicatedHtml);
setTimeout(function () {
const newContentHeight = $warningList[0].scrollHeight;
const singleListHeight = newContentHeight / 2; // Height of one copy
$warningList.addClass('warning-list-scroll');
const animationDuration = Math.max(20, Math.ceil(singleListHeight / 30)); // 30px per second
$warningList.css({
'animation': `scrollWarning ${animationDuration}s linear infinite forwards`
});
}, 100);
}
}, 300);
}
// 生成土建专业进度偏差模拟数据
function generateCivilDeviationMockData() {
const processNames = [];
const mockData = [];
for (let i = 0; i < 10; i++) {
mockData.push({
serialNumber: i + 1,
processName: processNames[Math.floor(Math.random() * processNames.length)],
personnelInput: Math.floor(Math.random() * 20) + 5,
equipmentInput: Math.floor(Math.random() * 10) + 2,
plannedProgress: (Math.random() * 40 + 40).toFixed(0) + '%',
actualProgress: (Math.random() * 30 + 30).toFixed(0) + '%',
analysis: '当前进度滞后,请合理安排工作计划及投入的人员设备等资源'
});
}
return mockData;
}
// 初始化土建专业进度偏差分析表格
function initCivilDeviationTable() {
const mockData = generateCivilDeviationMockData();
table.render({
elem: '#civilDeviationTable',
data: mockData,
skin: 'line',
page: false,
height: 'full-80',
cols: [[
{ field: 'serialNumber', title: '序号', width: '8%', align: 'center' },
{ field: 'gx', title: '工序名称', width: '15%', align: 'center' },
{ field: 'inUser', title: '投入人员', width: '12%', align: 'center' },
{ field: 'inDevice', title: '投入设备', width: '12%', align: 'center' },
{
field: 'sjprogress',
title: '计划进度',
width: '15%',
align: 'center',
templet: function (d) {
if (d.sjprogress === null || d.sjprogress === undefined || d.sjprogress === '') {
return '';
}
const str = String(d.sjprogress).replace('%', '').trim();
const num = parseFloat(str);
if (isNaN(num)) {
return d.sjprogress;
}
return num + '%';
}
},
{
field: 'progress',
title: '实际进度',
width: '15%',
align: 'center',
templet: function (d) {
if (d.progress === null || d.progress === undefined || d.progress === '') {
return '';
}
const str = String(d.progress).replace('%', '').trim();
const num = parseFloat(str);
if (isNaN(num)) {
return d.progress;
}
return num + '%';
}
},
{ field: 'analysis', title: '分析', width: '23%', align: 'left' }
]]
});
}
// 初始化电气专业进度偏差分析表格(与左侧相同列表)
function initElectricalDeviationTable() {
const mockData = generateCivilDeviationMockData();
table.render({
elem: '#electricalDeviationTable',
data: mockData,
skin: 'line',
page: false,
height: 'full-80',
cols: [[
{ field: 'serialNumber', title: '序号', width: '8%', align: 'center' },
{ field: 'gx', title: '工序名称', width: '15%', align: 'center' },
{ field: 'inUser', title: '投入人员', width: '12%', align: 'center' },
{ field: 'inDevice', title: '投入设备', width: '12%', align: 'center' },
{
field: 'sjprogress',
title: '计划进度',
width: '15%',
align: 'center',
templet: function (d) {
if (d.sjprogress === null || d.sjprogress === undefined || d.sjprogress === '') {
return '';
}
const str = String(d.sjprogress).replace('%', '').trim();
const num = parseFloat(str);
if (isNaN(num)) {
return d.sjprogress;
}
return num + '%';
}
},
{
field: 'progress',
title: '实际进度',
width: '15%',
align: 'center',
templet: function (d) {
if (d.progress === null || d.progress === undefined || d.progress === '') {
return '';
}
const str = String(d.progress).replace('%', '').trim();
const num = parseFloat(str);
if (isNaN(num)) {
return d.progress;
}
return num + '%';
}
},
{ field: 'analysis', title: '分析', width: '23%', align: 'left' }
]]
});
}
// 查询土建专业进度偏差
function queryCivilDeviation() {
const keyword = $('#civilKeywordInput').val();
const dateRange = $('#civilDeviationDateRange').val();
table.reload('civilDeviationTable', {
where: {
keyword: keyword,
dateRange: dateRange,
bidCode: parent.parent.$('#bidPro').val() || ''
},
page: {
curr: 1
}
});
}
// 此处原先的进度图表日期查询与接口调用已移除,
// 进度展示图表暂时使用页面内的模拟数据进行展示。
// 初始化统计卡片点击事件
function initStatItemClickEvents() {
// 土建专业统计卡片
$('#civilTotalProcesses').closest('.stat-item').on('click', function () {
openProcessDetailModal(1, '土建专业工序详情');
});
$('#civilCompletedProcesses').closest('.stat-item').on('click', function () {
openProcessDetailModal(1, '土建专业工序详情');
});
$('#civilAbnormalProcesses').closest('.stat-item').on('click', function () {
openProcessDetailModal(1, '土建专业工序详情');
});
// 电气专业统计卡片
$('#electricalTotalProcesses').closest('.stat-item').on('click', function () {
openProcessDetailModal(2, '电气专业工序详情');
});
$('#electricalCompletedProcesses').closest('.stat-item').on('click', function () {
openProcessDetailModal(2, '电气专业工序详情');
});
$('#electricalAbnormalProcesses').closest('.stat-item').on('click', function () {
openProcessDetailModal(2, '电气专业工序详情');
});
}
// 打开工序详情弹框
function openProcessDetailModal(gxType, title) {
currentGxType = gxType;
const modal = $('#processDetailModal');
$('#processDetailModalTitle').text(title);
modal.addClass('show');
// 重置关键字输入框
modalProcessKeyword = '';
$('#modalProcessKeywordInput').val('');
// 延迟初始化表格,确保弹框完全显示后再渲染表格
setTimeout(function () {
initModalProcessDetailTable();
}, 100);
}
// 关闭工序详情弹框
function closeProcessDetailModal() {
const modal = $('#processDetailModal');
modal.removeClass('show');
}
// 初始化弹框内工序详情表格
function initModalProcessDetailTable() {
table.render({
elem: '#modalProcessDetailTable',
id: 'modalProcessDetailTable',
url: commonUrl + 'screen/project/constructionProgress/detail',
headers: {
decrypt: 'decrypt',
Authorization: token
},
method: 'GET',
where: {
projectId: bidCode,
startTestDay: '',
endTestDay: '',
gxType: currentGxType,
keyword: modalProcessKeyword || ''
},
skin: 'line',
page: {
layout: ['prev', 'page', 'next', 'count', 'skip'],
groups: 5,
limit: 10,
limits: [10, 20, 30, 50]
},
height: 'full',
request: {
pageName: 'pageNum',
limitName: 'pageSize'
},
response: {
statusName: 'code',
statusCode: 200,
msgName: 'msg',
countName: 'total',
dataName: 'data'
},
cols: [[
{ type: 'numbers', title: '序号', width: '10%', align: 'center' },
{
field: 'gxType',
title: '工序类型',
width: '12%',
align: 'center',
templet: function (d) {
return d.gxType == 1 ? '土建' : (d.gxType == 2 ? '电气' : '');
}
},
{ field: 'gx', title: '工序', width: '18%', align: 'center' },
{ field: 'planStartTime', title: '计划开始时间', width: '15%', align: 'center' },
{ field: 'planEndTime', title: '计划结束时间', width: '15%', align: 'center' },
{ field: 'endTime', title: '实际结束时间', width: '15%', align: 'center' },
{
field: 'status',
title: '状态',
width: '15%',
align: 'center',
templet: function (d) {
// 状态1=已完成2=进行中3=未开始4=延期
const status = d.status;
if (status === 1 || status === '1') {
return '<span style="color: #00FFB8;">已完成</span>';
} else if (status === 2 || status === '2') {
return '<span style="color: #FFC857;">进行中</span>';
} else if (status === 3 || status === '3') {
return '<span style="color: #B9D6D9;">未开始</span>';
} else if (status === 4 || status === '4') {
return '<span style="color: #FF6B6B;">延期</span>';
} else {
// 如果没有status字段根据实际结束时间判断
if (d.endTime && d.endTime.trim() !== '') {
return '<span style="color: #00FFB8;">已完成</span>';
} else {
return '<span style="color: #FFC857;">进行中</span>';
}
}
}
}
]]
});
}
// 弹框内查询工序记录
function queryModalProcessRecords() {
modalProcessKeyword = $('#modalProcessKeywordInput').val() || '';
// 重新加载表格
table.reload('modalProcessDetailTable', {
where: {
projectId: bidCode,
startTestDay: queryParams.startTestDay,
endTestDay: queryParams.endTestDay,
gxType: currentGxType,
keyword: modalProcessKeyword
},
page: {
curr: 1
}
});
}