fix: 优化日志显示和下载完成反馈

This commit is contained in:
2026-05-22 10:30:52 +08:00
parent c2952d028f
commit 211f87e65e
15 changed files with 259 additions and 94 deletions
+6 -6
View File
@@ -487,14 +487,14 @@ async function downloadSelectedCsv() {
const filename = `${selectedCsvTable.value}.csv`;
downloadingFormat.value = 'csv';
try {
const completed = await download(
const result = await download(
'/api/download',
{ table_name: selectedCsvTable.value, format: 'csv' },
filename
);
if (completed) {
if (result.saved) {
csvDialogVisible.value = false;
showDownloadCompleteDialog(dialog, filename);
showDownloadCompleteDialog(dialog, filename, result.path);
}
} catch (error) {
message.error(error instanceof Error ? error.message : '导出失败');
@@ -510,14 +510,14 @@ async function downloadSelectedXlsx() {
downloadingFormat.value = 'xlsx';
const filename = selectedXlsxTables.value.length === 1 ? `${selectedXlsxTables.value[0]}.xlsx` : 'tables.xlsx';
try {
const completed = await download(
const result = await download(
'/api/download',
{ table_names: selectedXlsxTables.value, format: 'xlsx' },
filename
);
if (completed) {
if (result.saved) {
xlsxDialogVisible.value = false;
showDownloadCompleteDialog(dialog, filename);
showDownloadCompleteDialog(dialog, filename, result.path);
}
} catch (error) {
message.error(error instanceof Error ? error.message : '导出失败');
+8 -1
View File
@@ -141,7 +141,12 @@
{{ taskStatus.error }}
</n-alert>
<div ref="logContainer" class="log-container">
<pre class="log-content">{{ logText }}</pre>
<pre class="colored-log-content"><span
v-for="(line, index) in logLines"
:key="index"
class="colored-log-line"
:class="`log-level-${line.level}`"
>{{ line.text }}</span></pre>
</div>
</div>
</div>
@@ -189,6 +194,7 @@ import {
import { ApiRequestError, apiGet, apiPost, upload } from '../api/client';
import type { ActiveTask, ApiErrorDetail, LicenseStatus, TaskStatus } from '../types';
import { toColoredLogLines } from '../composables/logLines';
type FileStatus = 'pending' | 'uploading' | 'uploaded' | 'error';
@@ -248,6 +254,7 @@ const stageLabels: Record<string, string> = {
};
const logText = computed(() => taskStatus.value?.logs?.join('\n') || '等待任务开始');
const logLines = computed(() => toColoredLogLines(logText.value, '等待任务开始'));
const processVisible = computed(() => Boolean(taskStatus.value || activeTask.value?.has_active));
const taskInProgress = computed(() => {
if (activeTask.value?.has_active) {
+5 -20
View File
@@ -101,10 +101,9 @@ import { CopyOutline, DownloadOutline, FileTrayOutline, RefreshOutline, TrashOut
import { apiGet, apiPost, download as downloadFile } from '../api/client';
import type { CacheSize, HistoryDetail, HistoryRecord } from '../types';
import { showDownloadCompleteDialog } from '../composables/downloadFeedback';
import { toColoredLogLines } from '../composables/logLines';
import { resetPageHeader, setPageHeader } from '../composables/pageHeader';
type LogLevel = 'default' | 'info' | 'success' | 'warning' | 'error';
const message = useMessage();
const dialog = useDialog();
const records = ref<HistoryRecord[]>([]);
@@ -120,13 +119,7 @@ let recordSizeToken = 0;
const totalSizeText = computed(() => `总占用: ${cacheSize.value?.size_formatted || '计算中...'}`);
const hasRecords = computed(() => records.value.length > 0);
const detailLogText = computed(() => detail.value?.logs?.join('\n') || '');
const detailLogLines = computed(() => {
const lines = detailLogText.value ? detailLogText.value.split(/\r?\n/) : ['暂无日志'];
return lines.map(line => ({
text: line,
level: resolveLogLevel(line)
}));
});
const detailLogLines = computed(() => toColoredLogLines(detailLogText.value));
onMounted(() => {
setPageHeader({
@@ -186,9 +179,9 @@ async function downloadHistory(record: HistoryRecord) {
downloadingRecordId.value = record.id;
try {
const filename = `${record.id}.zip`;
const completed = await downloadFile('/api/history/download', { record_id: record.id }, filename);
if (completed) {
showDownloadCompleteDialog(dialog, filename);
const result = await downloadFile('/api/history/download', { record_id: record.id }, filename);
if (result.saved) {
showDownloadCompleteDialog(dialog, filename, result.path);
}
} catch (error) {
message.error(error instanceof Error ? error.message : '下载历史数据失败');
@@ -256,14 +249,6 @@ async function writeClipboardText(text: string) {
}
}
function resolveLogLevel(line: string): LogLevel {
if (/\b(ERROR|FAILED|FAILURE)\b|错误|失败/i.test(line)) return 'error';
if (/\b(WARN|WARNING)\b|警告/i.test(line)) return 'warning';
if (/\b(SUCCESS|SUCCEEDED|COMPLETED)\b|成功|完成/i.test(line)) return 'success';
if (/\bINFO\b|信息/i.test(line)) return 'info';
return 'default';
}
function confirmDelete(recordId: string) {
dialog.warning({
title: '删除历史记录',
+34 -11
View File
@@ -4,14 +4,8 @@
<div ref="editorHost" class="script-editor" />
</div>
<div class="script-status-bar">
<span>{{ cursorText }}</span>
<span>{{ editorStatus }}</span>
<span>{{ lineCount }} 行</span>
</div>
<div v-if="taskStatus" class="process-section script-process-section">
<div class="upload-process-card">
<div v-if="taskStatus || taskId" class="script-process-section">
<div class="upload-process-card script-process-card">
<div class="card-header">
<div class="card-header-left">
<span class="card-title">脚本执行进度</span>
@@ -22,10 +16,23 @@
</div>
</div>
<div class="card-body">
<n-log :log="logText" language="text" trim class="script-log" />
<div class="colored-log-panel script-log" role="log" aria-label="脚本执行日志">
<pre class="colored-log-content"><span
v-for="(line, index) in logLines"
:key="index"
class="colored-log-line"
:class="`log-level-${line.level}`"
>{{ line.text }}</span></pre>
</div>
</div>
</div>
</div>
<div class="script-status-bar">
<span>{{ cursorText }}</span>
<span>{{ editorStatus }}</span>
<span>{{ lineCount }} 行</span>
</div>
</div>
</template>
@@ -39,12 +46,17 @@ import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution';
import { apiGet, apiPost } from '../api/client';
import type { ApiMessage, ScriptContent, TaskStatus } from '../types';
import { toColoredLogLines } from '../composables/logLines';
import { resetPageHeader, setPageHeader } from '../composables/pageHeader';
type MonacoGlobal = typeof globalThis & {
MonacoEnvironment?: {
getWorker: (_moduleId: string, _label: string) => Worker;
};
__capacityReportScriptTaskState?: {
taskId: ReturnType<typeof ref<string>>;
taskStatus: ReturnType<typeof ref<TaskStatus | null>>;
};
};
(globalThis as MonacoGlobal).MonacoEnvironment = {
@@ -65,13 +77,21 @@ const isModified = ref(false);
const loading = ref(false);
const saving = ref(false);
const executing = ref(false);
const taskId = ref('');
const taskStatus = ref<TaskStatus | null>(null);
let timer: number | undefined;
let themeObserver: MutationObserver | undefined;
let applyingRemoteContent = false;
const globalWindow = window as MonacoGlobal;
const scriptTaskState = globalWindow.__capacityReportScriptTaskState || {
taskId: ref(''),
taskStatus: ref<TaskStatus | null>(null)
};
globalWindow.__capacityReportScriptTaskState = scriptTaskState;
const taskId = scriptTaskState.taskId;
const taskStatus = scriptTaskState.taskStatus;
const logText = computed(() => taskStatus.value?.logs?.join('\n') || '等待执行日志');
const logLines = computed(() => toColoredLogLines(logText.value, '等待执行日志'));
const statusClass = computed(() => {
if (taskStatus.value?.status === 'completed') return 'completed';
if (taskStatus.value?.status === 'failed') return 'failed';
@@ -123,6 +143,9 @@ onMounted(async () => {
createEditor();
observeTheme();
await loadScript();
if (taskId.value && taskStatus.value && !['completed', 'failed'].includes(taskStatus.value.status)) {
startPolling();
}
});
onBeforeUnmount(() => {
+3 -3
View File
@@ -659,9 +659,9 @@ async function changePassword() {
async function downloadConfig() {
try {
const filename = 'Configure.json';
const completed = await downloadGet('/api/config/download', filename);
if (completed) {
showDownloadCompleteDialog(dialog, filename);
const result = await downloadGet('/api/config/download', filename);
if (result.saved) {
showDownloadCompleteDialog(dialog, filename, result.path);
}
} catch (error) {
message.error(error instanceof Error ? error.message : '下载配置失败');