var app = new Vue({
|
el: '#app',
|
data: {
|
dateTreeData: [],
|
defaultProps: {
|
children: 'children',
|
label: 'title'
|
},
|
defaultExpandedKeys: [],
|
typeOrder: ['Crn', 'DualCrn', 'Rgv', 'Devp'],
|
typeLabels: {
|
Crn: '堆垛机',
|
DualCrn: '双工位堆垛机',
|
Rgv: 'RGV',
|
Devp: '输送设备'
|
},
|
selectedDay: '',
|
searchDeviceNo: '',
|
activeType: '',
|
viewMode: 'picker',
|
deviceSummary: {
|
stats: {
|
totalDevices: 0,
|
totalFiles: 0,
|
typeCounts: {}
|
},
|
groups: []
|
},
|
summaryLoading: false,
|
deviceEnums: {},
|
|
selectedType: '',
|
selectedDeviceNo: '',
|
activeDeviceKey: '',
|
|
timelineMeta: {
|
type: '',
|
typeLabel: '',
|
deviceNo: '',
|
startTime: 0,
|
endTime: 0,
|
totalFiles: 0,
|
segments: []
|
},
|
timelineLoading: false,
|
logLoading: false,
|
logLoadError: '',
|
logRows: [],
|
loadedOffsets: {},
|
loadingOffsets: {},
|
selectedTimestamp: 0,
|
loadedSegmentRadius: 1,
|
playbackTickMs: 120,
|
logWindowAnchorOffset: -1,
|
|
isPlaying: false,
|
playbackSpeed: 1,
|
timer: null,
|
lastTick: 0,
|
|
jumpVisible: false,
|
jumpTime: null,
|
detailTab: 'logs',
|
rawTab: 'wcs',
|
|
downloadDialogVisible: false,
|
buildProgress: 0,
|
receiveProgress: 0,
|
downloadTimer: null
|
},
|
computed: {
|
summaryStats: function () {
|
return this.deviceSummary && this.deviceSummary.stats ? this.deviceSummary.stats : {
|
totalDevices: 0,
|
totalFiles: 0,
|
typeCounts: {}
|
};
|
},
|
deviceGroups: function () {
|
var self = this;
|
var groups = {};
|
(this.deviceSummary.groups || []).forEach(function (group) {
|
groups[group.type] = group;
|
});
|
return this.typeOrder.map(function (type) {
|
var group = groups[type] || {
|
type: type,
|
typeLabel: self.typeLabels[type] || type,
|
deviceCount: 0,
|
totalFiles: 0,
|
devices: []
|
};
|
group.devices = (group.devices || []).slice().sort(function (a, b) {
|
return self.parseDeviceNo(a.deviceNo) - self.parseDeviceNo(b.deviceNo);
|
});
|
return group;
|
});
|
},
|
recentDays: function () {
|
var days = this.flattenDayNodes(this.dateTreeData);
|
days.sort(function (a, b) {
|
return b.day.localeCompare(a.day);
|
});
|
return days.slice(0, 7);
|
},
|
activeGroup: function () {
|
var match = null;
|
(this.deviceGroups || []).forEach(function (group) {
|
if (group.type === this.activeType) {
|
match = group;
|
}
|
}, this);
|
if (match) {
|
return match;
|
}
|
for (var i = 0; i < this.deviceGroups.length; i++) {
|
if ((this.deviceGroups[i].devices || []).length > 0) {
|
return this.deviceGroups[i];
|
}
|
}
|
return this.deviceGroups[0] || null;
|
},
|
filteredDevices: function () {
|
var group = this.activeGroup;
|
var devices = group && group.devices ? group.devices.slice() : [];
|
var keyword = String(this.searchDeviceNo || '').trim();
|
if (!keyword) {
|
return devices;
|
}
|
return devices.filter(function (item) {
|
return String(item.deviceNo).indexOf(keyword) >= 0;
|
});
|
},
|
selectedDeviceSummary: function () {
|
var key = this.activeDeviceKey;
|
var found = null;
|
(this.deviceGroups || []).forEach(function (group) {
|
(group.devices || []).forEach(function (device) {
|
if (this.buildDeviceKey(device.type, device.deviceNo) === key) {
|
found = device;
|
}
|
}, this);
|
}, this);
|
return found;
|
},
|
sliderMax: function () {
|
if (!this.timelineMeta.startTime || !this.timelineMeta.endTime) {
|
return 0;
|
}
|
return Math.max(0, this.timelineMeta.endTime - this.timelineMeta.startTime);
|
},
|
sliderValue: function () {
|
if (!this.timelineMeta.startTime || !this.selectedTimestamp) {
|
return 0;
|
}
|
return Math.max(0, this.selectedTimestamp - this.timelineMeta.startTime);
|
},
|
currentTimeStr: function () {
|
if (!this.selectedTimestamp) {
|
return '--';
|
}
|
return this.formatTimestamp(this.selectedTimestamp, true);
|
},
|
selectedLogRow: function () {
|
if (!this.logRows.length) {
|
return null;
|
}
|
var idx = this.binarySearch(this.selectedTimestamp);
|
if (idx < 0) {
|
return this.logRows[0];
|
}
|
return this.logRows[idx];
|
},
|
selectedLogKey: function () {
|
return this.selectedLogRow ? this.selectedLogRow._key : '';
|
},
|
currentStatusLabel: function () {
|
if (!this.selectedLogRow) {
|
return '未定位';
|
}
|
return this.selectedLogRow._summary.statusLabel;
|
},
|
currentStatusTone: function () {
|
if (!this.selectedLogRow) {
|
return 'muted';
|
}
|
return this.selectedLogRow._summary.tone;
|
},
|
currentLogTitle: function () {
|
if (!this.selectedLogRow) {
|
return '等待选择日志';
|
}
|
return this.selectedLogRow._summary.title;
|
},
|
currentLogDetail: function () {
|
if (!this.selectedLogRow) {
|
return this.selectedDeviceSummary ? '点击一条日志或拖动时间轴后,同步查看该时刻的状态。' : '请先选择设备。';
|
}
|
return this.selectedLogRow._summary.detail + (this.selectedLogRow._summary.hint ? (' · ' + this.selectedLogRow._summary.hint) : '');
|
},
|
timelineRangeText: function () {
|
var start = this.timelineMeta.startTime || (this.selectedDeviceSummary && this.selectedDeviceSummary.firstTime) || 0;
|
var end = this.timelineMeta.endTime || (this.selectedDeviceSummary && this.selectedDeviceSummary.lastTime) || 0;
|
if (!start && !end) {
|
return '--';
|
}
|
return this.formatTimestamp(start, false) + ' ~ ' + this.formatTimestamp(end, false);
|
},
|
loadedSegmentCount: function () {
|
return this.getLoadedOffsetNumbers().length;
|
},
|
visualComponentName: function () {
|
if (this.selectedType === 'Crn') {
|
return 'watch-crn-card';
|
}
|
if (this.selectedType === 'DualCrn') {
|
return 'watch-dual-crn-card';
|
}
|
if (this.selectedType === 'Rgv') {
|
return 'watch-rgv-card';
|
}
|
if (this.selectedType === 'Devp') {
|
return 'devp-card';
|
}
|
return '';
|
},
|
visualItems: function () {
|
return this.selectedLogRow ? this.selectedLogRow._visualItems : [];
|
},
|
visualParam: function () {
|
return this.selectedLogRow ? this.selectedLogRow._visualParam : {};
|
},
|
activeRawText: function () {
|
if (!this.selectedLogRow) {
|
return '';
|
}
|
return this.rawTab === 'origin'
|
? this.getOriginDataText(this.selectedLogRow)
|
: this.getWcsDataText(this.selectedLogRow);
|
},
|
activeRawHint: function () {
|
if (!this.selectedLogRow) {
|
return '';
|
}
|
if (this.rawTab === 'origin') {
|
if (!this.selectedLogRow.originData) {
|
return '当前记录没有 originData。';
|
}
|
return this.safeParse(this.selectedLogRow.originData)
|
? 'originData 已按 JSON 格式化展示。'
|
: 'originData 不是合法 JSON,以下展示原始文本。';
|
}
|
if (!this.selectedLogRow.wcsData) {
|
return '当前记录没有 wcsData。';
|
}
|
return this.selectedLogRow._protocol
|
? 'wcsData 已按 JSON 格式化展示。'
|
: 'wcsData 不是合法 JSON,以下展示原始文本。';
|
},
|
canDownload: function () {
|
return !!(this.selectedDay && this.selectedType && this.selectedDeviceNo);
|
},
|
canPlay: function () {
|
return !!(this.selectedDeviceSummary && this.timelineMeta.startTime && this.timelineMeta.endTime && this.sliderMax > 0);
|
},
|
canLoadPreviousSegment: function () {
|
if (!this.selectedDeviceSummary || !(this.timelineMeta.totalFiles > 0)) {
|
return false;
|
}
|
var offsets = this.getLoadedOffsetNumbers();
|
if (!offsets.length) {
|
return true;
|
}
|
return offsets[0] > 0;
|
},
|
canLoadNextSegment: function () {
|
if (!this.selectedDeviceSummary || !(this.timelineMeta.totalFiles > 0)) {
|
return false;
|
}
|
var offsets = this.getLoadedOffsetNumbers();
|
if (!offsets.length) {
|
return true;
|
}
|
return offsets[offsets.length - 1] < this.timelineMeta.totalFiles - 1;
|
},
|
downloadDialogTitle: function () {
|
return this.i18n('deviceLogs.downloadDialogTitle', '文件下载中');
|
}
|
},
|
created: function () {
|
this.loadDeviceEnums();
|
this.loadDateTree();
|
},
|
mounted: function () {
|
if (window.WCS_I18N && typeof window.WCS_I18N.onReady === 'function') {
|
var self = this;
|
window.WCS_I18N.onReady(function () {
|
self.$forceUpdate();
|
});
|
}
|
},
|
beforeDestroy: function () {
|
this.pause();
|
if (this.downloadTimer) {
|
clearInterval(this.downloadTimer);
|
this.downloadTimer = null;
|
}
|
},
|
methods: {
|
i18n: function (key, fallback, params) {
|
if (window.WCS_I18N && typeof window.WCS_I18N.t === 'function') {
|
var translated = window.WCS_I18N.t(key, params);
|
if (translated && translated !== key) {
|
return translated;
|
}
|
}
|
return fallback || key;
|
},
|
emptySummary: function () {
|
var self = this;
|
return {
|
stats: {
|
totalDevices: 0,
|
totalFiles: 0,
|
typeCounts: {}
|
},
|
groups: this.typeOrder.map(function (type) {
|
return {
|
type: type,
|
typeLabel: self.typeLabels[type] || type,
|
deviceCount: 0,
|
totalFiles: 0,
|
devices: []
|
};
|
})
|
};
|
},
|
createEmptyTimeline: function () {
|
return {
|
type: '',
|
typeLabel: '',
|
deviceNo: '',
|
startTime: 0,
|
endTime: 0,
|
totalFiles: 0,
|
segments: []
|
};
|
},
|
loadDeviceEnums: function () {
|
var self = this;
|
$.ajax({
|
url: baseUrl + '/deviceLog/enums/auth',
|
headers: { token: localStorage.getItem('token') },
|
method: 'GET',
|
success: function (res) {
|
if (res && res.code === 200) {
|
self.deviceEnums = res.data || {};
|
}
|
}
|
});
|
},
|
loadDateTree: function () {
|
var self = this;
|
$.ajax({
|
url: baseUrl + '/deviceLog/dates/auth',
|
headers: { token: localStorage.getItem('token') },
|
method: 'GET',
|
success: function (res) {
|
if (res && res.code === 200) {
|
self.dateTreeData = res.data || [];
|
var latest = self.recentDays.length ? self.recentDays[0].day : '';
|
self.defaultExpandedKeys = self.resolveExpandedKeys(latest);
|
if (latest) {
|
self.selectDay(latest);
|
}
|
} else if (res && res.code === 403) {
|
top.location.href = baseUrl + '/';
|
} else {
|
self.$message.error((res && res.msg) || '加载日期失败');
|
}
|
},
|
error: function () {
|
self.$message.error('加载日期失败');
|
}
|
});
|
},
|
resolveExpandedKeys: function (day) {
|
if (!day || day.length !== 8) {
|
return [];
|
}
|
return [day.substring(0, 4), day.substring(0, 4) + '-' + day.substring(4, 6)];
|
},
|
flattenDayNodes: function (nodes) {
|
var result = [];
|
(nodes || []).forEach(function (yearNode) {
|
(yearNode.children || []).forEach(function (monthNode) {
|
(monthNode.children || []).forEach(function (dayNode) {
|
if (dayNode.day) {
|
result.push({
|
day: dayNode.day,
|
label: dayNode.day.substring(4, 6) + '-' + dayNode.day.substring(6, 8)
|
});
|
}
|
});
|
});
|
});
|
return result;
|
},
|
handleNodeClick: function (data) {
|
if (data && data.day) {
|
this.selectDay(data.day);
|
}
|
},
|
handleRecentDayClick: function (day) {
|
this.selectDay(day);
|
},
|
selectDay: function (day) {
|
if (!day) {
|
return;
|
}
|
this.selectedDay = day;
|
this.searchDeviceNo = '';
|
this.pause();
|
this.summaryLoading = true;
|
this.resetSelectionState();
|
|
var self = this;
|
$.ajax({
|
url: baseUrl + '/deviceLog/day/' + day + '/summary/auth',
|
headers: { token: localStorage.getItem('token') },
|
method: 'GET',
|
success: function (res) {
|
self.summaryLoading = false;
|
if (res && res.code === 200) {
|
self.deviceSummary = self.normalizeSummaryData(res.data);
|
self.activeType = self.pickActiveType();
|
self.resetSelectionState();
|
} else if (res && res.code === 403) {
|
top.location.href = baseUrl + '/';
|
} else {
|
self.deviceSummary = self.emptySummary();
|
self.$message.error((res && res.msg) || '加载设备摘要失败');
|
}
|
},
|
error: function () {
|
self.summaryLoading = false;
|
self.deviceSummary = self.emptySummary();
|
self.$message.error('加载设备摘要失败');
|
}
|
});
|
},
|
normalizeSummaryData: function (data) {
|
var self = this;
|
var summary = this.emptySummary();
|
if (data && data.stats) {
|
summary.stats = {
|
totalDevices: data.stats.totalDevices || 0,
|
totalFiles: data.stats.totalFiles || 0,
|
typeCounts: data.stats.typeCounts || {}
|
};
|
}
|
var groupMap = {};
|
((data && data.groups) || []).forEach(function (group) {
|
groupMap[group.type] = {
|
type: group.type,
|
typeLabel: group.typeLabel || self.typeLabels[group.type] || group.type,
|
deviceCount: group.deviceCount || 0,
|
totalFiles: group.totalFiles || 0,
|
devices: (group.devices || []).map(function (device) {
|
return {
|
type: device.type,
|
typeLabel: device.typeLabel || self.typeLabels[device.type] || device.type,
|
deviceNo: String(device.deviceNo),
|
fileCount: device.fileCount || 0,
|
firstTime: device.firstTime || 0,
|
lastTime: device.lastTime || 0
|
};
|
})
|
};
|
});
|
summary.groups = this.typeOrder.map(function (type) {
|
return groupMap[type] || {
|
type: type,
|
typeLabel: self.typeLabels[type] || type,
|
deviceCount: 0,
|
totalFiles: 0,
|
devices: []
|
};
|
});
|
return summary;
|
},
|
pickActiveType: function () {
|
var existing = this.activeType;
|
if (existing) {
|
var existingGroup = this.getGroup(existing);
|
if (existingGroup && (existingGroup.devices || []).length) {
|
return existing;
|
}
|
}
|
for (var i = 0; i < this.typeOrder.length; i++) {
|
var group = this.getGroup(this.typeOrder[i]);
|
if (group && (group.devices || []).length) {
|
return group.type;
|
}
|
}
|
return this.typeOrder[0] || '';
|
},
|
ensureDeviceSelection: function () {
|
var selected = this.selectedDeviceSummary;
|
if (selected && selected.type === this.activeType) {
|
return;
|
}
|
this.resetSelectionState();
|
},
|
selectTypeGroup: function (type) {
|
if (!type) {
|
return;
|
}
|
this.activeType = type;
|
var current = this.selectedDeviceSummary;
|
if (current && current.type === type) {
|
return;
|
}
|
this.resetSelectionState();
|
},
|
getGroup: function (type) {
|
var result = null;
|
(this.deviceGroups || []).forEach(function (group) {
|
if (group.type === type) {
|
result = group;
|
}
|
});
|
return result;
|
},
|
resetSelectionState: function () {
|
this.viewMode = 'picker';
|
this.selectedType = '';
|
this.selectedDeviceNo = '';
|
this.activeDeviceKey = '';
|
this.detailTab = 'logs';
|
this.rawTab = 'wcs';
|
this.timelineMeta = this.createEmptyTimeline();
|
this.timelineLoading = false;
|
this.logLoading = false;
|
this.logLoadError = '';
|
this.logRows = [];
|
this.loadedOffsets = {};
|
this.loadingOffsets = {};
|
this.selectedTimestamp = 0;
|
this.logWindowAnchorOffset = -1;
|
},
|
selectDevice: function (device) {
|
if (!device) {
|
return;
|
}
|
var nextKey = this.buildDeviceKey(device.type, device.deviceNo);
|
if (this.activeDeviceKey === nextKey && this.logRows.length) {
|
return;
|
}
|
this.pause();
|
this.viewMode = 'viewer';
|
this.activeType = device.type;
|
this.selectedType = device.type;
|
this.selectedDeviceNo = String(device.deviceNo);
|
this.activeDeviceKey = nextKey;
|
this.detailTab = 'raw';
|
this.rawTab = 'wcs';
|
this.timelineMeta = this.createEmptyTimeline();
|
this.logRows = [];
|
this.loadedOffsets = {};
|
this.loadingOffsets = {};
|
this.selectedTimestamp = 0;
|
this.logLoadError = '';
|
this.loadTimeline();
|
},
|
returnToSelector: function () {
|
this.pause();
|
this.resetSelectionState();
|
},
|
loadTimeline: function () {
|
if (!this.selectedDay || !this.selectedType || !this.selectedDeviceNo) {
|
return;
|
}
|
this.timelineLoading = true;
|
this.logLoadError = '';
|
var self = this;
|
$.ajax({
|
url: baseUrl + '/deviceLog/day/' + this.selectedDay + '/timeline/auth',
|
headers: { token: localStorage.getItem('token') },
|
method: 'GET',
|
data: {
|
type: this.selectedType,
|
deviceNo: this.selectedDeviceNo
|
},
|
success: function (res) {
|
self.timelineLoading = false;
|
if (res && res.code === 200) {
|
self.timelineMeta = self.normalizeTimeline(res.data);
|
if (self.timelineMeta.startTime) {
|
self.selectedTimestamp = self.timelineMeta.startTime;
|
}
|
if (self.timelineMeta.totalFiles > 0) {
|
self.loadSegmentWindow(0, {
|
initialize: true,
|
focusTimestamp: self.timelineMeta.startTime
|
});
|
}
|
} else {
|
self.timelineMeta = self.createEmptyTimeline();
|
self.logLoadError = (res && res.msg) || '读取时间轴失败';
|
self.$message.error(self.logLoadError);
|
}
|
},
|
error: function () {
|
self.timelineLoading = false;
|
self.timelineMeta = self.createEmptyTimeline();
|
self.logLoadError = '读取时间轴失败';
|
self.$message.error('读取时间轴失败');
|
}
|
});
|
},
|
normalizeTimeline: function (data) {
|
var timeline = this.createEmptyTimeline();
|
if (!data) {
|
return timeline;
|
}
|
timeline.type = data.type || this.selectedType;
|
timeline.typeLabel = data.typeLabel || this.typeLabels[timeline.type] || timeline.type;
|
timeline.deviceNo = String(data.deviceNo || this.selectedDeviceNo || '');
|
timeline.startTime = data.startTime || 0;
|
timeline.endTime = data.endTime || 0;
|
timeline.totalFiles = data.totalFiles || 0;
|
timeline.segments = (data.segments || []).map(function (segment) {
|
return {
|
offset: segment.offset,
|
startTime: segment.startTime || 0,
|
endTime: segment.endTime || 0
|
};
|
}).sort(function (a, b) {
|
return a.offset - b.offset;
|
});
|
if (!timeline.startTime && timeline.segments.length) {
|
for (var i = 0; i < timeline.segments.length; i++) {
|
if (timeline.segments[i].startTime) {
|
timeline.startTime = timeline.segments[i].startTime;
|
break;
|
}
|
}
|
}
|
if (!timeline.endTime && timeline.segments.length) {
|
for (var j = timeline.segments.length - 1; j >= 0; j--) {
|
if (timeline.segments[j].endTime) {
|
timeline.endTime = timeline.segments[j].endTime;
|
break;
|
}
|
}
|
}
|
return timeline;
|
},
|
loadSegmentWindow: function (offset, options) {
|
options = options || {};
|
if (offset == null || offset < 0 || offset >= (this.timelineMeta.totalFiles || 0)) {
|
return;
|
}
|
if (this.loadedOffsets[String(offset)] || this.loadingOffsets[String(offset)]) {
|
if (options.focusTimestamp) {
|
this.selectedTimestamp = options.focusTimestamp;
|
if (options.resetWindow !== false) {
|
this.pruneLogRowsAroundOffset(options.anchorOffset != null ? options.anchorOffset : offset);
|
}
|
if (options.scrollIntoView !== false) {
|
this.$nextTick(this.scrollCurrentRowIntoView);
|
}
|
}
|
return;
|
}
|
var self = this;
|
var batchSize = 1;
|
this.logLoading = true;
|
this.$set(this.loadingOffsets, String(offset), true);
|
$.ajax({
|
url: baseUrl + '/deviceLog/day/' + this.selectedDay + '/preview/auth',
|
headers: { token: localStorage.getItem('token') },
|
method: 'GET',
|
data: {
|
type: this.selectedType,
|
deviceNo: this.selectedDeviceNo,
|
offset: offset,
|
limit: batchSize
|
},
|
success: function (res) {
|
self.logLoading = false;
|
self.$delete(self.loadingOffsets, String(offset));
|
if (res && res.code === 200) {
|
self.markLoadedOffsets(offset, batchSize);
|
var decorated = self.decorateLogs(res.data || [], offset);
|
self.mergeLogRows(decorated);
|
if (options.resetWindow !== false) {
|
self.pruneLogRowsAroundOffset(options.anchorOffset != null ? options.anchorOffset : offset);
|
}
|
if (options.initialize && self.logRows.length) {
|
self.selectedTimestamp = self.logRows[0]._ts || self.timelineMeta.startTime;
|
} else if (options.focusTimestamp) {
|
self.selectedTimestamp = options.focusTimestamp;
|
} else if (!self.selectedTimestamp && self.logRows.length) {
|
self.selectedTimestamp = self.logRows[0]._ts;
|
}
|
if (!decorated.length && options.initialize && offset + batchSize < self.timelineMeta.totalFiles) {
|
self.loadSegmentWindow(offset + batchSize, options);
|
return;
|
}
|
if (options.scrollIntoView !== false) {
|
self.$nextTick(self.scrollCurrentRowIntoView);
|
}
|
} else {
|
self.logLoadError = (res && res.msg) || '读取日志失败';
|
self.$message.error(self.logLoadError);
|
}
|
},
|
error: function () {
|
self.logLoading = false;
|
self.$delete(self.loadingOffsets, String(offset));
|
self.logLoadError = '读取日志失败';
|
self.$message.error('读取日志失败');
|
}
|
});
|
},
|
markLoadedOffsets: function (offset, limit) {
|
var total = this.timelineMeta.totalFiles || 0;
|
for (var i = offset; i < Math.min(total, offset + limit); i++) {
|
this.$set(this.loadedOffsets, String(i), true);
|
}
|
},
|
decorateLogs: function (logs, segmentOffset) {
|
var self = this;
|
return (logs || []).map(function (logItem) {
|
return self.decorateLog(logItem, segmentOffset);
|
});
|
},
|
decorateLog: function (logItem, segmentOffset) {
|
var protocol = this.safeParse(logItem && logItem.wcsData);
|
var visualItems = this.buildVisualItems(protocol, this.selectedType);
|
return Object.assign({}, logItem, {
|
_ts: this.parseTimestamp(logItem && logItem.createTime),
|
_key: this.buildLogRowKey(logItem),
|
_segmentOffset: segmentOffset,
|
_protocol: protocol,
|
_visualItems: visualItems,
|
_visualParam: this.buildVisualParam(this.selectedType, this.selectedDeviceNo),
|
_summary: this.buildLogSummary(this.selectedType, visualItems, protocol)
|
});
|
},
|
buildLogSummary: function (type, visualItems, protocol) {
|
var fallback = {
|
statusLabel: '离线',
|
tone: 'muted',
|
title: '原始日志',
|
detail: '当前记录缺少可解析的业务字段',
|
hint: ''
|
};
|
if (!type) {
|
return fallback;
|
}
|
if (type === 'Crn') {
|
var crn = visualItems[0] || {};
|
var crnStatus = MonitorCardKit.deviceStatusLabel(crn.deviceStatus);
|
return {
|
statusLabel: crnStatus,
|
tone: MonitorCardKit.statusTone(crnStatus),
|
title: '任务 ' + MonitorCardKit.orDash(crn.workNo) + ' · 排 ' + MonitorCardKit.orDash(crn.bay) + ' · 层 ' + MonitorCardKit.orDash(crn.lev),
|
detail: '模式 ' + MonitorCardKit.orDash(crn.mode) + ' / 状态 ' + MonitorCardKit.orDash(crn.status) + ' / 货叉 ' + MonitorCardKit.orDash(crn.forkOffset),
|
hint: crn.warnCode ? ('报警代码 ' + crn.warnCode) : ('载货 ' + MonitorCardKit.orDash(crn.loading))
|
};
|
}
|
if (type === 'DualCrn') {
|
var dual = visualItems[0] || {};
|
var dualStatus = MonitorCardKit.deviceStatusLabel(dual.deviceStatus);
|
return {
|
statusLabel: dualStatus,
|
tone: MonitorCardKit.statusTone(dualStatus),
|
title: '工位1任务 ' + MonitorCardKit.orDash(dual.taskNo) + ' · 工位2任务 ' + MonitorCardKit.orDash(dual.taskNoTwo),
|
detail: '排 ' + MonitorCardKit.orDash(dual.bay) + ' / 层 ' + MonitorCardKit.orDash(dual.lev) + ' / 状态 ' + MonitorCardKit.orDash(dual.status),
|
hint: dual.warnCode ? ('报警代码 ' + dual.warnCode) : ('工位2状态 ' + MonitorCardKit.orDash(dual.statusTwo))
|
};
|
}
|
if (type === 'Rgv') {
|
var rgv = visualItems[0] || {};
|
var rgvStatus = MonitorCardKit.deviceStatusLabel(rgv.deviceStatus);
|
return {
|
statusLabel: rgvStatus,
|
tone: MonitorCardKit.statusTone(rgvStatus),
|
title: '任务 ' + MonitorCardKit.orDash(rgv.taskNo) + ' · 轨道位 ' + MonitorCardKit.orDash(rgv.trackSiteNo),
|
detail: '模式 ' + MonitorCardKit.orDash(rgv.mode) + ' / 状态 ' + MonitorCardKit.orDash(rgv.status) + ' / 载货 ' + MonitorCardKit.orDash(rgv.loading),
|
hint: rgv.warnCode ? ('报警代码 ' + rgv.warnCode) : ''
|
};
|
}
|
if (type === 'Devp') {
|
var stations = visualItems || [];
|
var autoCount = 0;
|
var taskCount = 0;
|
var loadingCount = 0;
|
var errorStations = [];
|
var canInCount = 0;
|
for (var i = 0; i < stations.length; i++) {
|
if (this.toBool(stations[i].autoing)) {
|
autoCount += 1;
|
}
|
if (stations[i].taskNo != null && stations[i].taskNo !== '' && Number(stations[i].taskNo) !== 0) {
|
taskCount += 1;
|
}
|
if (this.toBool(stations[i].loading)) {
|
loadingCount += 1;
|
}
|
if (this.toBool(stations[i].inEnable)) {
|
canInCount += 1;
|
}
|
if (stations[i].error || stations[i].errorMsg) {
|
errorStations.push(stations[i].stationId);
|
}
|
}
|
var statusLabel = errorStations.length ? '故障' : (autoCount === stations.length && stations.length ? '自动' : '手动');
|
return {
|
statusLabel: statusLabel,
|
tone: MonitorCardKit.statusTone(statusLabel),
|
title: stations.length + ' 个站点 · 任务 ' + taskCount + ' · 有物 ' + loadingCount,
|
detail: '自动 ' + autoCount + ' / 手动 ' + Math.max(0, stations.length - autoCount) + ' / 可入 ' + canInCount,
|
hint: errorStations.length ? ('异常站点 ' + errorStations.slice(0, 6).join(', ')) : ('站点数组大小 ' + stations.length)
|
};
|
}
|
return fallback;
|
},
|
buildVisualItems: function (protocol, type) {
|
if (!protocol) {
|
return [];
|
}
|
if (type === 'Devp' && Array.isArray(protocol)) {
|
var self = this;
|
return protocol.map(function (item) {
|
return self.transformData(item, type);
|
}).sort(function (a, b) {
|
return (a.stationId || 0) - (b.stationId || 0);
|
});
|
}
|
if (type !== 'Devp') {
|
return [this.transformData(protocol, type)];
|
}
|
return [];
|
},
|
buildVisualParam: function (type, deviceNo) {
|
if (type === 'Crn' || type === 'DualCrn') {
|
return { crnNo: Number(deviceNo) };
|
}
|
if (type === 'Rgv') {
|
return { rgvNo: Number(deviceNo) };
|
}
|
return {};
|
},
|
transformData: function (protocol, type) {
|
if (!protocol) {
|
return {};
|
}
|
var CrnModeType = this.deviceEnums.CrnModeType || {};
|
var CrnStatusType = this.deviceEnums.CrnStatusType || {};
|
var CrnForkPosType = this.deviceEnums.CrnForkPosType || {};
|
var CrnLiftPosType = this.deviceEnums.CrnLiftPosType || {};
|
var DualCrnForkPosType = this.deviceEnums.DualCrnForkPosType || {};
|
var DualCrnLiftPosType = this.deviceEnums.DualCrnLiftPosType || {};
|
var RgvModeType = this.deviceEnums.RgvModeType || {};
|
var RgvStatusType = this.deviceEnums.RgvStatusType || {};
|
|
if (type === 'Crn') {
|
return {
|
crnNo: protocol.crnNo,
|
workNo: protocol.taskNo || 0,
|
mode: CrnModeType[protocol.mode] || '-',
|
status: CrnStatusType[protocol.status] || '-',
|
loading: protocol.loaded == 1 ? '有物' : '无物',
|
bay: protocol.bay,
|
lev: protocol.level,
|
forkOffset: CrnForkPosType[protocol.forkPos] || '-',
|
liftPos: CrnLiftPosType[protocol.liftPos] || '-',
|
walkPos: protocol.walkPos == 1 ? '不在定位' : '在定位',
|
xspeed: protocol.xSpeed || 0,
|
yspeed: protocol.ySpeed || 0,
|
zspeed: protocol.zSpeed || 0,
|
xdistance: protocol.xDistance || 0,
|
ydistance: protocol.yDistance || 0,
|
warnCode: protocol.alarm,
|
deviceStatus: protocol.alarm && protocol.alarm > 0 ? 'ERROR' :
|
((protocol.taskNo && protocol.taskNo > 0) ? 'WORKING' :
|
(protocol.mode == 3 ? 'AUTO' : 'OFFLINE'))
|
};
|
}
|
if (type === 'DualCrn') {
|
var dual = {
|
crnNo: protocol.crnNo,
|
taskNo: protocol.taskNo || 0,
|
taskNoTwo: protocol.taskNoTwo || 0,
|
mode: CrnModeType[protocol.mode] || '-',
|
status: CrnStatusType[protocol.status] || '-',
|
statusTwo: CrnStatusType[protocol.statusTwo] || '-',
|
loading: protocol.loaded == 1 ? '有物' : '无物',
|
loadingTwo: protocol.loadedTwo == 1 ? '有物' : '无物',
|
bay: protocol.bay,
|
lev: protocol.level,
|
forkOffset: DualCrnForkPosType[protocol.forkPos] || '-',
|
forkOffsetTwo: DualCrnForkPosType[protocol.forkPosTwo] || '-',
|
liftPos: DualCrnLiftPosType[protocol.liftPos] || '-',
|
walkPos: protocol.walkPos == 0 ? '在定位' : '不在定位',
|
taskReceive: protocol.taskReceive == 1 ? '接收' : '无任务',
|
taskReceiveTwo: protocol.taskReceiveTwo == 1 ? '接收' : '无任务',
|
xspeed: protocol.xSpeed,
|
yspeed: protocol.ySpeed,
|
zspeed: protocol.zSpeed,
|
xdistance: protocol.xDistance,
|
ydistance: protocol.yDistance,
|
warnCode: protocol.alarm
|
};
|
if (protocol.alarm && protocol.alarm > 0) dual.deviceStatus = 'ERROR';
|
else if ((protocol.taskNo && protocol.taskNo > 0) || (protocol.taskNoTwo && protocol.taskNoTwo > 0)) dual.deviceStatus = 'WORKING';
|
else if (protocol.mode == 3) dual.deviceStatus = 'AUTO';
|
else dual.deviceStatus = 'OFFLINE';
|
return dual;
|
}
|
if (type === 'Rgv') {
|
var rgv = {
|
rgvNo: protocol.rgvNo,
|
taskNo: protocol.taskNo,
|
mode: RgvModeType[protocol.mode] || '',
|
status: RgvStatusType[protocol.status] || '',
|
loading: protocol.loaded == 1 ? '有物' : '无物',
|
trackSiteNo: protocol.rgvPos,
|
warnCode: protocol.alarm
|
};
|
if (protocol.alarm && protocol.alarm > 0) rgv.deviceStatus = 'ERROR';
|
else if (protocol.taskNo && protocol.taskNo > 0) rgv.deviceStatus = 'WORKING';
|
else if (protocol.mode == 3) rgv.deviceStatus = 'AUTO';
|
else rgv.deviceStatus = 'OFFLINE';
|
return rgv;
|
}
|
if (type === 'Devp') {
|
return {
|
stationId: protocol.stationId,
|
taskNo: protocol.taskNo,
|
targetStaNo: protocol.targetStaNo,
|
autoing: protocol.autoing,
|
loading: protocol.loading,
|
inEnable: protocol.inEnable,
|
outEnable: protocol.outEnable,
|
emptyMk: protocol.emptyMk,
|
fullPlt: protocol.fullPlt,
|
runBlock: protocol.runBlock,
|
enableIn: protocol.enableIn,
|
palletHeight: protocol.palletHeight,
|
barcode: protocol.barcode,
|
weight: protocol.weight,
|
error: protocol.error,
|
errorMsg: protocol.errorMsg,
|
extend: protocol.extend
|
};
|
}
|
return protocol;
|
},
|
safeParse: function (text) {
|
if (!text) {
|
return null;
|
}
|
try {
|
return JSON.parse(text);
|
} catch (e) {
|
return null;
|
}
|
},
|
mergeLogRows: function (newRows) {
|
var map = {};
|
var merged = [];
|
this.logRows.concat(newRows || []).forEach(function (row) {
|
if (!row || !row._key || map[row._key]) {
|
return;
|
}
|
map[row._key] = true;
|
merged.push(row);
|
});
|
merged.sort(function (a, b) {
|
return a._ts - b._ts;
|
});
|
this.logRows = merged;
|
},
|
pruneLogRowsAroundOffset: function (anchorOffset) {
|
if (anchorOffset == null || anchorOffset < 0) {
|
return;
|
}
|
var minOffset = Math.max(0, anchorOffset - this.loadedSegmentRadius);
|
var maxOffset = anchorOffset + this.loadedSegmentRadius;
|
var loadedKeys = Object.keys(this.loadedOffsets);
|
if (this.logWindowAnchorOffset === anchorOffset && loadedKeys.length <= (this.loadedSegmentRadius * 2 + 1)) {
|
return;
|
}
|
var nextLoaded = {};
|
loadedKeys.forEach(function (key) {
|
var offset = Number(key);
|
if (offset >= minOffset && offset <= maxOffset) {
|
nextLoaded[key] = true;
|
}
|
});
|
this.loadedOffsets = nextLoaded;
|
this.logRows = (this.logRows || []).filter(function (row) {
|
return row && row._segmentOffset >= minOffset && row._segmentOffset <= maxOffset;
|
});
|
this.logWindowAnchorOffset = anchorOffset;
|
},
|
buildLogRowKey: function (logItem) {
|
return [
|
this.selectedType,
|
this.selectedDeviceNo,
|
logItem && logItem.createTime ? logItem.createTime : '',
|
this.hashString(logItem && logItem.originData ? logItem.originData : ''),
|
this.hashString(logItem && logItem.wcsData ? logItem.wcsData : '')
|
].join('|');
|
},
|
hashString: function (text) {
|
var str = String(text || '');
|
var hash = 0;
|
for (var i = 0; i < str.length; i++) {
|
hash = ((hash << 5) - hash) + str.charCodeAt(i);
|
hash |= 0;
|
}
|
return hash;
|
},
|
parseTimestamp: function (value) {
|
var ts = new Date(value).getTime();
|
return isNaN(ts) ? 0 : ts;
|
},
|
binarySearch: function (time) {
|
var list = this.logRows;
|
var left = 0;
|
var right = list.length - 1;
|
var answer = -1;
|
while (left <= right) {
|
var mid = Math.floor((left + right) / 2);
|
if ((list[mid]._ts || 0) <= time) {
|
answer = mid;
|
left = mid + 1;
|
} else {
|
right = mid - 1;
|
}
|
}
|
return answer;
|
},
|
handleLogRowClick: function (row) {
|
if (!row) {
|
return;
|
}
|
this.pause();
|
this.selectedTimestamp = row._ts;
|
if (row._segmentOffset != null) {
|
this.pruneLogRowsAroundOffset(row._segmentOffset);
|
}
|
this.$nextTick(this.scrollCurrentRowIntoView);
|
},
|
buildLogMetaLine: function (summary) {
|
if (!summary) {
|
return '';
|
}
|
return summary.detail + (summary.hint ? (' · ' + summary.hint) : '');
|
},
|
getWcsDataText: function (row) {
|
if (!row || !row.wcsData) {
|
return '当前记录没有 wcsData。';
|
}
|
if (row._protocol) {
|
return this.prettyPrintJson(row._protocol);
|
}
|
return String(row.wcsData);
|
},
|
getOriginDataText: function (row) {
|
if (!row || !row.originData) {
|
return '当前记录没有 originData。';
|
}
|
var parsed = this.safeParse(row.originData);
|
return parsed ? this.prettyPrintJson(parsed) : String(row.originData);
|
},
|
prettyPrintJson: function (value) {
|
if (value == null || value === '') {
|
return '';
|
}
|
try {
|
if (typeof value === 'string') {
|
return JSON.stringify(JSON.parse(value), null, 2);
|
}
|
return JSON.stringify(value, null, 2);
|
} catch (e) {
|
return String(value);
|
}
|
},
|
handleSliderInput: function (value) {
|
if (!this.selectedDeviceSummary) {
|
return;
|
}
|
var next = (this.timelineMeta.startTime || 0) + value;
|
this.selectedTimestamp = next;
|
},
|
handleSliderChange: function (value) {
|
if (!this.selectedDeviceSummary) {
|
return;
|
}
|
var next = (this.timelineMeta.startTime || 0) + value;
|
this.seekToTimestamp(next, { scrollIntoView: true });
|
},
|
ensureTimestampLoaded: function (timestamp) {
|
if (!timestamp || !this.timelineMeta.segments.length) {
|
return;
|
}
|
var segment = this.findSegmentByTime(timestamp);
|
if (!segment) {
|
return;
|
}
|
if (this.loadedOffsets[String(segment.offset)]) {
|
this.pruneLogRowsAroundOffset(segment.offset);
|
}
|
if (!this.loadedOffsets[String(segment.offset)] && !this.loadingOffsets[String(segment.offset)]) {
|
this.loadSegmentWindow(segment.offset, {
|
focusTimestamp: timestamp,
|
anchorOffset: segment.offset,
|
resetWindow: true,
|
scrollIntoView: false
|
});
|
return;
|
}
|
var nextOffset = segment.offset + 1;
|
if (nextOffset < this.timelineMeta.totalFiles && !this.loadedOffsets[String(nextOffset)] && !this.loadingOffsets[String(nextOffset)]) {
|
var segmentEnd = segment.endTime || this.timelineMeta.endTime;
|
if (segmentEnd && timestamp >= segmentEnd - Math.max(2000, this.playbackSpeed * 500)) {
|
this.loadSegmentWindow(nextOffset, {
|
anchorOffset: segment.offset,
|
resetWindow: true,
|
scrollIntoView: false
|
});
|
}
|
}
|
},
|
findSegmentByTime: function (timestamp) {
|
var segments = this.timelineMeta.segments || [];
|
if (!segments.length) {
|
return null;
|
}
|
var fallback = segments[0];
|
for (var i = 0; i < segments.length; i++) {
|
var segment = segments[i];
|
var start = segment.startTime || (i === 0 ? this.timelineMeta.startTime : segments[i - 1].endTime);
|
var end = segment.endTime || (i === segments.length - 1 ? this.timelineMeta.endTime : segments[i + 1].startTime);
|
if (start && timestamp < start) {
|
return fallback;
|
}
|
fallback = segment;
|
if ((!start || timestamp >= start) && (!end || timestamp <= end)) {
|
return segment;
|
}
|
}
|
return fallback;
|
},
|
getLoadedOffsetNumbers: function () {
|
var self = this;
|
return Object.keys(this.loadedOffsets)
|
.filter(function (key) { return !!self.loadedOffsets[key]; })
|
.map(function (key) { return Number(key); })
|
.sort(function (a, b) { return a - b; });
|
},
|
loadPreviousSegment: function () {
|
if (!this.canLoadPreviousSegment) {
|
return;
|
}
|
var offsets = this.getLoadedOffsetNumbers();
|
if (!offsets.length) {
|
this.loadSegmentWindow(0);
|
return;
|
}
|
this.loadSegmentWindow(offsets[0] - 1);
|
},
|
loadNextSegment: function () {
|
if (!this.canLoadNextSegment) {
|
return;
|
}
|
var offsets = this.getLoadedOffsetNumbers();
|
if (!offsets.length) {
|
this.loadSegmentWindow(0);
|
return;
|
}
|
this.loadSegmentWindow(offsets[offsets.length - 1] + 1);
|
},
|
play: function () {
|
if (!this.canPlay) {
|
return;
|
}
|
if (!this.selectedTimestamp) {
|
this.selectedTimestamp = this.timelineMeta.startTime;
|
}
|
this.isPlaying = true;
|
this.lastTick = Date.now();
|
this.tick();
|
},
|
tick: function () {
|
if (!this.isPlaying) {
|
return;
|
}
|
var now = Date.now();
|
var delta = Math.max(0, now - this.lastTick);
|
this.lastTick = now;
|
var endTime = this.timelineMeta.endTime || this.selectedTimestamp;
|
if (!endTime) {
|
this.pause();
|
return;
|
}
|
var next = this.selectedTimestamp + delta * this.playbackSpeed;
|
if (next >= endTime) {
|
this.selectedTimestamp = endTime;
|
this.ensureTimestampLoaded(endTime);
|
this.pause();
|
this.$nextTick(this.scrollCurrentRowIntoView);
|
return;
|
}
|
this.selectedTimestamp = next;
|
this.ensureTimestampLoaded(next);
|
var self = this;
|
this.timer = setTimeout(function () {
|
self.tick();
|
}, this.playbackTickMs);
|
},
|
pause: function () {
|
this.isPlaying = false;
|
if (this.timer) {
|
clearTimeout(this.timer);
|
cancelAnimationFrame(this.timer);
|
this.timer = null;
|
}
|
},
|
resetPlayback: function () {
|
this.pause();
|
if (this.timelineMeta.startTime) {
|
this.selectedTimestamp = this.timelineMeta.startTime;
|
this.ensureTimestampLoaded(this.selectedTimestamp);
|
this.$nextTick(this.scrollCurrentRowIntoView);
|
}
|
},
|
initJumpTime: function () {
|
if (this.selectedTimestamp) {
|
this.jumpTime = new Date(this.selectedTimestamp);
|
return;
|
}
|
if (this.selectedDay && this.selectedDay.length === 8) {
|
this.jumpTime = new Date(this.selectedDay.substring(0, 4) + '/' + this.selectedDay.substring(4, 6) + '/' + this.selectedDay.substring(6, 8) + ' 00:00:00');
|
return;
|
}
|
this.jumpTime = new Date();
|
},
|
confirmJump: function () {
|
if (!this.jumpTime || !this.selectedDay || !this.timelineMeta.startTime) {
|
return;
|
}
|
this.pause();
|
var baseDate = new Date(this.selectedDay.substring(0, 4) + '/' + this.selectedDay.substring(4, 6) + '/' + this.selectedDay.substring(6, 8) + ' 00:00:00');
|
var target = new Date(this.jumpTime);
|
baseDate.setHours(target.getHours());
|
baseDate.setMinutes(target.getMinutes());
|
baseDate.setSeconds(target.getSeconds());
|
baseDate.setMilliseconds(0);
|
var ts = baseDate.getTime();
|
if (this.timelineMeta.endTime && ts > this.timelineMeta.endTime) {
|
ts = this.timelineMeta.endTime;
|
this.$message.warning('目标时间超出日志范围,已跳转到结束时间');
|
}
|
if (ts < this.timelineMeta.startTime) {
|
ts = this.timelineMeta.startTime;
|
this.$message.warning('目标时间早于日志起点,已跳转到起始时间');
|
}
|
this.jumpVisible = false;
|
this.seekToTimestamp(ts, { scrollIntoView: true });
|
},
|
seekToTimestamp: function (timestamp, options) {
|
options = options || {};
|
if (!timestamp || !this.selectedDay || !this.selectedType || !this.selectedDeviceNo) {
|
return;
|
}
|
var self = this;
|
this.selectedTimestamp = timestamp;
|
$.ajax({
|
url: baseUrl + '/deviceLog/day/' + this.selectedDay + '/seek/auth',
|
headers: { token: localStorage.getItem('token') },
|
method: 'GET',
|
data: {
|
type: this.selectedType,
|
deviceNo: this.selectedDeviceNo,
|
timestamp: timestamp
|
},
|
success: function (res) {
|
if (res && res.code === 200 && res.data && res.data.offset != null) {
|
self.loadSegmentWindow(Number(res.data.offset), {
|
focusTimestamp: timestamp,
|
anchorOffset: Number(res.data.offset),
|
resetWindow: true,
|
scrollIntoView: options.scrollIntoView !== false
|
});
|
return;
|
}
|
self.ensureTimestampLoaded(timestamp);
|
if (options.scrollIntoView !== false) {
|
self.$nextTick(self.scrollCurrentRowIntoView);
|
}
|
},
|
error: function () {
|
self.ensureTimestampLoaded(timestamp);
|
if (options.scrollIntoView !== false) {
|
self.$nextTick(self.scrollCurrentRowIntoView);
|
}
|
}
|
});
|
},
|
scrollCurrentRowIntoView: function () {
|
if (!this.selectedLogKey) {
|
return;
|
}
|
var el = document.getElementById('log-row-' + this.selectedLogKey);
|
if (el && typeof el.scrollIntoView === 'function') {
|
el.scrollIntoView({ block: 'nearest', behavior: 'auto' });
|
}
|
},
|
formatTooltip: function (value) {
|
if (!this.timelineMeta.startTime) {
|
return '--';
|
}
|
return this.formatTimestamp(this.timelineMeta.startTime + value, true);
|
},
|
handleCurrentDeviceDownload: function () {
|
this.doDownload(this.selectedDay, this.selectedType, this.selectedDeviceNo);
|
},
|
doDownload: function (day, type, deviceNo) {
|
if (!day || !type || !deviceNo) {
|
return;
|
}
|
var self = this;
|
$.ajax({
|
url: baseUrl + '/deviceLog/download/init/auth',
|
headers: { token: localStorage.getItem('token') },
|
method: 'POST',
|
data: JSON.stringify({
|
day: day,
|
type: type,
|
deviceNo: deviceNo
|
}),
|
dataType: 'json',
|
contentType: 'application/json;charset=UTF-8',
|
success: function (res) {
|
if (!res || res.code !== 200) {
|
self.$message.error((res && res.msg) || '初始化失败');
|
return;
|
}
|
var pid = res.data.progressId;
|
self.startDownloadProgress(pid);
|
self.performDownloadRequest(day, type, deviceNo, pid);
|
},
|
error: function () {
|
self.$message.error('初始化失败');
|
}
|
});
|
},
|
startDownloadProgress: function (pid) {
|
if (this.downloadTimer) {
|
clearInterval(this.downloadTimer);
|
this.downloadTimer = null;
|
}
|
this.downloadDialogVisible = true;
|
this.buildProgress = 0;
|
this.receiveProgress = 0;
|
var self = this;
|
this.downloadTimer = setInterval(function () {
|
$.ajax({
|
url: baseUrl + '/deviceLog/download/progress/auth',
|
headers: { token: localStorage.getItem('token') },
|
method: 'GET',
|
data: { id: pid },
|
success: function (res) {
|
if (res && res.code === 200) {
|
self.buildProgress = res.data.percent || 0;
|
}
|
}
|
});
|
}, 500);
|
},
|
performDownloadRequest: function (day, type, deviceNo, pid) {
|
var self = this;
|
$.ajax({
|
url: baseUrl + '/deviceLog/day/' + day + '/download/auth?type=' + encodeURIComponent(type) + '&deviceNo=' + encodeURIComponent(deviceNo) + '&progressId=' + encodeURIComponent(pid),
|
headers: { token: localStorage.getItem('token') },
|
method: 'GET',
|
xhrFields: { responseType: 'blob' },
|
xhr: function () {
|
var xhr = new window.XMLHttpRequest();
|
xhr.onprogress = function (e) {
|
if (e.lengthComputable && e.total > 0) {
|
self.receiveProgress = Math.floor(e.loaded / e.total * 100);
|
}
|
};
|
return xhr;
|
},
|
success: function (data, status, xhr) {
|
var disposition = xhr.getResponseHeader('Content-Disposition') || '';
|
var filename = type + '_' + deviceNo + '_' + day + '.zip';
|
var match = /filename=(.+)/.exec(disposition);
|
if (match && match[1]) {
|
filename = decodeURIComponent(match[1]);
|
}
|
self.buildProgress = 100;
|
self.receiveProgress = 100;
|
var blob = new Blob([data], { type: 'application/zip' });
|
var link = document.createElement('a');
|
var url = window.URL.createObjectURL(blob);
|
link.href = url;
|
link.download = filename;
|
document.body.appendChild(link);
|
link.click();
|
document.body.removeChild(link);
|
window.URL.revokeObjectURL(url);
|
if (self.downloadTimer) {
|
clearInterval(self.downloadTimer);
|
self.downloadTimer = null;
|
}
|
setTimeout(function () {
|
self.downloadDialogVisible = false;
|
}, 900);
|
},
|
error: function () {
|
if (self.downloadTimer) {
|
clearInterval(self.downloadTimer);
|
self.downloadTimer = null;
|
}
|
self.downloadDialogVisible = false;
|
self.$message.error('下载失败或未找到日志');
|
}
|
});
|
},
|
buildDeviceKey: function (type, deviceNo) {
|
return String(type || '') + ':' + String(deviceNo || '');
|
},
|
parseDeviceNo: function (deviceNo) {
|
var n = parseInt(deviceNo, 10);
|
return isNaN(n) ? Number.MAX_SAFE_INTEGER : n;
|
},
|
formatTimestamp: function (timestamp, withMillis) {
|
if (!timestamp) {
|
return '--';
|
}
|
var d = new Date(timestamp);
|
if (isNaN(d.getTime())) {
|
return '--';
|
}
|
var Y = d.getFullYear();
|
var M = String(d.getMonth() + 1).padStart(2, '0');
|
var D = String(d.getDate()).padStart(2, '0');
|
var h = String(d.getHours()).padStart(2, '0');
|
var m = String(d.getMinutes()).padStart(2, '0');
|
var s = String(d.getSeconds()).padStart(2, '0');
|
if (withMillis) {
|
return Y + '-' + M + '-' + D + ' ' + h + ':' + m + ':' + s + '.' + String(d.getMilliseconds()).padStart(3, '0');
|
}
|
return Y + '-' + M + '-' + D + ' ' + h + ':' + m + ':' + s;
|
},
|
formatDayText: function (day) {
|
if (!day || day.length !== 8) {
|
return '--';
|
}
|
return day.substring(0, 4) + '-' + day.substring(4, 6) + '-' + day.substring(6, 8);
|
},
|
toBool: function (value) {
|
return value === true || value === 'Y' || value === 'y' || value === 1 || value === '1';
|
}
|
}
|
});
|