Junjie
2026-04-16 424002744fa37f4483fef3e36633bd193481781a
#系统日志下载V3.0.0.3
4个文件已修改
509 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/controller/DeviceLogController.java 374 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/deviceLogs/deviceLogs.js 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/deviceLogs/deviceLogs.html 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/DeviceLogController.java
@@ -18,12 +18,21 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
@@ -48,6 +57,15 @@
    @Value("${deviceLogStorage.loggingPath}")
    private String loggingPath;
    @Value("${logging.file.path:./stock/out/@pom.build.finalName@/logs}")
    private String systemLoggingPath;
    private static final DateTimeFormatter SYSTEM_LOG_DATE = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter SYSTEM_LOG_DATE_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter SYSTEM_LOG_EXPORT_TIME = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
    private static final Pattern SYSTEM_LOG_TIMESTAMP = Pattern.compile("^(\\d{2}:\\d{2}:\\d{2}\\.\\d{3})");
    private static final Pattern SYSTEM_LOG_ROLLED_FILE = Pattern.compile("^(info|error)_(\\d{4}-\\d{2}-\\d{2})\\.(\\d+)\\.log$");
    private static class ProgressInfo {
        long totalRaw;
@@ -85,7 +103,27 @@
        Path lastFile;
    }
    private static class SystemLogFileInfo {
        String logType;
        LocalDate day;
        Integer index;
        boolean active;
        Path path;
    }
    private static class SystemLogDownloadRequest {
        String logType;
        LocalDateTime startTime;
        LocalDateTime endTime;
        List<Path> files;
    }
    private static final Map<String, ProgressInfo> DOWNLOAD_PROGRESS = new ConcurrentHashMap<>();
    private static final int SYSTEM_LOG_MAX_RANGE_DAYS = 10;
    private static final int SYSTEM_LOG_MATCH_BUFFER_LINES = 200000;
    private static final String SYSTEM_LOG_ENTRY_NAME = "system.log";
    private static final Map<String, SystemLogDownloadRequest> SYSTEM_LOG_DOWNLOAD_REQUESTS = new ConcurrentHashMap<>();
    @RequestMapping(value = "/deviceLog/dates/auth")
    @ManagerAuth
