lsh
2026-04-21 720e0926fa1c94b952c26e111206c5d6e1ed5ba2
src/main/webapp/static/js/deviceLogs/deviceLogs.js
@@ -16,6 +16,7 @@
        },
        selectedDay: '',
        searchDeviceNo: '',
        searchStationId: '',
        activeType: '',
        viewMode: 'picker',
        deviceSummary: {
@@ -31,6 +32,9 @@
        selectedType: '',
        selectedDeviceNo: '',
        selectedStationId: '',
        selectedStationLabel: '',
        visualFocusStationId: '',
        activeDeviceKey: '',
        timelineMeta: {
@@ -62,6 +66,10 @@
        jumpTime: null,
        detailTab: 'logs',
        rawTab: 'wcs',
        systemLogType: 'info',
        systemLogRange: [],
        systemLogDialogVisible: false,
        downloadDialogVisible: false,
        buildProgress: 0,
@@ -123,12 +131,38 @@
        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;
            var deviceKeyword = String(this.searchDeviceNo || '').trim();
            var stationKeyword = String(this.searchStationId || '').trim();
            return devices.map(function (item) {
                var matchedStationIds = [];
                var matchesDeviceNo = !deviceKeyword || String(item.deviceNo).indexOf(deviceKeyword) >= 0;
                var matchesStation = true;
                if (stationKeyword) {
                    matchesStation = false;
                    if (item.type === 'Devp') {
                        if (item.stationId && String(item.stationId).indexOf(stationKeyword) >= 0) {
                            matchesStation = true;
                            matchedStationIds = [String(item.stationId)];
                        } else {
                            var stationIds = Array.isArray(item.stationIds) ? item.stationIds : [];
                            for (var i = 0; i < stationIds.length; i++) {
                                if (String(stationIds[i]).indexOf(stationKeyword) >= 0) {
                                    matchesStation = true;
                                    matchedStationIds.push(String(stationIds[i]));
                                }
                            }
                        }
                    }
                }
                if (!matchesDeviceNo || !matchesStation) {
                    return null;
                }
                return Object.assign({}, item, {
                    matchedStationIds: matchedStationIds,
                    matchedStationId: matchedStationIds.length === 1 ? matchedStationIds[0] : ''
                });
            }).filter(function (item) {
                return !!item;
            });
        },
        selectedDeviceSummary: function () {
@@ -136,7 +170,7 @@
            var found = null;
            (this.deviceGroups || []).forEach(function (group) {
                (group.devices || []).forEach(function (device) {
                    if (this.buildDeviceKey(device.type, device.deviceNo) === key) {
                    if (this.buildDeviceKey(device.type, device.deviceNo, device.stationId) === key) {
                        found = device;
                    }
                }, this);
@@ -228,6 +262,9 @@
            return this.selectedLogRow ? this.selectedLogRow._visualItems : [];
        },
        visualParam: function () {
            if (this.selectedType === 'Devp') {
                return this.buildVisualParam(this.selectedType, this.selectedDeviceNo, this.resolveVisualStationId(this.selectedLogRow));
            }
            return this.selectedLogRow ? this.selectedLogRow._visualParam : {};
        },
        activeRawText: function () {
@@ -259,6 +296,9 @@
        },
        canDownload: function () {
            return !!(this.selectedDay && this.selectedType && this.selectedDeviceNo);
        },
        canDownloadSystemLog: function () {
            return !!(this.selectedDay && this.systemLogType && this.systemLogRange && this.systemLogRange.length === 2 && this.systemLogRange[0] && this.systemLogRange[1]);
        },
        canPlay: function () {
            return !!(this.selectedDeviceSummary && this.timelineMeta.startTime && this.timelineMeta.endTime && this.sliderMax > 0);
@@ -420,6 +460,7 @@
            }
            this.selectedDay = day;
            this.searchDeviceNo = '';
            this.searchStationId = '';
            this.pause();
            this.summaryLoading = true;
            this.resetSelectionState();
@@ -471,6 +512,10 @@
                            type: device.type,
                            typeLabel: device.typeLabel || self.typeLabels[device.type] || device.type,
                            deviceNo: String(device.deviceNo),
                            stationId: device.stationId == null ? '' : String(device.stationId),
                            stationIds: (device.stationIds || []).map(function (stationId) { return String(stationId); }),
                            matchedStationIds: [],
                            matchedStationId: '',
                            fileCount: device.fileCount || 0,
                            firstTime: device.firstTime || 0,
                            lastTime: device.lastTime || 0
@@ -536,6 +581,9 @@
            this.viewMode = 'picker';
            this.selectedType = '';
            this.selectedDeviceNo = '';
            this.selectedStationId = '';
            this.selectedStationLabel = '';
            this.visualFocusStationId = '';
            this.activeDeviceKey = '';
            this.detailTab = 'logs';
            this.rawTab = 'wcs';
@@ -553,7 +601,7 @@
            if (!device) {
                return;
            }
            var nextKey = this.buildDeviceKey(device.type, device.deviceNo);
            var nextKey = this.buildDeviceKey(device.type, device.deviceNo, device.stationId);
            if (this.activeDeviceKey === nextKey && this.logRows.length) {
                return;
            }
@@ -562,6 +610,11 @@
            this.activeType = device.type;
            this.selectedType = device.type;
            this.selectedDeviceNo = String(device.deviceNo);
            this.selectedStationId = device.stationId == null ? '' : String(device.stationId);
            this.selectedStationLabel = device.stationId == null || String(device.stationId) === ''
                ? (device.matchedStationId == null ? '' : String(device.matchedStationId))
                : String(device.stationId);
            this.visualFocusStationId = this.selectedStationLabel;
            this.activeDeviceKey = nextKey;
            this.detailTab = 'raw';
            this.rawTab = 'wcs';
@@ -571,7 +624,29 @@
            this.loadingOffsets = {};
            this.selectedTimestamp = 0;
            this.logLoadError = '';
            if (this.selectedType === 'Devp' && !this.selectedStationId) {
                return;
            }
            this.loadTimeline();
        },
        resolveVisualStationId: function (logItem) {
            if (this.selectedType !== 'Devp') {
                return '';
            }
            if (logItem && logItem.stationId != null && String(logItem.stationId) !== '') {
                return String(logItem.stationId);
            }
            if (this.visualFocusStationId) {
                return String(this.visualFocusStationId);
            }
            var selected = this.selectedDeviceSummary;
            if (selected && selected.stationId) {
                return String(selected.stationId);
            }
            if (selected && selected.matchedStationId) {
                return String(selected.matchedStationId);
            }
            return '';
        },
        returnToSelector: function () {
            this.pause();
@@ -590,7 +665,8 @@
                method: 'GET',
                data: {
                    type: this.selectedType,
                    deviceNo: this.selectedDeviceNo
                    deviceNo: this.selectedDeviceNo,
                    stationId: this.selectedStationId || undefined
                },
                success: function (res) {
                    self.timelineLoading = false;
@@ -627,6 +703,7 @@
            timeline.type = data.type || this.selectedType;
            timeline.typeLabel = data.typeLabel || this.typeLabels[timeline.type] || timeline.type;
            timeline.deviceNo = String(data.deviceNo || this.selectedDeviceNo || '');
            timeline.stationId = data.stationId == null ? this.selectedStationId : String(data.stationId || '');
            timeline.startTime = data.startTime || 0;
            timeline.endTime = data.endTime || 0;
            timeline.totalFiles = data.totalFiles || 0;
@@ -685,6 +762,7 @@
                data: {
                    type: this.selectedType,
                    deviceNo: this.selectedDeviceNo,
                    stationId: this.selectedStationId || undefined,
                    offset: offset,
                    limit: batchSize
                },
@@ -741,12 +819,13 @@
            var protocol = this.safeParse(logItem && logItem.wcsData);
            var visualItems = this.buildVisualItems(protocol, this.selectedType);
            return Object.assign({}, logItem, {
                stationId: logItem && logItem.stationId == null ? this.selectedStationId : logItem.stationId,
                _ts: this.parseTimestamp(logItem && logItem.createTime),
                _key: this.buildLogRowKey(logItem),
                _segmentOffset: segmentOffset,
                _protocol: protocol,
                _visualItems: visualItems,
                _visualParam: this.buildVisualParam(this.selectedType, this.selectedDeviceNo),
                _visualParam: this.buildVisualParam(this.selectedType, this.selectedDeviceNo, this.resolveVisualStationId(logItem)),
                _summary: this.buildLogSummary(this.selectedType, visualItems, protocol)
            });
        },
@@ -795,36 +874,17 @@
                };
            }
            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 ? '自动' : '手动');
                var station = visualItems[0] || this.transformData(protocol, type) || {};
                var hasError = !!(station.error || station.errorMsg);
                var autoing = this.toBool(station.autoing);
                var loading = this.toBool(station.loading);
                var statusLabel = hasError ? '故障' : (autoing ? '自动' : '手动');
                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)
                    title: '站点 ' + MonitorCardKit.orDash(station.stationId) + ' · 任务 ' + MonitorCardKit.orDash(station.taskNo),
                    detail: '目标站点 ' + MonitorCardKit.orDash(station.targetStaNo) + ' / 载货 ' + (loading ? '有物' : '无物') + ' / 托盘高度 ' + MonitorCardKit.orDash(station.palletHeight) + ' / 条码 ' + MonitorCardKit.orDash(station.barcode),
                    hint: hasError ? (MonitorCardKit.orDash(station.error) + (station.errorMsg ? (' · ' + station.errorMsg) : '')) : ('自动 ' + (autoing ? '是' : '否') + ' / 允许入库 ' + (this.toBool(station.inEnable) ? '是' : '否'))
                };
            }
            return fallback;
