This commit is contained in:
Your Name
2026-04-16 22:41:13 +08:00
parent ff79f5c13d
commit 0d8c0b4aed
3 changed files with 180 additions and 2 deletions

6
.gitignore vendored
View File

@@ -21,7 +21,6 @@ dist/
# Logs and runtime data
*.log
logs/
/runtime/
/diagnostics/
/domain-api/runtime/
@@ -34,12 +33,15 @@ release/
# Local data and generated files
uploads/
exports/
/logs/
/exports/
tmp/
*.sqlite3
*.pkl
domains.txt
credentials.json
!/domain-web/src/views/logs/
!/domain-web/src/views/exports/
# Local deploy config copies
domain-api/deploy/multi-region/*.conf

View File

@@ -0,0 +1,85 @@
<template>
<PageCard title="导出中心" description="创建导出任务,并查看当前已生成的导出文件。">
<div class="toolbar">
<el-button type="primary" :loading="runningTxt" @click="runExport('txt')">导出 TXT</el-button>
<el-button :loading="runningCsv" @click="runExport('csv')">导出 CSV</el-button>
<el-button plain :loading="loading" @click="loadExports">刷新列表</el-button>
</div>
<el-alert
v-if="lastMessage"
:title="lastMessage"
type="success"
:closable="false"
show-icon
style="margin-bottom: 16px"
/>
<el-table :data="records" border>
<el-table-column prop="filename" label="文件名" min-width="260" />
<el-table-column prop="format" label="格式" width="100" />
<el-table-column prop="scope" label="范围" width="120" />
<el-table-column prop="created_at" label="生成时间" min-width="180" />
<el-table-column label="下载" width="120">
<template #default="{ row }">
<el-link :href="downloadUrl(row.filename)" target="_blank" type="primary">下载</el-link>
</template>
</el-table-column>
</el-table>
</PageCard>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { ElMessage } from "element-plus";
import PageCard from "@/components/PageCard.vue";
import { exportsApi } from "@/api/modules";
const records = ref<any[]>([]);
const loading = ref(false);
const runningTxt = ref(false);
const runningCsv = ref(false);
const lastMessage = ref("");
const apiBase = (import.meta.env.VITE_API_BASE_URL || "http://127.0.0.1:8100/api/v1").replace(/\/api\/v1\/?$/, "");
const downloadUrl = (filename: string) => `${apiBase}/api/v1/exports/download/${encodeURIComponent(filename)}`;
const loadExports = async () => {
loading.value = true;
try {
const response = await exportsApi.list();
records.value = response.data || [];
} catch {
ElMessage.error("读取导出记录失败");
} finally {
loading.value = false;
}
};
const runExport = async (format: "txt" | "csv") => {
const loadingRef = format === "txt" ? runningTxt : runningCsv;
loadingRef.value = true;
try {
const response = await exportsApi.run({ format, scope: "all" });
lastMessage.value = response.message || "导出文件已生成";
ElMessage.success(lastMessage.value);
await loadExports();
} catch (error: any) {
ElMessage.error(error?.message || "创建导出任务失败");
} finally {
loadingRef.value = false;
}
};
onMounted(loadExports);
</script>
<style scoped>
.toolbar {
display: flex;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 16px;
}
</style>

View File

@@ -0,0 +1,91 @@
<template>
<PageCard title="日志诊断" description="查看最近日志摘要,并直接下载诊断包。">
<template #header-extra>
<div class="toolbar">
<el-link :href="bundleUrl" target="_blank" type="primary">下载诊断包</el-link>
<el-button plain :loading="loading" @click="loadLogs">刷新</el-button>
</div>
</template>
<el-row :gutter="16">
<el-col :xs="24" :lg="12">
<div class="log-block">
<h3>API 标准输出</h3>
<pre>{{ apiStdout }}</pre>
</div>
</el-col>
<el-col :xs="24" :lg="12">
<div class="log-block">
<h3>Worker 日志</h3>
<pre>{{ workerLog }}</pre>
</div>
</el-col>
</el-row>
</PageCard>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { ElMessage } from "element-plus";
import PageCard from "@/components/PageCard.vue";
import { logsApi } from "@/api/modules";
const loading = ref(false);
const apiStdout = ref("暂无日志");
const workerLog = ref("暂无日志");
const bundleUrl = logsApi.bundleUrl();
const normalizeLines = (value: unknown) => {
if (Array.isArray(value)) {
return value.join("\n") || "暂无日志";
}
return typeof value === "string" && value.trim() ? value : "暂无日志";
};
const loadLogs = async () => {
loading.value = true;
try {
const response = await logsApi.latest();
apiStdout.value = normalizeLines(response.data?.api_stdout);
workerLog.value = normalizeLines(response.data?.worker_log);
} catch {
ElMessage.error("读取日志失败");
} finally {
loading.value = false;
}
};
onMounted(loadLogs);
</script>
<style scoped>
.toolbar {
display: flex;
gap: 12px;
align-items: center;
}
.log-block {
height: 100%;
padding: 18px;
border-radius: 16px;
background: #0f172a;
color: #e2e8f0;
}
.log-block h3 {
margin: 0 0 12px;
color: #f8fafc;
}
.log-block pre {
margin: 0;
white-space: pre-wrap;
word-break: break-word;
font-size: 12px;
line-height: 1.6;
font-family: Consolas, Monaco, monospace;
max-height: 520px;
overflow: auto;
}
</style>