hb_zhgd_screen/js/pages/dataAnalysisOctober/proQualityAnalysis.js

829 lines
28 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 teamQualityChart = null;
let qualityRatioChart = null;
let currentPage = 1;
let pageSize = 10;
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();
});
// 初始化页面
function initPage() {
initTeamQualityChart();
initQualityRatioChart();
initQualityRecordTable();
initWarningList();
// 初始化自适应处理
// initResizeHandler();
}
// 防抖函数
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 初始化自适应处理
function initResizeHandler() {
// 创建防抖的resize处理函数
const handleResize = debounce(function() {
// 重新调整所有图表大小
if (teamQualityChart) {
try {
teamQualityChart.resize();
} catch(e) {
console.error('调整柱状图大小失败:', e);
}
}
if (qualityRatioChart) {
try {
qualityRatioChart.resize();
} catch(e) {
console.error('调整环形图大小失败:', e);
}
}
// 重新调整表格大小(如果需要)
if (table) {
try {
table.resize('qualityRecordTable');
} catch(e) {
// 表格resize可能不支持忽略错误
}
}
}, 300); // 300ms防抖延迟
// 监听窗口resize事件
window.addEventListener('resize', handleResize);
// 监听iframe的resize如果页面在iframe中
if (window.parent !== window) {
window.parent.addEventListener('resize', handleResize);
}
// 页面加载完成后也执行一次resize确保初始状态正确
setTimeout(function() {
handleResize();
}, 100);
}
// 初始化施工队伍质量合格率柱状图
function initTeamQualityChart() {
const chartDom = document.getElementById('teamQualityChart');
if (!chartDom) return;
if (teamQualityChart) {
teamQualityChart.dispose();
}
teamQualityChart = echarts.init(chartDom);
// 模拟数据 - 后续替换为真实接口数据
const teams = ['班组一', '班组二', '班组三', '班组四', '班组五', '班组六', '班组七', '班组八'];
const acceptedData = [450, 380, 520, 420, 480, 350, 510, 440]; // 已验收
const unacceptedData = [150, 120, 180, 160, 140, 221, 170, 160]; // 未验收
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
shadowStyle: {
color: 'rgba(6, 189, 221, 0.2)'
}
},
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
},
formatter: function(params) {
if (!params || params.length === 0) return '';
const dataIndex = params[0].dataIndex;
const teamName = params[0].name;
let unaccepted = 0;
let accepted = 0;
params.forEach(function(item) {
if (item.seriesName === '未验收') {
unaccepted = item.value;
} else if (item.seriesName === '已验收') {
accepted = item.value;
}
});
return `
<div style="font-weight: bold; margin-bottom: 10px; color: #FFFFFF; font-size: 13px;">子分部工程</div>
<div style="display: flex; align-items: center; margin-bottom: 6px; min-width: 150px;">
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: #4A90E2; margin-right: 10px; flex-shrink: 0;"></span>
<span style="color: #B9D6D9; margin-right: 10px;">未验收</span>
<span style="color: #FFFFFF; margin-left: auto; font-weight: bold;">${unaccepted}</span>
</div>
<div style="display: flex; align-items: center;">
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: #00FFB8; margin-right: 10px; flex-shrink: 0;"></span>
<span style="color: #B9D6D9; margin-right: 10px;">已验收</span>
<span style="color: #FFFFFF; margin-left: auto; font-weight: bold;">${accepted}</span>
</div>
`;
}
},
legend: {
data: ['未验收', '已验收'],
top: '-15%',
right: '5%',
itemGap: 15,
textStyle: {
color: '#FFFFFF',
fontSize: 12
},
itemWidth: 12,
itemHeight: 12,
icon: 'circle'
},
grid: {
left: '2%',
right: '2%',
bottom: '3%',
top: '10%',
containLabel: true
},
xAxis: {
type: 'category',
data: teams,
axisLabel: {
color: '#FFFFFF',
fontSize: 12,
margin: 10
},
axisLine: {
show: true,
lineStyle: {
color: 'rgba(255, 255, 255, 0.3)',
width: 1
}
},
axisTick: {
show: false
}
},
yAxis: {
type: 'value',
min: 0,
max: 600,
interval: 150,
axisLabel: {
color: '#FFFFFF',
fontSize: 12,
margin: 8
},
axisLine: {
show: true,
lineStyle: {
color: 'rgba(255, 255, 255, 0.3)',
width: 1
}
},
splitLine: {
show: true,
lineStyle: {
color: 'rgba(255, 255, 255, 0.15)',
type: 'dashed',
width: 1
}
}
},
series: [
{
name: '未验收',
type: 'bar',
data: unacceptedData,
stack: 'total',
barWidth: '45%',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(74, 144, 226, 0.9)'
}, {
offset: 1,
color: 'rgba(74, 144, 226, 0.6)'
}]),
borderRadius: [2, 2, 0, 0],
shadowColor: 'rgba(74, 144, 226, 0.5)',
shadowBlur: 8,
shadowOffsetY: 3
},
emphasis: {
itemStyle: {
shadowBlur: 15,
shadowOffsetY: 5,
shadowColor: 'rgba(74, 144, 226, 0.8)'
}
},
label: {
show: false
}
},
{
name: '已验收',
type: 'bar',
data: acceptedData,
stack: 'total',
barWidth: '45%',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: '#00FFB8'
}, {
offset: 0.5,
color: '#00D4FF'
}, {
offset: 1,
color: '#00A8FF'
}]),
borderRadius: [2, 2, 0, 0],
shadowColor: 'rgba(0, 255, 184, 0.6)',
shadowBlur: 10,
shadowOffsetY: 4,
borderColor: 'rgba(0, 255, 184, 0.3)',
borderWidth: 1
},
emphasis: {
itemStyle: {
shadowBlur: 18,
shadowOffsetY: 6,
shadowColor: 'rgba(0, 255, 184, 0.9)',
borderColor: 'rgba(0, 255, 184, 0.6)',
borderWidth: 2
}
},
label: {
show: true,
position: 'top',
color: '#FFFFFF',
fontSize: 12,
fontWeight: 'bold',
formatter: function(params) {
return params.value;
},
textShadowColor: 'rgba(0, 0, 0, 0.5)',
textShadowBlur: 3,
textShadowOffsetX: 1,
textShadowOffsetY: 1
}
}
]
};
teamQualityChart.setOption(option);
}
// 初始化质量检测合格占比环形图
function initQualityRatioChart() {
const chartDom = document.getElementById('qualityRatioChart');
if (!chartDom) return;
if (qualityRatioChart) {
qualityRatioChart.dispose();
}
qualityRatioChart = echarts.init(chartDom);
// 模拟数据 - 后续替换为真实接口数据
const passRate = 65.00;
const failRate = 35.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;', // 确保tooltip在最上层
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: [
// 外层圆环 - 2-4px宽度白色/亮色,完整圆环
{
name: '外层圆环',
type: 'pie',
radius: ['94%', '97%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
silent: true, // 不响应鼠标事件
itemStyle: {
color: function(params) {
// 根据图片,外层圆环可能是白色或亮青色,带发光效果
return 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: ''
}]
},
// 内层圆环 - 15-20px宽度显示数据
{
name: '质量检测合格占比',
type: 'pie',
radius: ['70%', '80%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
// 添加分段之间的间隙
gap: 3, // 分段之间的间隙(像素),创建可见的深色间隙
itemStyle: {
borderRadius: 5, // 添加圆角效果,使圆环更圆润
borderColor: 'transparent',
borderWidth: 0
},
label: {
show: true,
position: 'center',
formatter: function() {
return '{a|100}{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: passRate,
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: failRate,
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)'
}])
}
}
]
}
]
};
qualityRatioChart.setOption(option);
// 更新显示值
const passRatioEl = document.getElementById('passRatio');
const failRatioEl = document.getElementById('failRatio');
if (passRatioEl) {
passRatioEl.textContent = passRate.toFixed(2) + '%';
}
if (failRatioEl) {
failRatioEl.textContent = failRate.toFixed(2) + '%';
}
}
// 生成模拟数据
function generateMockData() {
const teams = ['班组一', '班组二', '班组三', '班组四', '班组五', '班组六', '班组七', '班组八'];
const contents = [
'混凝土强度检测', '钢筋焊接质量检测', '模板安装质量检测', '钢筋绑扎质量检测',
'混凝土浇筑质量检测', '模板拆除质量检测', '钢筋加工质量检测', '混凝土养护质量检测',
'钢筋连接质量检测', '模板支撑质量检测', '混凝土配合比检测', '钢筋保护层检测',
'模板平整度检测', '混凝土坍落度检测', '钢筋间距检测', '模板垂直度检测'
];
const results = ['合格', '不合格'];
const mockData = [];
const today = new Date();
for (let i = 0; i < 25; 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');
mockData.push({
teamName: teams[Math.floor(Math.random() * teams.length)],
inspectionContent: contents[Math.floor(Math.random() * contents.length)],
inspectionDate: dateStr,
inspectionResult: results[Math.random() > 0.3 ? 0 : 1]
});
}
return mockData;
}
// 初始化质量检测记录表格
function initQualityRecordTable() {
const mockData = generateMockData();
table.render({
elem: '#qualityRecordTable',
data: mockData, // 直接使用数据不使用url
skin: 'line',
page: {
layout: ['prev', 'page', 'next', 'count', 'skip'],
groups: 5,
limit: 10,
limits: [10, 20, 30]
},
height: 'full',
cols: [[
{type: 'numbers', title: '序号', width: '15%', align: 'center'},
{field: 'teamName', title: '班组名称', width: '20%', align: 'center'},
{field: 'inspectionContent', title: '检测内容', width: '25%', align: 'center'},
{field: 'inspectionDate', title: '检测日期', width: '20%', align: 'center'},
{field: 'inspectionResult', title: '检测结果', width: '20%', align: 'center', templet: function(d) {
return d.inspectionResult === '合格' ?
'<span style="color: #00FFB8;">合格</span>' :
'<span style="color: #FF6B6B;">不合格</span>';
}}
]]
});
}
// 初始化日期范围选择器
function initDateRangePicker() {
// 使用范围选择器,两个输入框都可以触发
// 先绑定开始日期
laydate.render({
elem: '#dateStart',
type: 'date',
range: ['#dateStart', '#dateEnd'],
format: 'yyyy-MM-dd',
theme: 'dark',
max: 0, // 最大日期为今天
done: function(value, date, endDate) {
if (value) {
const dates = value.split(' - ');
if (dates.length === 2) {
$('#dateStart').val(dates[0]);
$('#dateEnd').val(dates[1]);
}
}
}
});
// 为结束日期也绑定相同的配置,确保点击结束日期也能触发
$('#dateEnd').on('click', function() {
// 点击结束日期时,触发开始日期的日期选择器
$('#dateStart').click();
});
}
// 查询记录
function queryRecords() {
const keyword = $('#keywordInput').val();
const startDate = $('#dateStart').val();
const endDate = $('#dateEnd').val();
table.reload('qualityRecordTable', {
where: {
keyword: keyword,
startDate: startDate,
endDate: endDate,
bidCode: parent.parent.$('#bidPro').val() || ''
},
page: {
curr: 1
}
});
}
// 初始化警告列表
function initWarningList() {
// 模拟数据 - 后续替换为真实接口数据
const warnings = [
{date: '2026-01-11', team: '班组一', message: '质量检测合格率低于80%,请持续关注该班组施工质量情况。'},
{date: '2026-01-11', team: '班组三', message: '质量检测合格率低于80%,请持续关注该班组施工质量情况。'},
{date: '2026-01-10', team: '班组五', message: '质量检测合格率低于80%,请持续关注该班组施工质量情况。'},
{date: '2026-01-10', team: '班组七', message: '质量检测合格率低于80%,请持续关注该班组施工质量情况。'},
{date: '2026-01-09', team: '班组二', message: '质量检测合格率低于80%,请持续关注该班组施工质量情况。'},
{date: '2026-01-09', team: '班组四', message: '质量检测合格率低于80%,请持续关注该班组施工质量情况。'},
{date: '2026-01-08', team: '班组六', message: '质量检测合格率低于80%,请持续关注该班组施工质量情况。'},
{date: '2026-01-08', team: '班组八', message: '质量检测合格率低于80%,请持续关注该班组施工质量情况。'}
];
// 生成警告项HTML
const warningItemsHtml = warnings.map(function(warning) {
const fullText = `${warning.date} ${warning.team}${warning.message}`;
return `
<div class="warning-item">
<div class="warning-icon"></div>
<div class="warning-text" title="${fullText}">
${fullText}
</div>
</div>
`;
}).join('');
const $warningList = $('#warningList');
// 先渲染一次,检查是否需要滚动
$warningList.html(warningItemsHtml);
// 等待DOM渲染后检查高度
setTimeout(function() {
const $container = $warningList.parent();
const $listElement = $warningList[0];
if (!$listElement) return;
const contentHeight = $listElement.scrollHeight;
const containerHeight = $container.height();
// 如果内容高度超过容器高度,启用无缝滚动
if (contentHeight > containerHeight) {
// 复制2份内容实现无缝循环原始 + 复制1
const duplicatedHtml = warningItemsHtml + warningItemsHtml;
$warningList.html(duplicatedHtml);
// 等待DOM更新后重新计算高度和启动动画
setTimeout(function() {
const newContentHeight = $listElement.scrollHeight;
const singleListHeight = newContentHeight / 2; // 单份内容的高度
// 添加滚动类
$warningList.addClass('warning-list-scroll');
// 计算动画时长,根据内容高度动态调整
// 滚动距离是单份内容的高度50%对应单份高度)
// 每25px用1秒确保滚动速度适中且流畅
const animationDuration = Math.max(25, Math.ceil(singleListHeight / 25));
// 动态设置动画
$warningList.css({
'animation': `scrollWarning ${animationDuration}s linear infinite`,
'animation-fill-mode': 'forwards'
});
}, 200);
} else {
// 如果内容不需要滚动,也添加类以保持样式一致
$warningList.addClass('warning-list-scroll');
}
}, 500);
}
// 获取施工队伍质量合格率数据(接口调用)
function getTeamQualityData() {
const bidCode = parent.parent.$('#bidPro').val() || '';
const url = commonUrl + 'screen/largeScreen/dataAnalysis/getTeamQualityPassRate';
$.ajax({
url: url,
type: 'GET',
headers: {
decrypt: 'decrypt',
Authorization: token
},
data: {
bidCode: bidCode
},
success: function(res) {
if (res.code === 200 && res.data) {
// 更新图表数据
updateTeamQualityChart(res.data);
}
},
error: function(err) {
console.error('获取施工队伍质量合格率数据失败:', err);
}
});
}
// 更新施工队伍质量合格率图表
function updateTeamQualityChart(data) {
if (!teamQualityChart) return;
const teams = data.map(item => item.teamName);
const acceptedData = data.map(item => item.accepted || 0);
const unacceptedData = data.map(item => item.unaccepted || 0);
teamQualityChart.setOption({
xAxis: {
data: teams
},
series: [
{
name: '未验收',
data: unacceptedData
},
{
name: '已验收',
data: acceptedData
}
]
});
}
// 获取质量检测合格占比数据(接口调用)
function getQualityRatioData() {
const bidCode = parent.parent.$('#bidPro').val() || '';
const url = commonUrl + 'screen/largeScreen/dataAnalysis/getQualityRatio';
$.ajax({
url: url,
type: 'GET',
headers: {
decrypt: 'decrypt',
Authorization: token
},
data: {
bidCode: bidCode
},
success: function(res) {
if (res.code === 200 && res.data) {
// 更新图表数据
updateQualityRatioChart(res.data);
}
},
error: function(err) {
console.error('获取质量检测合格占比数据失败:', err);
}
});
}
// 更新质量检测合格占比图表
function updateQualityRatioChart(data) {
if (!qualityRatioChart) return;
const passRate = data.passRate || 0;
const failRate = data.failRate || 0;
qualityRatioChart.setOption({
series: [{
data: [
{value: passRate, name: '合格'},
{value: failRate, name: '不合格'}
]
}]
});
// 更新显示值
document.getElementById('passRatio').textContent = passRate.toFixed(2) + '%';
document.getElementById('failRatio').textContent = failRate.toFixed(2) + '%';
}
// 获取警告列表数据(接口调用)
function getWarningListData() {
const bidCode = parent.parent.$('#bidPro').val() || '';
const url = commonUrl + 'screen/largeScreen/dataAnalysis/getQualityWarnings';
$.ajax({
url: url,
type: 'GET',
headers: {
decrypt: 'decrypt',
Authorization: token
},
data: {
bidCode: bidCode
},
success: function(res) {
if (res.code === 200 && res.data) {
// 更新警告列表
updateWarningList(res.data);
}
},
error: function(err) {
console.error('获取警告列表数据失败:', err);
}
});
}
// 更新警告列表
function updateWarningList(data) {
const warningListHtml = data.map(function(warning) {
return `
<div class="warning-item">
<div class="warning-icon"></div>
<div class="warning-text">
${warning.date} ${warning.message}
</div>
</div>
`;
}).join('');
$('#warningList').html(warningListHtml);
}