@@ -833,27 +893,38 @@
            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)];
            }
            if (type !== 'Devp') {
                return [this.transformData(protocol, type)];
            }
            return [];
        },
        buildVisualParam: function (type, deviceNo) {
        buildVisualParam: function (type, deviceNo, stationId) {
            if (type === 'Crn' || type === 'DualCrn') {
                return { crnNo: Number(deviceNo) };
            }
            if (type === 'Rgv') {
                return { rgvNo: Number(deviceNo) };
            }
            if (type === 'Devp' && stationId) {
                return { stationId: Number(stationId) };
            }
            return {};
        },
        normalizeVisualValue: function (value) {
            if (value == null || value === '') {
                return value;
            }
            if (Array.isArray(value) || Object.prototype.toString.call(value) === '[object Object]') {
                try {
                    return JSON.stringify(value);
                } catch (e) {
                    return String(value);
                }
            }
            return value;
        },
        transformData: function (protocol, type) {
            if (!protocol) {
@@ -945,18 +1016,23 @@
                    targetStaNo: protocol.targetStaNo,
                    autoing: protocol.autoing,
                    loading: protocol.loading,
                    ioMode: protocol.ioMode,
                    inEnable: protocol.inEnable,
                    outEnable: protocol.outEnable,
                    emptyMk: protocol.emptyMk,
                    fullPlt: protocol.fullPlt,
                    runBlock: protocol.runBlock,
                    enableIn: protocol.enableIn,
                    inBarcodeError: protocol.inBarcodeError,
                    palletHeight: protocol.palletHeight,
                    barcode: protocol.barcode,
                    weight: protocol.weight,
                    taskWriteIdx: protocol.taskWriteIdx,
                    taskBufferItems: Array.isArray(protocol.taskBufferItems) ? protocol.taskBufferItems : [],
                    error: protocol.error,
                    errorMsg: protocol.errorMsg,
                    extend: protocol.extend
                    systemWarning: this.normalizeVisualValue(protocol.systemWarning),
                    extend: this.normalizeVisualValue(protocol.extend)
                };
            }
            return protocol;
@@ -1013,6 +1089,7 @@
            return [
                this.selectedType,
                this.selectedDeviceNo,
                this.selectedStationId,
                logItem && logItem.createTime ? logItem.createTime : '',
                this.hashString(logItem && logItem.originData ? logItem.originData : ''),
                this.hashString(logItem && logItem.wcsData ? logItem.wcsData : '')
@@ -1290,6 +1367,7 @@
                data: {
                    type: this.selectedType,
                    deviceNo: this.selectedDeviceNo,
                    stationId: this.selectedStationId || undefined,
                    timestamp: timestamp
                },
                success: function (res) {
@@ -1331,9 +1409,39 @@
            return this.formatTimestamp(this.timelineMeta.startTime + value, true);
        },
        handleCurrentDeviceDownload: function () {
            this.doDownload(this.selectedDay, this.selectedType, this.selectedDeviceNo);
            this.doDownload(this.selectedDay, this.selectedType, this.selectedDeviceNo, this.selectedStationId);
        },
        doDownload: function (day, type, deviceNo) {
        openSystemLogDialog: function () {
            if (!this.selectedDay) {
                this.$message.warning('请先选择日志日期');
                return;
            }
            if (!this.systemLogRange || this.systemLogRange.length !== 2) {
                var start = this.formatDayText(this.selectedDay) + ' 00:00:00';
                var end = this.formatDayText(this.selectedDay) + ' 23:59:59';
                this.systemLogRange = [start, end];
            }
            this.systemLogDialogVisible = true;
        },
        handleSystemLogDownload: function () {
            if (!this.canDownloadSystemLog) {
                this.$message.warning('请选择日志类型和时间范围');
                return;
            }
            var startTime = this.systemLogRange[0];
            var endTime = this.systemLogRange[1];
            if (!startTime || !endTime) {
                this.$message.warning('请选择完整时间范围');
                return;
            }
            if (new Date(startTime).getTime() > new Date(endTime).getTime()) {
                this.$message.warning('开始时间不能晚于结束时间');
                return;
            }
            this.systemLogDialogVisible = false;
            this.doSystemLogDownload(this.systemLogType, startTime, endTime);
        },
        doDownload: function (day, type, deviceNo, stationId) {
            if (!day || !type || !deviceNo) {
                return;
            }
@@ -1345,7 +1453,8 @@
                data: JSON.stringify({
                    day: day,
                    type: type,
                    deviceNo: deviceNo
                    deviceNo: deviceNo,
                    stationId: stationId || undefined
                }),
                dataType: 'json',
                contentType: 'application/json;charset=UTF-8',
@@ -1356,7 +1465,34 @@
                    }
                    var pid = res.data.progressId;
                    self.startDownloadProgress(pid);
                    self.performDownloadRequest(day, type, deviceNo, pid);
                    self.performDownloadRequest(day, type, deviceNo, stationId, pid);
                },
                error: function () {
                    self.$message.error('初始化失败');
                }
            });
        },
        doSystemLogDownload: function (logType, startTime, endTime) {
            var self = this;
            $.ajax({
                url: baseUrl + '/deviceLog/system/download/init/auth',
                headers: { token: localStorage.getItem('token') },
                method: 'POST',
                data: JSON.stringify({
                    logType: logType,
                    startTime: startTime,
                    endTime: endTime
                }),
                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.performSystemLogDownloadRequest(logType, startTime, endTime, pid);
                },
                error: function () {
                    self.$message.error('初始化失败');
@@ -1386,10 +1522,26 @@
                });
            }, 500);
        },
        performDownloadRequest: function (day, type, deviceNo, pid) {
        performDownloadRequest: function (day, type, deviceNo, stationId, pid) {
            var self = this;
            var query = '?type=' + encodeURIComponent(type) + '&deviceNo=' + encodeURIComponent(deviceNo);
            if (stationId) {
                query += '&stationId=' + encodeURIComponent(stationId);
            }
            query += '&progressId=' + encodeURIComponent(pid);
            this.performBlobDownload(baseUrl + '/deviceLog/day/' + day + '/download/auth' + query, type + '_' + deviceNo + '_' + day + '.zip', 'application/zip');
        },
        performSystemLogDownloadRequest: function (logType, startTime, endTime, pid) {
            var query = '?logType=' + encodeURIComponent(logType)
                + '&startTime=' + encodeURIComponent(startTime)
                + '&endTime=' + encodeURIComponent(endTime)
                + '&progressId=' + encodeURIComponent(pid);
            this.performBlobDownload(baseUrl + '/deviceLog/system/download/auth' + query, logType + '_system_logs.zip', 'application/zip');
        },
        performBlobDownload: function (url, fallbackFilename, mimeType) {
            var self = this;
            $.ajax({
                url: baseUrl + '/deviceLog/day/' + day + '/download/auth?type=' + encodeURIComponent(type) + '&deviceNo=' + encodeURIComponent(deviceNo) + '&progressId=' + encodeURIComponent(pid),
                url: url,
                headers: { token: localStorage.getItem('token') },
                method: 'GET',
                xhrFields: { responseType: 'blob' },
@@ -1404,22 +1556,22 @@
                },
                success: function (data, status, xhr) {
                    var disposition = xhr.getResponseHeader('Content-Disposition') || '';
                    var filename = type + '_' + deviceNo + '_' + day + '.zip';
                    var filename = fallbackFilename;
                    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 blob = new Blob([data], { type: mimeType || 'application/octet-stream' });
                    var link = document.createElement('a');
                    var url = window.URL.createObjectURL(blob);
                    link.href = url;
                    var objectUrl = window.URL.createObjectURL(blob);
                    link.href = objectUrl;
                    link.download = filename;
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                    window.URL.revokeObjectURL(url);
                    window.URL.revokeObjectURL(objectUrl);
                    if (self.downloadTimer) {
                        clearInterval(self.downloadTimer);
                        self.downloadTimer = null;
@@ -1438,8 +1590,12 @@
                }
            });
        },
        buildDeviceKey: function (type, deviceNo) {
            return String(type || '') + ':' + String(deviceNo || '');
        buildDeviceKey: function (type, deviceNo, stationId) {
            var key = String(type || '') + ':' + String(deviceNo || '');
            if (String(type || '') === 'Devp') {
                key += ':' + String(stationId || '');
            }
            return key;
        },
        parseDeviceNo: function (deviceNo) {
            var n = parseInt(deviceNo, 10);