2025-10-18 15:55:58 +08:00
|
|
|
|
let table, layer, form, laydate;
|
2026-01-21 10:34:06 +08:00
|
|
|
|
let civilProgressChart = null;
|
|
|
|
|
|
let electricalProgressChart = null;
|
|
|
|
|
|
let civilProgressBarChart = null;
|
|
|
|
|
|
let electricalProgressBarChart = null;
|
2026-01-24 14:23:35 +08:00
|
|
|
|
let modalProcessKeyword = '';
|
|
|
|
|
|
let currentGxType = 1; // 当前弹框的工序类型:1=土建,2=电气
|
|
|
|
|
|
|
|
|
|
|
|
// 工程ID & 日期范围(参考 proQualityAnalysis.js)
|
|
|
|
|
|
let bidCode = parent.parent.$('#bidPro').val();
|
|
|
|
|
|
|
2026-01-24 18:55:45 +08:00
|
|
|
|
// 获取当天日期
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当月第一天和最后一天(保留用于其他功能)
|
2026-01-24 14:23:35 +08:00
|
|
|
|
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 };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-24 18:55:45 +08:00
|
|
|
|
const today = getTodayDate();
|
2026-01-24 14:23:35 +08:00
|
|
|
|
let queryParams = {
|
|
|
|
|
|
projectId: bidCode,
|
2026-01-24 18:55:45 +08:00
|
|
|
|
startTestDay: today,
|
|
|
|
|
|
endTestDay: today,
|
2026-01-24 14:23:35 +08:00
|
|
|
|
};
|
2025-10-14 18:16:49 +08:00
|
|
|
|
|
2025-10-18 15:55:58 +08:00
|
|
|
|
layui.use(["layer", "table", "form", "laydate"], function () {
|
2025-10-14 18:16:49 +08:00
|
|
|
|
layer = layui.layer;
|
|
|
|
|
|
table = layui.table;
|
|
|
|
|
|
form = layui.form;
|
2025-10-18 15:55:58 +08:00
|
|
|
|
laydate = layui.laydate;
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 响应成功后的拦截器
|
|
|
|
|
|
$.ajaxSetup({
|
|
|
|
|
|
beforeSend: function (xhr, options) {
|
|
|
|
|
|
var originalSuccess = options.success;
|
|
|
|
|
|
options.success = function (data, textStatus, jqXhr) {
|
|
|
|
|
|
data = modifyResponseData(data);
|
|
|
|
|
|
originalSuccess.apply(this, arguments);
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
2025-10-18 15:55:58 +08:00
|
|
|
|
});
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 初始化页面
|
|
|
|
|
|
initPage();
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 初始化日期范围选择器
|
2026-01-24 14:23:35 +08:00
|
|
|
|
initDateRangePicker();
|
|
|
|
|
|
|
|
|
|
|
|
// 首次加载时刷新各模块数据
|
|
|
|
|
|
refreshAllModules();
|
|
|
|
|
|
|
|
|
|
|
|
// 为统计卡片添加点击事件
|
|
|
|
|
|
initStatItemClickEvents();
|
|
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 响应式调整
|
2026-01-24 14:23:35 +08:00
|
|
|
|
window.addEventListener('resize', debounce(function () {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
handleResize();
|
|
|
|
|
|
}, 300));
|
2025-10-14 18:16:49 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
// 获取第一行整体进度(分别查询土建/电气,带工程和时间参数)
|
|
|
|
|
|
function getFirstRowProgress() {
|
|
|
|
|
|
const baseUrl = commonUrl + "screen/project/constructionProgress/summary";
|
|
|
|
|
|
|
|
|
|
|
|
// 土建专业(gxType=1)
|
|
|
|
|
|
const civilUrl = baseUrl
|
|
|
|
|
|
+ "?projectId=" + (bidCode || '')
|
|
|
|
|
|
+ "&startTestDay=" + (queryParams.startTestDay || '')
|
|
|
|
|
|
+ "&endTestDay=" + (queryParams.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=1,pageSize=9999
|
|
|
|
|
|
const url =
|
|
|
|
|
|
commonUrl +
|
|
|
|
|
|
"screen/project/constructionProgress/list"
|
|
|
|
|
|
+ "?projectId=" + (bidCode || '')
|
|
|
|
|
|
+ "&startTestDay=" + (queryParams.startTestDay || '')
|
|
|
|
|
|
+ "&endTestDay=" + (queryParams.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) {
|
2026-01-24 18:55:45 +08:00
|
|
|
|
// 直接使用接口返回的 analysis 字段,不再进行计算
|
|
|
|
|
|
// analysis 字段由后端返回,前端直接使用
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 防抖函数
|
|
|
|
|
|
function debounce(func, wait) {
|
|
|
|
|
|
let timeout;
|
|
|
|
|
|
return function executedFunction(...args) {
|
|
|
|
|
|
const later = () => {
|
|
|
|
|
|
clearTimeout(timeout);
|
|
|
|
|
|
func(...args);
|
|
|
|
|
|
};
|
|
|
|
|
|
clearTimeout(timeout);
|
|
|
|
|
|
timeout = setTimeout(later, wait);
|
|
|
|
|
|
};
|
2025-10-27 19:10:17 +08:00
|
|
|
|
}
|
2025-10-18 15:55:58 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 处理窗口大小变化
|
|
|
|
|
|
function handleResize() {
|
|
|
|
|
|
if (civilProgressChart) {
|
|
|
|
|
|
civilProgressChart.resize();
|
|
|
|
|
|
}
|
|
|
|
|
|
if (electricalProgressChart) {
|
|
|
|
|
|
electricalProgressChart.resize();
|
2025-10-18 15:55:58 +08:00
|
|
|
|
}
|
2026-01-21 10:34:06 +08:00
|
|
|
|
if (civilProgressBarChart) {
|
|
|
|
|
|
civilProgressBarChart.resize();
|
|
|
|
|
|
}
|
|
|
|
|
|
if (electricalProgressBarChart) {
|
|
|
|
|
|
electricalProgressBarChart.resize();
|
|
|
|
|
|
}
|
|
|
|
|
|
if (table) {
|
|
|
|
|
|
table.resize('civilDeviationTable');
|
|
|
|
|
|
table.resize('electricalDeviationTable');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-18 15:55:58 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 初始化页面
|
|
|
|
|
|
function initPage() {
|
|
|
|
|
|
initCivilProgressChart();
|
|
|
|
|
|
initElectricalProgressChart();
|
|
|
|
|
|
initCivilProgressBarChart();
|
|
|
|
|
|
initElectricalProgressBarChart();
|
|
|
|
|
|
initCivilDeviationTable();
|
|
|
|
|
|
initElectricalDeviationTable();
|
2025-10-18 15:55:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
// 初始化日期范围选择器(顶部日期栏)
|
|
|
|
|
|
function initDateRangePicker() {
|
2026-01-24 18:55:45 +08:00
|
|
|
|
// 设置初始显示值为当天范围
|
2026-01-24 14:23:35 +08:00
|
|
|
|
const initialValue = queryParams.startTestDay + ' ~ ' + queryParams.endTestDay;
|
|
|
|
|
|
$('#dateRange').val(initialValue);
|
|
|
|
|
|
|
|
|
|
|
|
// 使用范围选择器,单个输入框显示日期范围
|
2026-01-21 10:34:06 +08:00
|
|
|
|
laydate.render({
|
2026-01-24 14:23:35 +08:00
|
|
|
|
elem: '#dateRange',
|
2026-01-21 10:34:06 +08:00
|
|
|
|
type: 'date',
|
2026-01-24 14:23:35 +08:00
|
|
|
|
range: true, // 启用范围选择
|
2026-01-21 10:34:06 +08:00
|
|
|
|
format: 'yyyy-MM-dd',
|
|
|
|
|
|
theme: 'dark',
|
2026-01-24 18:55:45 +08:00
|
|
|
|
// 默认值使用当天范围
|
2026-01-24 14:23:35 +08:00
|
|
|
|
value: queryParams.startTestDay + ' - ' + queryParams.endTestDay,
|
|
|
|
|
|
done: function (value, date, endDate) {
|
2026-01-24 18:55:45 +08:00
|
|
|
|
// 重置为当天日期的函数
|
|
|
|
|
|
const resetToToday = function () {
|
|
|
|
|
|
const today = getTodayDate();
|
|
|
|
|
|
queryParams.startTestDay = today;
|
|
|
|
|
|
queryParams.endTestDay = today;
|
|
|
|
|
|
$('#dateRange').val(today + ' ~ ' + today);
|
2026-01-24 14:23:35 +08:00
|
|
|
|
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-24 18:55:45 +08:00
|
|
|
|
// 在单个输入框中显示日期范围(格式:2026-01-15 ~ 2026-01-15)
|
2026-01-24 14:23:35 +08:00
|
|
|
|
$('#dateRange').val(startDate + ' ~ ' + endDateStr);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新查询参数
|
|
|
|
|
|
queryParams.startTestDay = startDate;
|
|
|
|
|
|
queryParams.endTestDay = endDateStr;
|
|
|
|
|
|
|
|
|
|
|
|
// 日期变化后,重新调用所有模块接口
|
|
|
|
|
|
refreshAllModules();
|
|
|
|
|
|
} else {
|
2026-01-24 18:55:45 +08:00
|
|
|
|
// 如果格式不正确,重置为当天日期
|
|
|
|
|
|
resetToToday();
|
2026-01-24 14:23:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2026-01-24 18:55:45 +08:00
|
|
|
|
// 清空时,重置为当天日期
|
|
|
|
|
|
resetToToday();
|
2026-01-24 14:23:35 +08:00
|
|
|
|
}
|
2026-01-21 10:34:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
// 刷新当前页面所有需要接口的数据
|
|
|
|
|
|
function refreshAllModules() {
|
|
|
|
|
|
// 第一行整体进度(两个环形图 + 数字卡片)
|
|
|
|
|
|
getFirstRowProgress();
|
|
|
|
|
|
// 第三行进度偏差 + 中间两个柱状图
|
|
|
|
|
|
getThirdRowProgressDeviation();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 初始化土建专业工序整体进度环形图
|
|
|
|
|
|
function initCivilProgressChart() {
|
|
|
|
|
|
const chartDom = document.getElementById('civilProgressChart');
|
|
|
|
|
|
if (!chartDom) return;
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
if (civilProgressChart) {
|
|
|
|
|
|
civilProgressChart.dispose();
|
|
|
|
|
|
}
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
civilProgressChart = echarts.init(chartDom);
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 模拟数据 - 后续替换为真实接口数据
|
|
|
|
|
|
const progressRate = 50.00;
|
|
|
|
|
|
const remainingRate = 50.00;
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08: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;',
|
2026-01-24 14:23:35 +08:00
|
|
|
|
formatter: function (params) {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
const name = params.name;
|
|
|
|
|
|
const value = params.value;
|
|
|
|
|
|
const color = params.color;
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
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',
|
2026-01-24 14:23:35 +08:00
|
|
|
|
formatter: function () {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
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)'
|
|
|
|
|
|
}])
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
};
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
civilProgressChart.setOption(option);
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
|
|
|
|
|
// 添加点击事件监听
|
|
|
|
|
|
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: '未完成' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}]
|
|
|
|
|
|
});
|
2026-01-21 10:34:06 +08:00
|
|
|
|
}
|
2025-10-27 19:10:17 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 初始化土建专业进度柱状图(中间柱状图)
|
|
|
|
|
|
function initCivilProgressBarChart() {
|
|
|
|
|
|
const chartDom = document.getElementById('civilProgressBarChart');
|
|
|
|
|
|
if (!chartDom) return;
|
2025-10-27 19:10:17 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
if (civilProgressBarChart) {
|
|
|
|
|
|
civilProgressBarChart.dispose();
|
2025-10-18 15:55:58 +08:00
|
|
|
|
}
|
2025-10-14 18:16:49 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
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;
|
2025-10-27 19:10:17 +08:00
|
|
|
|
|
2025-10-18 15:55:58 +08:00
|
|
|
|
const option = {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
backgroundColor: 'transparent',
|
2025-10-14 18:16:49 +08:00
|
|
|
|
tooltip: {
|
|
|
|
|
|
trigger: 'axis',
|
2026-01-21 10:34:06 +08:00
|
|
|
|
axisPointer: { type: 'shadow' },
|
|
|
|
|
|
backgroundColor: 'rgba(13, 34, 37, 0.95)',
|
|
|
|
|
|
borderColor: 'rgba(6, 189, 221, 0.8)',
|
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
|
borderRadius: 4,
|
|
|
|
|
|
padding: [12, 16],
|
2025-10-14 18:16:49 +08:00
|
|
|
|
textStyle: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
fontSize: 12
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
|
|
|
|
|
legend: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
data: ['计划', '实际'],
|
|
|
|
|
|
top: 8,
|
|
|
|
|
|
right: 20,
|
2025-10-18 15:55:58 +08:00
|
|
|
|
textStyle: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
fontSize: 12
|
|
|
|
|
|
},
|
|
|
|
|
|
itemWidth: 12,
|
|
|
|
|
|
itemHeight: 12
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
|
|
|
|
|
grid: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
left: '6%',
|
2025-10-14 18:16:49 +08:00
|
|
|
|
right: '4%',
|
2026-01-21 10:34:06 +08:00
|
|
|
|
bottom: '10%',
|
|
|
|
|
|
top: '22%',
|
|
|
|
|
|
containLabel: true
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
xAxis: {
|
|
|
|
|
|
type: 'category',
|
|
|
|
|
|
data: categories,
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
fontSize: 12
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
axisLine: {
|
2025-10-14 18:16:49 +08:00
|
|
|
|
lineStyle: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
color: 'rgba(255, 255, 255, 0.3)',
|
|
|
|
|
|
type: 'dashed'
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
axisTick: { show: false }
|
|
|
|
|
|
},
|
|
|
|
|
|
yAxis: {
|
|
|
|
|
|
type: 'value',
|
|
|
|
|
|
min: 0,
|
|
|
|
|
|
max: 20,
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
fontSize: 12
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
|
|
|
|
|
axisLine: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
show: false
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
splitLine: {
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
color: 'rgba(0, 255, 255, 0.2)',
|
|
|
|
|
|
type: 'dashed',
|
|
|
|
|
|
opacity: 0.3
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
axisTick: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
show: false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
series: [
|
|
|
|
|
|
{
|
|
|
|
|
|
z: 1,
|
|
|
|
|
|
name: '计划',
|
|
|
|
|
|
type: 'bar',
|
|
|
|
|
|
data: plannedData,
|
|
|
|
|
|
barWidth: barWidth,
|
|
|
|
|
|
barGap: '0%',
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
color: color1
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
{
|
|
|
|
|
|
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
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
{
|
|
|
|
|
|
z: 1,
|
|
|
|
|
|
name: '实际',
|
|
|
|
|
|
type: 'bar',
|
|
|
|
|
|
data: actualData,
|
|
|
|
|
|
barWidth: barWidth,
|
|
|
|
|
|
barGap: '50%',
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
color: color2
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
{
|
|
|
|
|
|
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
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
2026-01-21 10:34:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
]
|
2025-10-14 18:16:49 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
civilProgressBarChart.setOption(option);
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 初始化电气专业进度柱状图(中间柱状图)
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
2025-10-18 15:55:58 +08:00
|
|
|
|
const option = {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
backgroundColor: 'transparent',
|
2025-10-14 18:16:49 +08:00
|
|
|
|
tooltip: {
|
|
|
|
|
|
trigger: 'axis',
|
2026-01-21 10:34:06 +08:00
|
|
|
|
axisPointer: { type: 'shadow' },
|
|
|
|
|
|
backgroundColor: 'rgba(13, 34, 37, 0.95)',
|
|
|
|
|
|
borderColor: 'rgba(6, 189, 221, 0.8)',
|
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
|
borderRadius: 4,
|
|
|
|
|
|
padding: [12, 16],
|
2025-10-14 18:16:49 +08:00
|
|
|
|
textStyle: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
fontSize: 12
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
|
|
|
|
|
legend: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
data: ['计划', '实际'],
|
|
|
|
|
|
top: 8,
|
|
|
|
|
|
right: 20,
|
2025-10-18 15:55:58 +08:00
|
|
|
|
textStyle: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
fontSize: 12
|
|
|
|
|
|
},
|
|
|
|
|
|
itemWidth: 12,
|
|
|
|
|
|
itemHeight: 12
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
|
|
|
|
|
grid: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
left: '6%',
|
2025-10-14 18:16:49 +08:00
|
|
|
|
right: '4%',
|
2026-01-21 10:34:06 +08:00
|
|
|
|
bottom: '10%',
|
|
|
|
|
|
top: '22%',
|
|
|
|
|
|
containLabel: true
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
xAxis: {
|
|
|
|
|
|
type: 'category',
|
|
|
|
|
|
data: categories,
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
fontSize: 12
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
axisLine: {
|
2025-10-14 18:16:49 +08:00
|
|
|
|
lineStyle: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
color: 'rgba(255, 255, 255, 0.3)',
|
|
|
|
|
|
type: 'dashed'
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
axisTick: { show: false }
|
|
|
|
|
|
},
|
|
|
|
|
|
yAxis: {
|
|
|
|
|
|
type: 'value',
|
|
|
|
|
|
min: 0,
|
|
|
|
|
|
max: 20,
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
fontSize: 12
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
|
|
|
|
|
axisLine: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
show: false
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
splitLine: {
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
color: 'rgba(0, 255, 255, 0.2)',
|
|
|
|
|
|
type: 'dashed',
|
|
|
|
|
|
opacity: 0.3
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
axisTick: {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
show: false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
series: [
|
|
|
|
|
|
{
|
|
|
|
|
|
z: 1,
|
|
|
|
|
|
name: '计划',
|
|
|
|
|
|
type: 'bar',
|
|
|
|
|
|
data: plannedData,
|
|
|
|
|
|
barWidth: barWidth,
|
|
|
|
|
|
barGap: '0%',
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
color: color1
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
{
|
|
|
|
|
|
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
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
{
|
|
|
|
|
|
z: 1,
|
|
|
|
|
|
name: '实际',
|
|
|
|
|
|
type: 'bar',
|
|
|
|
|
|
data: actualData,
|
|
|
|
|
|
barWidth: barWidth,
|
|
|
|
|
|
barGap: '50%',
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
color: color2
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
{
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
};
|
2025-10-14 18:16:49 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
electricalProgressBarChart.setOption(option);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化电气专业工序整体进度环形图
|
|
|
|
|
|
function initElectricalProgressChart() {
|
|
|
|
|
|
const chartDom = document.getElementById('electricalProgressChart');
|
|
|
|
|
|
if (!chartDom) return;
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
if (electricalProgressChart) {
|
|
|
|
|
|
electricalProgressChart.dispose();
|
|
|
|
|
|
}
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
electricalProgressChart = echarts.init(chartDom);
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 模拟数据 - 后续替换为真实接口数据
|
|
|
|
|
|
const progressRate = 50.00;
|
|
|
|
|
|
const remainingRate = 50.00;
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08: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
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
extraCssText: 'z-index: 9999999999;',
|
2026-01-24 14:23:35 +08:00
|
|
|
|
formatter: function (params) {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
const name = params.name;
|
|
|
|
|
|
const value = params.value;
|
|
|
|
|
|
const color = params.color;
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
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',
|
2026-01-24 14:23:35 +08:00
|
|
|
|
formatter: function () {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
return '{a|50}{b|%}';
|
2025-10-14 18:16:49 +08:00
|
|
|
|
},
|
2026-01-21 10:34:06 +08:00
|
|
|
|
rich: {
|
|
|
|
|
|
a: {
|
|
|
|
|
|
fontSize: 32,
|
|
|
|
|
|
fontWeight: 'bold',
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
lineHeight: 40
|
|
|
|
|
|
},
|
|
|
|
|
|
b: {
|
|
|
|
|
|
fontSize: 20,
|
|
|
|
|
|
fontWeight: 'bold',
|
|
|
|
|
|
color: '#FFFFFF',
|
|
|
|
|
|
lineHeight: 40
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
2026-01-21 10:34:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
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)'
|
|
|
|
|
|
}])
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
2025-10-14 18:16:49 +08:00
|
|
|
|
};
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
electricalProgressChart.setOption(option);
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
|
|
|
|
|
// 添加点击事件监听
|
|
|
|
|
|
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: '未完成' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}]
|
|
|
|
|
|
});
|
2025-10-14 18:16:49 +08:00
|
|
|
|
}
|
2025-10-18 15:55:58 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
// 初始化异常工序预警列表
|
|
|
|
|
|
function initWarningList() {
|
|
|
|
|
|
// 生成模拟警告数据
|
|
|
|
|
|
const warnings = [];
|
|
|
|
|
|
const teams = ['施工队A', '施工队B', '施工队C', '施工队D', '施工队E'];
|
|
|
|
|
|
const processes = ['钢筋绑扎工序', '混凝土浇筑工序', '模板安装工序', '钢筋焊接工序', '混凝土养护工序'];
|
|
|
|
|
|
const messages = ['进度滞后', '质量异常', '人员不足', '设备故障', '材料短缺'];
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
const today = new Date();
|
|
|
|
|
|
for (let i = 0; i < 15; i++) {
|
|
|
|
|
|
const date = new Date(today);
|
|
|
|
|
|
date.setDate(date.getDate() - Math.floor(i / 3));
|
2026-01-24 14:23:35 +08:00
|
|
|
|
const dateStr = date.getFullYear() + '-' +
|
|
|
|
|
|
String(date.getMonth() + 1).padStart(2, '0') + '-' +
|
|
|
|
|
|
String(date.getDate()).padStart(2, '0');
|
|
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
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)]
|
2025-10-18 15:55:58 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
|
|
|
|
|
const warningItemsHtml = warnings.map(function (warning) {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
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}
|
2025-10-18 15:55:58 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
`;
|
2026-01-21 10:34:06 +08:00
|
|
|
|
}).join('');
|
|
|
|
|
|
|
|
|
|
|
|
const $warningList = $('#civilWarningList');
|
|
|
|
|
|
$warningList.html(warningItemsHtml);
|
|
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
setTimeout(function () {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
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);
|
|
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
setTimeout(function () {
|
2026-01-21 10:34:06 +08:00
|
|
|
|
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() {
|
2026-01-24 14:23:35 +08:00
|
|
|
|
const processNames = [];
|
2026-01-21 10:34:06 +08:00
|
|
|
|
const mockData = [];
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
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: '当前进度滞后,请合理安排工作计划及投入的人员设备等资源'
|
2025-10-18 15:55:58 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
return mockData;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化土建专业进度偏差分析表格
|
|
|
|
|
|
function initCivilDeviationTable() {
|
|
|
|
|
|
const mockData = generateCivilDeviationMockData();
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
table.render({
|
|
|
|
|
|
elem: '#civilDeviationTable',
|
|
|
|
|
|
data: mockData,
|
|
|
|
|
|
skin: 'line',
|
|
|
|
|
|
page: false,
|
|
|
|
|
|
height: 'full-80',
|
|
|
|
|
|
cols: [[
|
2026-01-24 14:23:35 +08:00
|
|
|
|
{ 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' }
|
2026-01-21 10:34:06 +08:00
|
|
|
|
]]
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化电气专业进度偏差分析表格(与左侧相同列表)
|
|
|
|
|
|
function initElectricalDeviationTable() {
|
|
|
|
|
|
const mockData = generateCivilDeviationMockData();
|
|
|
|
|
|
|
|
|
|
|
|
table.render({
|
|
|
|
|
|
elem: '#electricalDeviationTable',
|
|
|
|
|
|
data: mockData,
|
|
|
|
|
|
skin: 'line',
|
|
|
|
|
|
page: false,
|
|
|
|
|
|
height: 'full-80',
|
|
|
|
|
|
cols: [[
|
2026-01-24 14:23:35 +08:00
|
|
|
|
{ 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' }
|
2026-01-21 10:34:06 +08:00
|
|
|
|
]]
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查询土建专业进度偏差
|
|
|
|
|
|
function queryCivilDeviation() {
|
|
|
|
|
|
const keyword = $('#civilKeywordInput').val();
|
|
|
|
|
|
const dateRange = $('#civilDeviationDateRange').val();
|
2026-01-24 14:23:35 +08:00
|
|
|
|
|
2026-01-21 10:34:06 +08:00
|
|
|
|
table.reload('civilDeviationTable', {
|
|
|
|
|
|
where: {
|
|
|
|
|
|
keyword: keyword,
|
|
|
|
|
|
dateRange: dateRange,
|
|
|
|
|
|
bidCode: parent.parent.$('#bidPro').val() || ''
|
|
|
|
|
|
},
|
|
|
|
|
|
page: {
|
|
|
|
|
|
curr: 1
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
// 此处原先的进度图表日期查询与接口调用已移除,
|
|
|
|
|
|
// 进度展示图表暂时使用页面内的模拟数据进行展示。
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化统计卡片点击事件
|
|
|
|
|
|
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, '土建专业工序详情');
|
2026-01-21 10:34:06 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
// 电气专业统计卡片
|
|
|
|
|
|
$('#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, '电气专业工序详情');
|
|
|
|
|
|
});
|
2026-01-21 10:34:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
// 打开工序详情弹框
|
|
|
|
|
|
function openProcessDetailModal(gxType, title) {
|
|
|
|
|
|
currentGxType = gxType;
|
|
|
|
|
|
const modal = $('#processDetailModal');
|
|
|
|
|
|
$('#processDetailModalTitle').text(title);
|
|
|
|
|
|
modal.addClass('show');
|
2026-01-21 10:34:06 +08:00
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
// 重置关键字输入框
|
|
|
|
|
|
modalProcessKeyword = '';
|
|
|
|
|
|
$('#modalProcessKeywordInput').val('');
|
2026-01-21 10:34:06 +08:00
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
// 延迟初始化表格,确保弹框完全显示后再渲染表格
|
|
|
|
|
|
setTimeout(function () {
|
|
|
|
|
|
initModalProcessDetailTable();
|
|
|
|
|
|
}, 100);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭工序详情弹框
|
|
|
|
|
|
function closeProcessDetailModal() {
|
|
|
|
|
|
const modal = $('#processDetailModal');
|
|
|
|
|
|
modal.removeClass('show');
|
2026-01-21 10:34:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
// 初始化弹框内工序详情表格
|
|
|
|
|
|
function initModalProcessDetailTable() {
|
|
|
|
|
|
table.render({
|
|
|
|
|
|
elem: '#modalProcessDetailTable',
|
|
|
|
|
|
id: 'modalProcessDetailTable',
|
|
|
|
|
|
url: commonUrl + 'screen/project/constructionProgress/detail',
|
2026-01-21 10:34:06 +08:00
|
|
|
|
headers: {
|
|
|
|
|
|
decrypt: 'decrypt',
|
|
|
|
|
|
Authorization: token
|
|
|
|
|
|
},
|
2026-01-24 14:23:35 +08:00
|
|
|
|
method: 'GET',
|
|
|
|
|
|
where: {
|
|
|
|
|
|
projectId: bidCode,
|
|
|
|
|
|
startTestDay: queryParams.startTestDay,
|
|
|
|
|
|
endTestDay: queryParams.endTestDay,
|
|
|
|
|
|
gxType: currentGxType,
|
|
|
|
|
|
keyword: modalProcessKeyword || ''
|
2026-01-21 10:34:06 +08:00
|
|
|
|
},
|
2026-01-24 14:23:35 +08:00
|
|
|
|
skin: 'line',
|
|
|
|
|
|
page: {
|
|
|
|
|
|
layout: ['prev', 'page', 'next', 'count', 'skip'],
|
|
|
|
|
|
groups: 5,
|
|
|
|
|
|
limit: 10,
|
|
|
|
|
|
limits: [10, 20, 30, 50]
|
2026-01-21 10:34:06 +08:00
|
|
|
|
},
|
2026-01-24 14:23:35 +08:00
|
|
|
|
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 ? '电气' : '');
|
2026-01-24 18:55:45 +08:00
|
|
|
|
}
|
2026-01-24 14:23:35 +08:00
|
|
|
|
},
|
|
|
|
|
|
{ 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>';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
]]
|
2026-01-21 10:34:06 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
// 弹框内查询工序记录
|
|
|
|
|
|
function queryModalProcessRecords() {
|
|
|
|
|
|
modalProcessKeyword = $('#modalProcessKeywordInput').val() || '';
|
2026-01-21 10:34:06 +08:00
|
|
|
|
|
2026-01-24 14:23:35 +08:00
|
|
|
|
// 重新加载表格
|
|
|
|
|
|
table.reload('modalProcessDetailTable', {
|
|
|
|
|
|
where: {
|
|
|
|
|
|
projectId: bidCode,
|
|
|
|
|
|
startTestDay: queryParams.startTestDay,
|
|
|
|
|
|
endTestDay: queryParams.endTestDay,
|
|
|
|
|
|
gxType: currentGxType,
|
|
|
|
|
|
keyword: modalProcessKeyword
|
|
|
|
|
|
},
|
|
|
|
|
|
page: {
|
|
|
|
|
|
curr: 1
|
|
|
|
|
|
}
|
2026-01-21 10:34:06 +08:00
|
|
|
|
});
|
2025-10-18 15:55:58 +08:00
|
|
|
|
}
|