@@ -651,20 +689,145 @@
        return R.ok(res);
    }
    @RequestMapping(value = "/deviceLog/system/download/init/auth")
    @ManagerAuth
    public R initSystemDownload(@org.springframework.web.bind.annotation.RequestBody com.alibaba.fastjson.JSONObject param) {
        try {
            String logType = normalizeSystemLogType(param.getString("logType"));
            if (logType == null) {
                return R.error("日志类型错误");
            }
            LocalDateTime startTime = parseSystemLogDateTime(param.getString("startTime"));
            LocalDateTime endTime = parseSystemLogDateTime(param.getString("endTime"));
            if (startTime == null || endTime == null) {
                return R.error("时间格式错误");
            }
            if (startTime.isAfter(endTime)) {
                return R.error("开始时间不能晚于结束时间");
            }
            long daySpan = java.time.temporal.ChronoUnit.DAYS.between(startTime.toLocalDate(), endTime.toLocalDate());
            if (daySpan > SYSTEM_LOG_MAX_RANGE_DAYS) {
                return R.error("时间范围不能超过" + SYSTEM_LOG_MAX_RANGE_DAYS + "天");
            }
            List<Path> files = findSystemLogFiles(logType, startTime, endTime);
            if (files.isEmpty()) {
                return R.error("未找到日志文件");
            }
            String id = UUID.randomUUID().toString();
            ProgressInfo info = new ProgressInfo();
            info.totalCount = files.size();
            long sum = 0L;
            for (Path f : files) {
                try { sum += Files.size(f); } catch (Exception ignored) {}
            }
            info.totalRaw = sum;
            info.processedRaw = 0L;
            info.processedCount = 0;
            info.finished = false;
            DOWNLOAD_PROGRESS.put(id, info);
            SystemLogDownloadRequest request = new SystemLogDownloadRequest();
            request.logType = logType;
            request.startTime = startTime;
            request.endTime = endTime;
            request.files = files;
            SYSTEM_LOG_DOWNLOAD_REQUESTS.put(id, request);
            Map<String, Object> res = new HashMap<>();
            res.put("progressId", id);
            res.put("totalSize", info.totalRaw);
            res.put("fileCount", info.totalCount);
            return R.ok(res);
        } catch (Exception e) {
            log.error("初始化系统日志下载失败", e);
            return R.error("初始化失败");
        }
    }
    @RequestMapping(value = "/deviceLog/system/download/auth")
    @ManagerAuth
    public void downloadSystemLog(@RequestParam("logType") String logTypeParam,
                                  @RequestParam("startTime") String startTimeParam,
                                  @RequestParam("endTime") String endTimeParam,
                                  @RequestParam(value = "progressId", required = false) String progressId,
                                  HttpServletResponse response) {
        String progressKey = null;
        try {
            String logType = normalizeSystemLogType(logTypeParam);
            LocalDateTime startTime = parseSystemLogDateTime(startTimeParam);
            LocalDateTime endTime = parseSystemLogDateTime(endTimeParam);
            if (logType == null || startTime == null || endTime == null || startTime.isAfter(endTime)) {
                response.setStatus(400);
                return;
            }
            SystemLogDownloadRequest requestInfo = null;
            if (!Cools.isEmpty(progressId)) {
                requestInfo = SYSTEM_LOG_DOWNLOAD_REQUESTS.get(progressId);
                progressKey = progressId;
            }
            List<Path> files;
            if (requestInfo != null
                    && Objects.equals(requestInfo.logType, logType)
                    && Objects.equals(requestInfo.startTime, startTime)
                    && Objects.equals(requestInfo.endTime, endTime)) {
                files = requestInfo.files == null ? Collections.emptyList() : requestInfo.files;
            } else {
                files = findSystemLogFiles(logType, startTime, endTime);
            }
            if (files.isEmpty()) {
                response.setStatus(404);
                return;
            }
            if (Cools.isEmpty(progressKey)) {
                progressKey = UUID.randomUUID().toString();
            }
            ProgressInfo info = prepareProgress(progressKey, files);
            response.reset();
            response.setContentType("application/zip");
            String filename = logType + "_" + formatSystemExportTime(startTime) + "_" + formatSystemExportTime(endTime) + ".zip";
            response.setHeader("Content-Disposition", "attachment; filename=" + filename);
            response.setHeader("X-Progress-Id", progressKey);
            try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
                zos.putNextEntry(new ZipEntry(SYSTEM_LOG_ENTRY_NAME));
                try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(zos, StandardCharsets.UTF_8))) {
                    boolean written = writeSystemLogContent(files, logType, startTime, endTime, writer, info);
                    writer.flush();
                    if (!written) {
                        response.reset();
                        response.setStatus(404);
                        return;
                    }
                }
                zos.closeEntry();
                zos.finish();
                info.finished = true;
            }
        } catch (Exception e) {
            log.error("下载系统日志失败", e);
            try {
                response.reset();
                response.setStatus(500);
            } catch (Exception ignore) {
            }
        } finally {
            if (!Cools.isEmpty(progressId)) {
                SYSTEM_LOG_DOWNLOAD_REQUESTS.remove(progressId);
            }
        }
    }
    @RequestMapping(value = "/deviceLog/enums/auth")
    @ManagerAuth
    public R getEnums() {
        Map<String, Map<String, String>> enums = new HashMap<>();
        enums.put("CrnModeType", Arrays.stream(com.zy.core.enums.CrnModeType.values())
                .collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
        enums.put("CrnStatusType", Arrays.stream(com.zy.core.enums.CrnStatusType.values())
                .collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
        enums.put("CrnForkPosType", Arrays.stream(com.zy.core.enums.CrnForkPosType.values())
                .collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
        enums.put("CrnLiftPosType", Arrays.stream(com.zy.core.enums.CrnLiftPosType.values())
                .collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
@@ -679,10 +842,211 @@
        enums.put("RgvStatusType", Arrays.stream(com.zy.core.enums.RgvStatusType.values())
                .collect(Collectors.toMap(e -> String.valueOf(e.id), e -> e.desc)));
        return R.ok(enums);
    }
    private ProgressInfo prepareProgress(String progressId, List<Path> files) {
        return DOWNLOAD_PROGRESS.compute(progressId, (key, existing) -> {
            ProgressInfo next = existing == null ? new ProgressInfo() : existing;
            next.totalCount = files == null ? 0 : files.size();
            long total = 0L;
            if (files != null) {
                for (Path f : files) {
                    try { total += Files.size(f); } catch (Exception ignored) {}
                }
            }
            next.totalRaw = total;
            next.processedRaw = 0L;
            next.processedCount = 0;
            next.finished = false;
            return next;
        });
    }
    private boolean writeSystemLogContent(List<Path> files,
                                          String logType,
                                          LocalDateTime startTime,
                                          LocalDateTime endTime,
                                          BufferedWriter writer,
                                          ProgressInfo progressInfo) throws Exception {
        boolean wroteAny = false;
        for (Path file : files) {
            SystemLogFileInfo fileInfo = parseSystemLogFile(file);
            if (fileInfo == null) {
                updateProgress(progressInfo, file);
                continue;
            }
            LocalDate baseDate = fileInfo.day != null ? fileInfo.day : startTime.toLocalDate();
            try (Stream<String> lines = Files.lines(file, StandardCharsets.UTF_8)) {
                List<String> pending = new ArrayList<>();
                boolean pendingMatched = false;
                for (Iterator<String> iterator = lines.iterator(); iterator.hasNext(); ) {
                    String line = iterator.next();
                    Matcher matcher = SYSTEM_LOG_TIMESTAMP.matcher(line == null ? "" : line);
                    if (matcher.find()) {
                        if (pendingMatched && !pending.isEmpty()) {
                            for (String pendingLine : pending) {
                                writer.write(pendingLine == null ? "" : pendingLine);
                                writer.newLine();
                            }
                            wroteAny = true;
                        }
                        pending.clear();
                        pendingMatched = isSystemLogLineInRange(baseDate, matcher.group(1), startTime, endTime);
                    }
                    if (pendingMatched) {
                        pending.add(line);
                        if (pending.size() > SYSTEM_LOG_MATCH_BUFFER_LINES) {
                            for (String pendingLine : pending) {
                                writer.write(pendingLine == null ? "" : pendingLine);
                                writer.newLine();
                            }
                            wroteAny = true;
                            pending.clear();
                        }
                    } else if (!pending.isEmpty()) {
                        pending.clear();
                    }
                }
                if (pendingMatched && !pending.isEmpty()) {
                    for (String pendingLine : pending) {
                        writer.write(pendingLine == null ? "" : pendingLine);
                        writer.newLine();
                    }
                    wroteAny = true;
                }
            }
            updateProgress(progressInfo, file);
        }
        if (progressInfo != null) {
            progressInfo.finished = true;
        }
        return wroteAny;
    }
    private void updateProgress(ProgressInfo progressInfo, Path file) {
        if (progressInfo == null) {
            return;
        }
        try {
            progressInfo.processedRaw += Files.size(file);
        } catch (Exception ignored) {
        }
        progressInfo.processedCount += 1;
    }
    private List<Path> findSystemLogFiles(String logType, LocalDateTime startTime, LocalDateTime endTime) throws Exception {
        Path baseDir = Paths.get(systemLoggingPath);
        if (!Files.exists(baseDir) || !Files.isDirectory(baseDir)) {
            return Collections.emptyList();
        }
        LocalDate startDate = startTime.toLocalDate();
        LocalDate endDate = endTime.toLocalDate();
        List<SystemLogFileInfo> matched = new ArrayList<>();
        try (Stream<Path> stream = Files.list(baseDir)) {
            stream.filter(path -> !Files.isDirectory(path)).forEach(path -> {
                SystemLogFileInfo info = parseSystemLogFile(path);
                if (info == null || !Objects.equals(info.logType, logType)) {
                    return;
                }
                if (info.active || info.day == null || (!info.day.isBefore(startDate) && !info.day.isAfter(endDate))) {
                    matched.add(info);
                }
            });
        }
        matched.sort((left, right) -> {
            LocalDate leftDay = left.day == null ? LocalDate.MAX : left.day;
            LocalDate rightDay = right.day == null ? LocalDate.MAX : right.day;
            int cmp = leftDay.compareTo(rightDay);
            if (cmp != 0) {
                return cmp;
            }
            int leftIndex = left.index == null ? Integer.MAX_VALUE : left.index;
            int rightIndex = right.index == null ? Integer.MAX_VALUE : right.index;
            if (left.active != right.active) {
                return left.active ? 1 : -1;
            }
            return Integer.compare(leftIndex, rightIndex);
        });
        return matched.stream().map(item -> item.path).collect(Collectors.toList());
    }
    private SystemLogFileInfo parseSystemLogFile(Path path) {
        if (path == null) {
            return null;
        }
        String name = path.getFileName().toString();
        if ("info.log".equals(name) || "error.log".equals(name)) {
            SystemLogFileInfo info = new SystemLogFileInfo();
            info.logType = name.startsWith("info") ? "info" : "error";
            info.active = true;
            info.path = path;
            return info;
        }
        Matcher matcher = SYSTEM_LOG_ROLLED_FILE.matcher(name);
        if (!matcher.matches()) {
            return null;
        }
        try {
            SystemLogFileInfo info = new SystemLogFileInfo();
            info.logType = matcher.group(1);
            info.day = LocalDate.parse(matcher.group(2), SYSTEM_LOG_DATE);
            info.index = Integer.parseInt(matcher.group(3));
            info.active = false;
            info.path = path;
            return info;
        } catch (Exception e) {
            return null;
        }
    }
    private String normalizeSystemLogType(String logType) {
        if (Cools.isEmpty(logType)) {
            return null;
        }
        String value = logType.trim().toLowerCase(Locale.ROOT);
        if ("info".equals(value) || "error".equals(value)) {
            return value;
        }
        return null;
    }
    private LocalDateTime parseSystemLogDateTime(String value) {
        if (Cools.isEmpty(value)) {
            return null;
        }
        try {
            return LocalDateTime.parse(value.trim(), SYSTEM_LOG_DATE_TIME);
        } catch (DateTimeParseException e) {
            return null;
        }
    }
    private boolean isSystemLogLineInRange(LocalDate baseDate,
                                           String timePart,
                                           LocalDateTime startTime,
                                           LocalDateTime endTime) {
        if (baseDate == null || Cools.isEmpty(timePart) || startTime == null || endTime == null) {
            return false;
        }
        try {
            LocalTime time = LocalTime.parse(timePart);
            LocalDateTime timestamp = LocalDateTime.of(baseDate, time);
            return !timestamp.isBefore(startTime) && !timestamp.isAfter(endTime);
        } catch (DateTimeParseException e) {
            return false;
        }
    }
    private String formatSystemExportTime(LocalDateTime dateTime) {
        if (dateTime == null) {
            return "unknown";
        }
        return SYSTEM_LOG_EXPORT_TIME.format(dateTime);
    }
    private Map<String, Object> buildEmptySummary() {
        return buildSummaryResponse(Collections.emptyList());
    }
src/main/resources/application.yml
@@ -1,6 +1,6 @@
# 系统版本信息
app:
  version: 3.0.0.2
  version: 3.0.0.3
  version-type: prd  # prd 或 dev
  i18n:
    default-locale: zh-CN
src/main/webapp/static/js/deviceLogs/deviceLogs.js
@@ -67,6 +67,10 @@
        detailTab: 'logs',
        rawTab: 'wcs',
        systemLogType: 'info',
        systemLogRange: [],
        systemLogDialogVisible: false,
        downloadDialogVisible: false,
        buildProgress: 0,
        receiveProgress: 0,
@@ -292,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);
@@ -1404,6 +1411,36 @@
        handleCurrentDeviceDownload: function () {
            this.doDownload(this.selectedDay, this.selectedType, this.selectedDeviceNo, this.selectedStationId);
        },
        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;
@@ -1429,6 +1466,33 @@
                    var pid = res.data.progressId;
                    self.startDownloadProgress(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('初始化失败');
@@ -1465,8 +1529,19 @@
                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' + query,
                url: url,
                headers: { token: localStorage.getItem('token') },
                method: 'GET',
                xhrFields: { responseType: 'blob' },
@@ -1481,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;
src/main/webapp/views/deviceLogs/deviceLogs.html
@@ -429,6 +429,25 @@
            gap: 8px;
        }
        .dl-picker-actions {
            display: flex;
            justify-content: flex-end;
            margin-bottom: 12px;
        }
        .dl-dialog-note {
            margin-bottom: 14px;
            font-size: 12px;
            line-height: 1.6;
            color: var(--dl-text-sub);
        }
        .dl-dialog-form {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        .dl-btn {
            height: 34px;
            padding: 0 14px;
@@ -1122,6 +1141,9 @@
                        <div class="dl-panel-desc">{{ selectedDay ? ('日志日期 ' + formatDayText(selectedDay) + ',先筛选设备类型,再点击一台设备进入状态查看页。') : '先从左侧选择一个日志日期。' }}</div>
                    </div>
                    <div class="dl-panel-body">
                        <div class="dl-picker-actions">
                            <button type="button" class="dl-btn is-ghost" @click="openSystemLogDialog" :disabled="!selectedDay">下载系统日志</button>
                        </div>
                        <div class="dl-picker-toolbar">
                            <div class="dl-type-tabs">
                                <button
@@ -1356,6 +1378,30 @@
        </section>
    </div>
    <el-dialog title="下载系统日志" :visible.sync="systemLogDialogVisible" width="520px">
        <div class="dl-dialog-note">请选择日志类型和时间范围,然后下载指定时间段内的系统日志。</div>
        <div class="dl-dialog-form">
            <el-select v-model="systemLogType" size="small" placeholder="日志类型">
                <el-option label="info.log" value="info"></el-option>
                <el-option label="error.log" value="error"></el-option>
            </el-select>
            <el-date-picker
                v-model="systemLogRange"
                size="small"
                type="datetimerange"
                unlink-panels
                range-separator="至"
                start-placeholder="开始时间"
                end-placeholder="结束时间"
                value-format="yyyy-MM-dd HH:mm:ss">
            </el-date-picker>
        </div>
        <span slot="footer" class="dialog-footer">
            <el-button size="small" @click="systemLogDialogVisible = false">取消</el-button>
            <el-button size="small" type="primary" @click="handleSystemLogDownload" :disabled="!canDownloadSystemLog">下载</el-button>
        </span>
    </el-dialog>
    <el-dialog :title="downloadDialogTitle" :visible.sync="downloadDialogVisible" width="400px" :close-on-click-modal="false" :show-close="false">
        <div style="padding: 10px;">
            <div style="margin-bottom: 5px; font-size: 14px;">压缩生成进度</div>