(function () {
|
"use strict";
|
|
function nowDate() {
|
return new Date();
|
}
|
|
function startOfToday() {
|
var date = new Date();
|
date.setHours(0, 0, 0, 0);
|
return date;
|
}
|
|
function createDefaultFilters() {
|
return {
|
mode: "TASK",
|
keyword: "",
|
ioType: "",
|
finalWrkSts: "",
|
sourceStaNo: "",
|
staNo: "",
|
deviceType: "",
|
timeField: "finish_time",
|
timeRange: [startOfToday(), nowDate()]
|
};
|
}
|
|
function createEmptyAnalysis() {
|
return {
|
summary: {
|
taskCount: 0,
|
avgTotalDurationMs: null,
|
avgStationDurationMs: null,
|
avgCraneDurationMs: null,
|
faultTaskCount: 0,
|
faultDurationMs: 0,
|
partialTaskCount: 0
|
},
|
durationCompare: [],
|
trend: [],
|
faultPie: [],
|
faultDuration: [],
|
detail: []
|
};
|
}
|
|
new Vue({
|
el: "#app",
|
data: function () {
|
return {
|
options: {
|
ioTypes: [],
|
statuses: [],
|
stations: [],
|
deviceTypes: [],
|
timeFields: []
|
},
|
filters: createDefaultFilters(),
|
tableData: [],
|
currentPage: 1,
|
pageSize: 20,
|
pageTotal: 0,
|
listLoading: false,
|
analyzeLoading: false,
|
exportingPdf: false,
|
selectedWrkNoMap: {},
|
analysis: createEmptyAnalysis(),
|
analysisReady: false,
|
charts: {
|
duration: null,
|
trend: null,
|
faultPie: null,
|
faultDuration: null
|
},
|
resizeHandler: null
|
};
|
},
|
computed: {
|
selectedWrkNos: function () {
|
return Object.keys(this.selectedWrkNoMap).map(function (key) {
|
return Number(key);
|
}).filter(function (value) {
|
return !!value;
|
});
|
}
|
},
|
mounted: function () {
|
var self = this;
|
this.loadOptions();
|
this.loadList();
|
this.resizeHandler = function () {
|
self.resizeCharts();
|
};
|
window.addEventListener("resize", this.resizeHandler);
|
},
|
beforeDestroy: function () {
|
if (this.resizeHandler) {
|
window.removeEventListener("resize", this.resizeHandler);
|
}
|
this.disposeCharts();
|
},
|
methods: {
|
loadOptions: function () {
|
var self = this;
|
$.ajax({
|
url: baseUrl + "/wrkAnalysis/options/auth",
|
headers: { token: localStorage.getItem("token") },
|
method: "GET",
|
success: function (res) {
|
if (res && res.code === 200) {
|
self.options = Object.assign(self.options, res.data || {});
|
return;
|
}
|
self.$message.error((res && res.msg) || "分析选项加载失败");
|
},
|
error: function () {
|
self.$message.error("分析选项加载失败");
|
}
|
});
|
},
|
buildListParams: function () {
|
var params = {
|
curr: this.currentPage,
|
limit: this.pageSize,
|
keyword: this.filters.keyword,
|
ioType: this.filters.ioType,
|
finalWrkSts: this.filters.finalWrkSts,
|
sourceStaNo: this.filters.sourceStaNo,
|
staNo: this.filters.staNo,
|
deviceType: this.filters.deviceType
|
};
|
if (this.filters.timeRange && this.filters.timeRange.length === 2) {
|
if (this.filters.timeField === "appe_time") {
|
params.appeTimeRange = this.formatRange(this.filters.timeRange);
|
} else {
|
params.finishTimeRange = this.formatRange(this.filters.timeRange);
|
}
|
}
|
return this.cleanParams(params);
|
},
|
loadList: function () {
|
var self = this;
|
this.listLoading = true;
|
$.ajax({
|
url: baseUrl + "/wrkAnalysis/list/auth",
|
headers: { token: localStorage.getItem("token") },
|
method: "GET",
|
data: self.buildListParams(),
|
success: function (res) {
|
if (res && res.code === 200) {
|
var data = res.data || {};
|
self.tableData = data.records || [];
|
self.pageTotal = data.total || 0;
|
self.$nextTick(function () {
|
self.restoreSelection();
|
});
|
return;
|
}
|
self.$message.error((res && res.msg) || "历史任务加载失败");
|
},
|
error: function () {
|
self.$message.error("历史任务加载失败");
|
},
|
complete: function () {
|
self.listLoading = false;
|
}
|
});
|
},
|
handleSearch: function () {
|
this.currentPage = 1;
|
this.loadList();
|
},
|
handleReset: function () {
|
this.filters = createDefaultFilters();
|
this.currentPage = 1;
|
this.pageSize = 20;
|
this.selectedWrkNoMap = {};
|
this.analysis = createEmptyAnalysis();
|
this.analysisReady = false;
|
this.disposeCharts();
|
this.loadList();
|
},
|
handleSizeChange: function (size) {
|
this.pageSize = size;
|
this.currentPage = 1;
|
this.loadList();
|
},
|
handleCurrentChange: function (page) {
|
this.currentPage = page;
|
this.loadList();
|
},
|
restoreSelection: function () {
|
var table = this.$refs.historyTable;
|
var self = this;
|
if (!table) {
|
return;
|
}
|
table.clearSelection();
|
(this.tableData || []).forEach(function (row) {
|
if (self.selectedWrkNoMap[row.wrkNo]) {
|
table.toggleRowSelection(row, true);
|
}
|
});
|
},
|
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 nextMap[row.wrkNo];
|
});
|
Object.keys(selectedMap).forEach(function (key) {
|
nextMap[key] = true;
|
});
|
this.selectedWrkNoMap = nextMap;
|
},
|
runAnalysis: function () {
|
var self = this;
|
var request = {
|
mode: this.filters.mode,
|
ioType: this.filters.ioType,
|
finalWrkSts: this.filters.finalWrkSts,
|
sourceStaNo: this.filters.sourceStaNo,
|
staNo: this.filters.staNo,
|
deviceType: this.filters.deviceType
|
};
|
if (this.filters.mode === "TASK") {
|
if (!this.selectedWrkNos.length) {
|
this.$message.warning("请先勾选要分析的任务");
|
return;
|
}
|
request.wrkNos = this.selectedWrkNos;
|
request.timeField = this.filters.timeField;
|
} else {
|
if (!this.filters.timeRange || this.filters.timeRange.length !== 2) {
|
this.$message.warning("请先选择分析时间范围");
|
return;
|
}
|
request.timeField = this.filters.timeField;
|
request.startTime = this.filters.timeRange[0].getTime();
|
request.endTime = this.filters.timeRange[1].getTime();
|
}
|
this.analyzeLoading = true;
|
$.ajax({
|
url: baseUrl + "/wrkAnalysis/analyze/auth",
|
headers: {
|
token: localStorage.getItem("token"),
|
"Content-Type": "application/json"
|
},
|
method: "POST",
|
data: JSON.stringify(this.cleanParams(request)),
|
success: function (res) {
|
if (res && res.code === 200) {
|
self.analysis = Object.assign(createEmptyAnalysis(), res.data || {});
|
self.analysisReady = true;
|
self.$nextTick(function () {
|
self.updateCharts();
|
});
|
return;
|
}
|
self.$message.error((res && res.msg) || "分析失败");
|
},
|
error: function () {
|
self.$message.error("分析失败");
|
},
|
complete: function () {
|
self.analyzeLoading = false;
|
}
|
});
|
},
|
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) {
|
self.appendCanvasSlicesToPdf(pdf, results[0], {
|
margin: 8,
|
startY: 8,
|
addNewPage: false
|
});
|
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);
|
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;
|
}
|
},
|
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();
|
return;
|
}
|
this.ensureCharts();
|
this.renderDurationChart();
|
this.renderTrendChart();
|
this.renderFaultPieChart();
|
this.renderFaultDurationChart();
|
},
|
ensureCharts: function () {
|
if (this.$refs.durationChart && !this.charts.duration) {
|
this.charts.duration = echarts.init(this.$refs.durationChart);
|
}
|
if (this.$refs.trendChart && !this.charts.trend) {
|
this.charts.trend = echarts.init(this.$refs.trendChart);
|
}
|
if (this.$refs.faultPieChart && !this.charts.faultPie) {
|
this.charts.faultPie = echarts.init(this.$refs.faultPieChart);
|
}
|
if (this.$refs.faultDurationChart && !this.charts.faultDuration) {
|
this.charts.faultDuration = echarts.init(this.$refs.faultDurationChart);
|
}
|
},
|
renderDurationChart: function () {
|
if (!this.charts.duration) {
|
return;
|
}
|
var self = this;
|
var rows = this.analysis.durationCompare || [];
|
this.charts.duration.setOption({
|
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 },
|
xAxis: {
|
type: "category",
|
data: rows.map(function (item) { return String(item.wrkNo); }),
|
axisLabel: { rotate: rows.length > 8 ? 30 : 0 }
|
},
|
yAxis: {
|
type: "value",
|
axisLabel: {
|
formatter: function (value) {
|
return self.formatChartSeconds(value);
|
}
|
}
|
},
|
series: [
|
{ 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);
|
},
|
renderTrendChart: function () {
|
if (!this.charts.trend) {
|
return;
|
}
|
var self = this;
|
var rows = this.analysis.trend || [];
|
this.charts.trend.setOption({
|
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 },
|
xAxis: {
|
type: "category",
|
data: rows.map(function (item) { return item.bucketLabel; }),
|
axisLabel: { rotate: rows.length > 8 ? 25 : 0 }
|
},
|
yAxis: {
|
type: "value",
|
axisLabel: {
|
formatter: function (value) {
|
return self.formatChartSeconds(value);
|
}
|
}
|
},
|
series: [
|
{ 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);
|
},
|
renderFaultPieChart: function () {
|
if (!this.charts.faultPie) {
|
return;
|
}
|
this.charts.faultPie.setOption({
|
tooltip: { trigger: "item" },
|
legend: { bottom: 0 },
|
series: [{
|
type: "pie",
|
radius: ["42%", "68%"],
|
center: ["50%", "46%"],
|
label: { formatter: "{b}\n{d}%" },
|
data: this.analysis.faultPie || []
|
}]
|
}, true);
|
},
|
renderFaultDurationChart: function () {
|
if (!this.charts.faultDuration) {
|
return;
|
}
|
var self = this;
|
var rows = this.analysis.faultDuration || [];
|
this.charts.faultDuration.setOption({
|
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: 50, right: 20, top: 20, bottom: 40 },
|
xAxis: {
|
type: "category",
|
data: rows.map(function (item) { return item.name; })
|
},
|
yAxis: {
|
type: "value",
|
axisLabel: {
|
formatter: function (value) {
|
return self.formatChartSeconds(value);
|
}
|
}
|
},
|
series: [{
|
name: "故障耗时",
|
type: "bar",
|
barMaxWidth: 36,
|
data: rows.map(function (item) { return self.toChartSeconds(item.value); })
|
}]
|
}, true);
|
},
|
resizeCharts: function () {
|
Object.keys(this.charts).forEach(function (key) {
|
if (this.charts[key]) {
|
this.charts[key].resize();
|
}
|
}, this);
|
},
|
disposeCharts: function () {
|
Object.keys(this.charts).forEach(function (key) {
|
if (this.charts[key]) {
|
this.charts[key].dispose();
|
this.charts[key] = null;
|
}
|
}, this);
|
},
|
formatRange: function (range) {
|
return this.formatDateTime(range[0]) + " ~ " + this.formatDateTime(range[1]);
|
},
|
formatDateTime: function (date) {
|
if (!date) {
|
return "";
|
}
|
var year = date.getFullYear();
|
var month = this.pad(date.getMonth() + 1);
|
var day = this.pad(date.getDate());
|
var hour = this.pad(date.getHours());
|
var minute = this.pad(date.getMinutes());
|
var second = this.pad(date.getSeconds());
|
return year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
|
},
|
pad: function (value) {
|
return value < 10 ? "0" + value : String(value);
|
},
|
cleanParams: function (params) {
|
var result = {};
|
Object.keys(params || {}).forEach(function (key) {
|
var value = params[key];
|
if (value === "" || value === null || value === undefined) {
|
return;
|
}
|
if (Array.isArray(value) && !value.length) {
|
return;
|
}
|
result[key] = value;
|
});
|
return result;
|
},
|
formatNumber: function (value) {
|
var num = Number(value || 0);
|
if (!isFinite(num)) {
|
return "0";
|
}
|
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";
|
}
|
var text = String(num);
|
if (text.indexOf(".") >= 0) {
|
text = text.replace(/0+$/, "").replace(/\.$/, "");
|
}
|
return text + "s";
|
},
|
formatDuration: function (value) {
|
if (value === null || value === undefined || value === "") {
|
return "--";
|
}
|
var ms = Number(value);
|
if (!isFinite(ms)) {
|
return "--";
|
}
|
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;
|
if (hours > 0) {
|
return hours + "h " + this.pad(minutes) + "m " + this.pad(seconds) + "s";
|
}
|
if (minutes > 0) {
|
return minutes + "m " + this.pad(seconds) + "s";
|
}
|
return seconds + "s";
|
}
|
}
|
});
|
|
})();
|