From f2e91a3304aca45a248cc4b66996a30532700949 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期四, 26 三月 2026 09:53:24 +0800
Subject: [PATCH] #
---
src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js | 439 +++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 410 insertions(+), 29 deletions(-)
diff --git a/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js b/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js
index 12c5835..4ea91bf 100644
--- a/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js
+++ b/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js
@@ -29,6 +29,12 @@
return {
summary: {
taskCount: 0,
+ taskStartTime: null,
+ taskStartTime$: "",
+ taskEndTime: null,
+ taskEndTime$: "",
+ taskDurationMs: null,
+ avgTaskBeatDurationMs: null,
avgTotalDurationMs: null,
avgStationDurationMs: null,
avgCraneDurationMs: null,
@@ -62,6 +68,7 @@
pageTotal: 0,
listLoading: false,
analyzeLoading: false,
+ exportingPdf: false,
selectedWrkNoMap: {},
analysis: createEmptyAnalysis(),
analysisReady: false,
@@ -202,16 +209,18 @@
});
},
syncCurrentPageSelection: function (selection) {
+ var nextMap = Object.assign({}, this.selectedWrkNoMap);
var selectedMap = {};
(selection || []).forEach(function (row) {
selectedMap[row.wrkNo] = true;
});
(this.tableData || []).forEach(function (row) {
- delete this.selectedWrkNoMap[row.wrkNo];
- }, this);
+ delete nextMap[row.wrkNo];
+ });
Object.keys(selectedMap).forEach(function (key) {
- this.selectedWrkNoMap[key] = true;
- }, this);
+ nextMap[key] = true;
+ });
+ this.selectedWrkNoMap = nextMap;
},
runAnalysis: function () {
var self = this;
@@ -267,6 +276,298 @@
}
});
},
+ exportAnalysisPdf: function () {
+ var self = this;
+ if (!this.analysisReady) {
+ this.$message.warning("璇峰厛鎵ц鍒嗘瀽锛屽啀瀵煎嚭PDF");
+ return;
+ }
+ if (!window.html2canvas || !window.jspdf || !window.jspdf.jsPDF) {
+ this.$message.error("PDF瀵煎嚭缁勪欢鍔犺浇澶辫触");
+ return;
+ }
+ this.exportingPdf = true;
+ this.$nextTick(function () {
+ self.resizeCharts();
+ window.setTimeout(function () {
+ self.generatePdf();
+ }, 300);
+ });
+ },
+ generatePdf: function () {
+ var self = this;
+ var visualRoot = this.$refs.analysisVisualRoot;
+ var detailRoot = this.$refs.exportDetailRoot;
+ var detailTable = this.$refs.exportDetailTable;
+ var cleanup = function () {
+ self.exportingPdf = false;
+ self.$nextTick(function () {
+ self.resizeCharts();
+ });
+ };
+ if (!visualRoot || !detailRoot || !detailTable) {
+ this.$message.error("鏈壘鍒板彲瀵煎嚭鐨勫垎鏋愬尯鍩�");
+ cleanup();
+ return;
+ }
+ var jsPDF = window.jspdf.jsPDF;
+ var pdf = new jsPDF("p", "mm", "a4");
+ Promise.all([
+ window.html2canvas(visualRoot, this.buildCaptureOptions(visualRoot)),
+ window.html2canvas(detailRoot, this.buildCaptureOptions(detailRoot))
+ ]).then(function (results) {
+ var visualAvoidSplitBounds = self.collectAvoidSplitBounds(visualRoot, results[0], [
+ ".quality-banner",
+ ".chart-card"
+ ]);
+ self.appendCanvasSlicesToPdf(pdf, results[0], {
+ margin: 8,
+ startY: 8,
+ addNewPage: false,
+ avoidSplitBounds: visualAvoidSplitBounds
+ });
+ self.appendDetailTableToPdf(pdf, results[1], detailRoot, detailTable, 8);
+ pdf.save(self.buildPdfFileName());
+ self.$message.success("PDF瀵煎嚭鎴愬姛");
+ cleanup();
+ }).catch(function (error) {
+ console.error(error);
+ self.$message.error("PDF瀵煎嚭澶辫触");
+ cleanup();
+ });
+ },
+ buildCaptureOptions: function (target) {
+ return {
+ scale: 2,
+ useCORS: true,
+ backgroundColor: "#ffffff",
+ logging: false,
+ scrollX: 0,
+ scrollY: -window.scrollY,
+ width: target.scrollWidth,
+ height: target.scrollHeight,
+ windowWidth: Math.max(document.documentElement.clientWidth, target.scrollWidth),
+ windowHeight: Math.max(document.documentElement.clientHeight, target.scrollHeight)
+ };
+ },
+ appendCanvasSlicesToPdf: function (pdf, canvas, options) {
+ var settings = options || {};
+ var pageWidth = pdf.internal.pageSize.getWidth();
+ var pageHeight = pdf.internal.pageSize.getHeight();
+ var margin = settings.margin == null ? 8 : settings.margin;
+ var startY = settings.startY == null ? margin : settings.startY;
+ var usableWidth = pageWidth - margin * 2;
+ var pxPerMm = canvas.width / usableWidth;
+ var renderedHeight = 0;
+ var currentY = startY;
+ var pageCanvas = document.createElement("canvas");
+ var pageContext = pageCanvas.getContext("2d");
+ while (renderedHeight < canvas.height) {
+ var currentPageHeightPx = Math.max(1, Math.floor((pageHeight - margin - currentY) * pxPerMm));
+ var sliceHeight = Math.min(currentPageHeightPx, canvas.height - renderedHeight);
+ sliceHeight = this.resolveSliceHeight(renderedHeight, sliceHeight, settings.avoidSplitBounds);
+ pageCanvas.width = canvas.width;
+ pageCanvas.height = sliceHeight;
+ pageContext.fillStyle = "#ffffff";
+ pageContext.fillRect(0, 0, pageCanvas.width, pageCanvas.height);
+ pageContext.drawImage(
+ canvas,
+ 0,
+ renderedHeight,
+ canvas.width,
+ sliceHeight,
+ 0,
+ 0,
+ pageCanvas.width,
+ pageCanvas.height
+ );
+ if (renderedHeight === 0 && settings.addNewPage) {
+ pdf.addPage();
+ } else if (renderedHeight > 0) {
+ pdf.addPage();
+ currentY = margin;
+ }
+ pdf.addImage(
+ pageCanvas.toDataURL("image/jpeg", 0.95),
+ "JPEG",
+ margin,
+ currentY,
+ usableWidth,
+ sliceHeight / pxPerMm,
+ undefined,
+ "FAST"
+ );
+ renderedHeight += sliceHeight;
+ currentY = margin;
+ }
+ },
+ collectAvoidSplitBounds: function (rootElement, rootCanvas, selectors) {
+ if (!rootElement || !rootCanvas || !selectors || !selectors.length) {
+ return [];
+ }
+ var rootRect = rootElement.getBoundingClientRect();
+ if (!rootRect.width) {
+ return [];
+ }
+ var scale = rootCanvas.width / rootRect.width;
+ var elements = [];
+ selectors.forEach(function (selector) {
+ Array.prototype.push.apply(elements, Array.prototype.slice.call(rootElement.querySelectorAll(selector)));
+ });
+ return elements.map(function (element) {
+ var rect = element.getBoundingClientRect();
+ return {
+ top: Math.max(0, Math.round((rect.top - rootRect.top) * scale)),
+ bottom: Math.max(0, Math.round((rect.bottom - rootRect.top) * scale))
+ };
+ }).filter(function (item) {
+ return item.bottom > item.top;
+ }).sort(function (a, b) {
+ return a.top - b.top;
+ });
+ },
+ resolveSliceHeight: function (renderedHeight, desiredHeight, avoidSplitBounds) {
+ if (!avoidSplitBounds || !avoidSplitBounds.length) {
+ return desiredHeight;
+ }
+ var sliceEnd = renderedHeight + desiredHeight;
+ var adjustedHeight = desiredHeight;
+ avoidSplitBounds.forEach(function (bound) {
+ if (bound.top <= renderedHeight) {
+ return;
+ }
+ if (bound.top >= sliceEnd || bound.bottom <= sliceEnd) {
+ return;
+ }
+ var candidateHeight = bound.top - renderedHeight;
+ if (candidateHeight > 0 && candidateHeight < adjustedHeight) {
+ adjustedHeight = candidateHeight;
+ }
+ });
+ return adjustedHeight > 0 ? adjustedHeight : desiredHeight;
+ },
+ appendDetailTableToPdf: function (pdf, rootCanvas, detailRoot, detailTable, margin) {
+ var tbody = detailTable && detailTable.tBodies && detailTable.tBodies[0];
+ var rows = tbody ? Array.prototype.slice.call(tbody.rows) : [];
+ if (!rows.length) {
+ return;
+ }
+ var pageWidth = pdf.internal.pageSize.getWidth();
+ var pageHeight = pdf.internal.pageSize.getHeight();
+ var usableWidth = pageWidth - margin * 2;
+ var usableHeight = pageHeight - margin * 2;
+ var rootRect = detailRoot.getBoundingClientRect();
+ var tableRect = detailTable.getBoundingClientRect();
+ var scale = rootCanvas.width / rootRect.width;
+ var pxPerMm = rootCanvas.width / usableWidth;
+ var pageHeightPx = Math.max(1, Math.floor(usableHeight * pxPerMm));
+ var titleHeightPx = Math.max(0, Math.round((tableRect.top - rootRect.top) * scale));
+ var headerHeightPx = Math.max(1, Math.round(detailTable.tHead.getBoundingClientRect().height * scale));
+ var rowBounds = rows.map(function (row) {
+ var rect = row.getBoundingClientRect();
+ return {
+ top: Math.round((rect.top - tableRect.top) * scale),
+ bottom: Math.round((rect.bottom - tableRect.top) * scale)
+ };
+ });
+ var firstPage = true;
+ var startIndex = 0;
+ while (startIndex < rowBounds.length) {
+ var bodyCapacityPx = pageHeightPx - headerHeightPx - (firstPage ? titleHeightPx : 0);
+ var endIndex = this.findLastFittingRowIndex(rowBounds, startIndex, bodyCapacityPx);
+ var pageCanvas = this.createDetailPageCanvas(
+ rootCanvas,
+ titleHeightPx,
+ headerHeightPx,
+ rowBounds[startIndex].top,
+ rowBounds[endIndex].bottom,
+ firstPage
+ );
+ pdf.addPage();
+ pdf.addImage(
+ pageCanvas.toDataURL("image/jpeg", 0.95),
+ "JPEG",
+ margin,
+ margin,
+ usableWidth,
+ pageCanvas.height / pxPerMm,
+ undefined,
+ "FAST"
+ );
+ firstPage = false;
+ startIndex = endIndex + 1;
+ }
+ },
+ findLastFittingRowIndex: function (rowBounds, startIndex, bodyCapacityPx) {
+ var lastIndex = startIndex;
+ for (var i = startIndex; i < rowBounds.length; i++) {
+ if (rowBounds[i].bottom - rowBounds[startIndex].top > bodyCapacityPx) {
+ break;
+ }
+ lastIndex = i;
+ }
+ return lastIndex;
+ },
+ createDetailPageCanvas: function (rootCanvas, titleHeightPx, headerHeightPx, bodyStartPx, bodyEndPx, includeTitle) {
+ var width = rootCanvas.width;
+ var titleHeight = includeTitle ? titleHeightPx : 0;
+ var bodyHeight = Math.max(1, bodyEndPx - bodyStartPx);
+ var pageCanvas = document.createElement("canvas");
+ var pageContext = pageCanvas.getContext("2d");
+ pageCanvas.width = width;
+ pageCanvas.height = titleHeight + headerHeightPx + bodyHeight;
+ pageContext.fillStyle = "#ffffff";
+ pageContext.fillRect(0, 0, pageCanvas.width, pageCanvas.height);
+ var offsetY = 0;
+ if (titleHeight > 0) {
+ pageContext.drawImage(
+ rootCanvas,
+ 0,
+ 0,
+ width,
+ titleHeight,
+ 0,
+ 0,
+ width,
+ titleHeight
+ );
+ offsetY += titleHeight;
+ }
+ pageContext.drawImage(
+ rootCanvas,
+ 0,
+ titleHeightPx,
+ width,
+ headerHeightPx,
+ 0,
+ offsetY,
+ width,
+ headerHeightPx
+ );
+ offsetY += headerHeightPx;
+ pageContext.drawImage(
+ rootCanvas,
+ 0,
+ titleHeightPx + bodyStartPx,
+ width,
+ bodyHeight,
+ 0,
+ offsetY,
+ width,
+ bodyHeight
+ );
+ return pageCanvas;
+ },
+ buildPdfFileName: function () {
+ var now = new Date();
+ return "浠诲姟鎵ц鍒嗘瀽_" +
+ now.getFullYear() +
+ this.pad(now.getMonth() + 1) +
+ this.pad(now.getDate()) + "_" +
+ this.pad(now.getHours()) +
+ this.pad(now.getMinutes()) +
+ this.pad(now.getSeconds()) + ".pdf";
+ },
updateCharts: function () {
if (!this.analysisReady) {
this.disposeCharts();
@@ -296,11 +597,24 @@
if (!this.charts.duration) {
return;
}
+ var self = this;
var rows = this.analysis.durationCompare || [];
this.charts.duration.setOption({
- tooltip: { trigger: "axis" },
+ tooltip: {
+ trigger: "axis",
+ formatter: function (params) {
+ if (!params || !params.length) {
+ return "";
+ }
+ var lines = [params[0].axisValue];
+ params.forEach(function (item) {
+ lines.push(item.marker + item.seriesName + ": " + self.formatChartSeconds(item.value));
+ });
+ return lines.join("<br>");
+ }
+ },
legend: { data: ["绔欑偣鑰楁椂", "鍫嗗灈鏈鸿�楁椂", "鎬昏�楁椂"] },
- grid: { left: 50, right: 20, top: 40, bottom: 70 },
+ grid: { left: 88, right: 20, top: 40, bottom: 70, containLabel: true },
xAxis: {
type: "category",
data: rows.map(function (item) { return String(item.wrkNo); }),
@@ -310,14 +624,14 @@
type: "value",
axisLabel: {
formatter: function (value) {
- return Math.round((value || 0) / 1000) + "s";
+ return self.formatChartSeconds(value);
}
}
},
series: [
- { name: "绔欑偣鑰楁椂", type: "bar", barMaxWidth: 28, data: rows.map(function (item) { return item.stationDurationMs || 0; }) },
- { name: "鍫嗗灈鏈鸿�楁椂", type: "bar", barMaxWidth: 28, data: rows.map(function (item) { return item.craneDurationMs || 0; }) },
- { name: "鎬昏�楁椂", type: "bar", barMaxWidth: 28, data: rows.map(function (item) { return item.totalDurationMs || 0; }) }
+ { name: "绔欑偣鑰楁椂", type: "bar", barMaxWidth: 28, data: rows.map(function (item) { return self.toChartSeconds(item.stationDurationMs); }) },
+ { name: "鍫嗗灈鏈鸿�楁椂", type: "bar", barMaxWidth: 28, data: rows.map(function (item) { return self.toChartSeconds(item.craneDurationMs); }) },
+ { name: "鎬昏�楁椂", type: "bar", barMaxWidth: 28, data: rows.map(function (item) { return self.toChartSeconds(item.totalDurationMs); }) }
]
}, true);
},
@@ -325,11 +639,24 @@
if (!this.charts.trend) {
return;
}
+ var self = this;
var rows = this.analysis.trend || [];
this.charts.trend.setOption({
- tooltip: { trigger: "axis" },
+ tooltip: {
+ trigger: "axis",
+ formatter: function (params) {
+ if (!params || !params.length) {
+ return "";
+ }
+ var lines = [params[0].axisValue];
+ params.forEach(function (item) {
+ lines.push(item.marker + item.seriesName + ": " + self.formatChartSeconds(item.value));
+ });
+ return lines.join("<br>");
+ }
+ },
legend: { data: ["骞冲潎鎬昏�楁椂", "骞冲潎绔欑偣鑰楁椂", "骞冲潎鍫嗗灈鏈鸿�楁椂"] },
- grid: { left: 50, right: 20, top: 40, bottom: 70 },
+ grid: { left: 88, right: 20, top: 40, bottom: 70, containLabel: true },
xAxis: {
type: "category",
data: rows.map(function (item) { return item.bucketLabel; }),
@@ -339,14 +666,14 @@
type: "value",
axisLabel: {
formatter: function (value) {
- return Math.round((value || 0) / 1000) + "s";
+ return self.formatChartSeconds(value);
}
}
},
series: [
- { name: "骞冲潎鎬昏�楁椂", type: "line", smooth: true, data: rows.map(function (item) { return item.avgTotalDurationMs || 0; }) },
- { name: "骞冲潎绔欑偣鑰楁椂", type: "line", smooth: true, data: rows.map(function (item) { return item.avgStationDurationMs || 0; }) },
- { name: "骞冲潎鍫嗗灈鏈鸿�楁椂", type: "line", smooth: true, data: rows.map(function (item) { return item.avgCraneDurationMs || 0; }) }
+ { name: "骞冲潎鎬昏�楁椂", type: "line", smooth: true, data: rows.map(function (item) { return self.toChartSeconds(item.avgTotalDurationMs); }) },
+ { name: "骞冲潎绔欑偣鑰楁椂", type: "line", smooth: true, data: rows.map(function (item) { return self.toChartSeconds(item.avgStationDurationMs); }) },
+ { name: "骞冲潎鍫嗗灈鏈鸿�楁椂", type: "line", smooth: true, data: rows.map(function (item) { return self.toChartSeconds(item.avgCraneDurationMs); }) }
]
}, true);
},
@@ -370,19 +697,36 @@
if (!this.charts.faultDuration) {
return;
}
+ var self = this;
var rows = this.analysis.faultDuration || [];
this.charts.faultDuration.setOption({
- tooltip: { trigger: "axis" },
- grid: { left: 50, right: 20, top: 20, bottom: 40 },
+ tooltip: {
+ trigger: "axis",
+ formatter: function (params) {
+ if (!params || !params.length) {
+ return "";
+ }
+ var lines = [params[0].axisValue];
+ params.forEach(function (item) {
+ lines.push(item.marker + item.seriesName + ": " + self.formatChartSeconds(item.value));
+ });
+ return lines.join("<br>");
+ }
+ },
+ grid: { left: 88, right: 20, top: 20, bottom: 68, containLabel: true },
xAxis: {
type: "category",
- data: rows.map(function (item) { return item.name; })
+ data: rows.map(function (item) { return item.name; }),
+ axisLabel: {
+ interval: 0,
+ rotate: rows.length > 6 ? 30 : 0
+ }
},
yAxis: {
type: "value",
axisLabel: {
formatter: function (value) {
- return Math.round((value || 0) / 1000) + "s";
+ return self.formatChartSeconds(value);
}
}
},
@@ -390,7 +734,7 @@
name: "鏁呴殰鑰楁椂",
type: "bar",
barMaxWidth: 36,
- data: rows.map(function (item) { return item.value || 0; })
+ data: rows.map(function (item) { return self.toChartSeconds(item.value); })
}]
}, true);
},
@@ -448,6 +792,20 @@
}
return num.toLocaleString("zh-CN");
},
+ toChartSeconds: function (value) {
+ var num = Number(value || 0);
+ if (!isFinite(num)) {
+ return 0;
+ }
+ return Number((num / 1000).toFixed(3));
+ },
+ formatChartSeconds: function (value) {
+ var num = Number(value || 0);
+ if (!isFinite(num)) {
+ return "0s";
+ }
+ return this.formatDurationBySeconds(num);
+ },
formatDuration: function (value) {
if (value === null || value === undefined || value === "") {
return "--";
@@ -459,17 +817,40 @@
if (ms < 1000) {
return Math.round(ms) + " ms";
}
- var totalSeconds = Math.floor(ms / 1000);
- var hours = Math.floor(totalSeconds / 3600);
- var minutes = Math.floor((totalSeconds % 3600) / 60);
- var seconds = totalSeconds % 60;
+ return this.formatDurationBySeconds(ms / 1000);
+ },
+ formatDurationBySeconds: function (seconds) {
+ var totalSeconds = Number(seconds || 0);
+ if (!isFinite(totalSeconds)) {
+ return "0s";
+ }
+ var safeSeconds = Math.max(0, totalSeconds);
+ if (safeSeconds < 60) {
+ return this.trimTrailingZeros(safeSeconds) + "s";
+ }
+ var hours = Math.floor(safeSeconds / 3600);
+ var minutes = Math.floor((safeSeconds % 3600) / 60);
+ var remainSeconds = safeSeconds - hours * 3600 - minutes * 60;
+ var secondText = this.trimTrailingZeros(remainSeconds);
if (hours > 0) {
- return hours + "h " + this.pad(minutes) + "m " + this.pad(seconds) + "s";
+ return hours + "h" + this.pad(minutes) + "m" + this.padSeconds(secondText) + "s";
}
- if (minutes > 0) {
- return minutes + "m " + this.pad(seconds) + "s";
+ return minutes + "m" + this.padSeconds(secondText) + "s";
+ },
+ trimTrailingZeros: function (value) {
+ var text = String(Number(Number(value).toFixed(3)));
+ if (text.indexOf(".") >= 0) {
+ text = text.replace(/0+$/, "").replace(/\.$/, "");
}
- return seconds + "s";
+ return text;
+ },
+ padSeconds: function (value) {
+ var text = String(value);
+ if (text.indexOf(".") >= 0) {
+ var parts = text.split(".");
+ return (parts[0].length < 2 ? "0" + parts[0] : parts[0]) + "." + parts[1];
+ }
+ return text.length < 2 ? "0" + text : text;
}
}
});
--
Gitblit v1.9.1