feat: improve recording automation and task workflows

This commit is contained in:
2026-04-23 23:18:11 +08:00
parent 1c892259a9
commit 23ead56781
88 changed files with 9579 additions and 1581 deletions
+98 -26
View File
@@ -1,31 +1,44 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from "vue";
import apiClient from "@/api/client";
import apiClient, { getApiErrorMessage } from "@/api/client";
import { useViewport } from "@/composables/useViewport";
import type { SystemLog } from "@/types";
import { logLevelLabelMap } from "@/types";
const loading = ref(false);
const logs = ref<SystemLog[]>([]);
const loadError = ref("");
const { isMobile } = useViewport();
const filters = reactive({
liveRoomId: "",
level: undefined as number | undefined,
recordTaskId: "",
take: 200
});
const logLevelOptions = Object.entries(logLevelLabelMap).map(([value, label]) => ({
value: Number(value),
label
}));
async function loadLogs() {
loading.value = true;
loadError.value = "";
try {
const { data } = await apiClient.get<SystemLog[]>("/logs", {
params: {
liveRoomId: filters.liveRoomId || undefined,
level: typeof filters.level === "number" ? filters.level : undefined,
recordTaskId: filters.recordTaskId || undefined,
take: filters.take
}
});
logs.value = data;
} catch (error) {
loadError.value = getApiErrorMessage(error, "系统日志加载失败,请稍后重试。");
} finally {
loading.value = false;
}
@@ -55,18 +68,32 @@ onMounted(loadLogs);
<div class="page-header">
<div>
<h1 class="page-title">系统日志</h1>
<p class="page-subtitle">支持按直播间或任务过滤日志便于定位解析失败巡检异常和 ffmpeg 输出问题</p>
<p class="page-subtitle">
支持按直播间任务和日志级别筛选便于快速定位解析失败巡检异常和 ffmpeg 输出问题
</p>
</div>
</div>
<el-alert v-if="loadError" class="page-error-alert" type="error" :closable="false" show-icon :title="loadError" />
<el-card class="surface-card filter-card" shadow="never">
<h3 class="section-title">日志过滤</h3>
<p class="section-subtitle">可选传入直播间 ID 或录制任务 ID数量默认取最近 200 </p>
<p class="section-subtitle">默认拉取最近 200 条日志可按直播间 ID任务 ID 和级别缩小范围</p>
<el-form class="filter-form">
<el-form-item label="直播间 ID">
<el-input v-model="filters.liveRoomId" placeholder="可选" />
</el-form-item>
<el-form-item label="日志级别">
<el-select v-model="filters.level" clearable placeholder="全部级别">
<el-option
v-for="option in logLevelOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item label="任务 ID">
<el-input v-model="filters.recordTaskId" placeholder="可选" />
</el-form-item>
@@ -81,30 +108,38 @@ onMounted(loadLogs);
<el-card class="surface-card table-card" shadow="never">
<h3 class="section-title">日志列表</h3>
<p class="section-subtitle">按时间倒序排列便于快速回看最近一次异常</p>
<p class="section-subtitle">按时间倒序排列优先展示最近发生的事件</p>
<el-table :data="logs" v-loading="loading" height="720" class="premium-table">
<el-table-column label="级别" width="100">
<template #default="{ row }">
<el-tag :type="levelTagType(row.level)">
{{ logLevelLabelMap[row.level] }}
</el-tag>
</template>
</el-table-column>
<div class="table-scroll-shell logs-table-shell">
<el-table
:data="logs"
v-loading="loading"
:height="isMobile ? undefined : 720"
class="premium-table logs-table"
table-layout="auto"
>
<el-table-column label="级别" width="100">
<template #default="{ row }">
<el-tag :type="levelTagType(row.level)">
{{ logLevelLabelMap[row.level] }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="分类" width="120" prop="category" />
<el-table-column label="消息" min-width="240" prop="message" />
<el-table-column label="详情" min-width="360">
<template #default="{ row }">
<span class="monospace log-detail">{{ row.detail || "-" }}</span>
</template>
</el-table-column>
<el-table-column label="时间" width="180">
<template #default="{ row }">
{{ formatDate(row.createdAt) }}
</template>
</el-table-column>
</el-table>
<el-table-column label="分类" width="128" prop="category" />
<el-table-column label="消息" min-width="240" prop="message" />
<el-table-column label="详情" min-width="360">
<template #default="{ row }">
<span class="monospace log-detail">{{ row.detail || "-" }}</span>
</template>
</el-table-column>
<el-table-column label="时间" width="180">
<template #default="{ row }">
<span class="log-date-text">{{ formatDate(row.createdAt) }}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</div>
</template>
@@ -115,6 +150,10 @@ onMounted(loadLogs);
gap: 24px;
}
.page-error-alert {
border-radius: 14px;
}
.filter-card :deep(.el-card__body),
.table-card :deep(.el-card__body) {
padding-top: 20px;
@@ -122,7 +161,7 @@ onMounted(loadLogs);
.filter-form {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 8px 12px;
align-items: end;
}
@@ -134,6 +173,35 @@ onMounted(loadLogs);
.log-detail {
font-size: 12px;
color: #6d665b;
line-height: 1.6;
}
.log-date-text {
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.logs-table-shell {
margin-inline: -4px;
padding-inline: 4px;
}
.logs-table {
min-width: 980px;
}
.logs-table :deep(.el-table__cell) {
vertical-align: top;
}
.logs-table :deep(.cell) {
overflow: visible;
}
@media (max-width: 1200px) {
.filter-form {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
@media (max-width: 900px) {
@@ -150,5 +218,9 @@ onMounted(loadLogs);
.filter-form {
grid-template-columns: 1fr;
}
.filter-form :deep(.el-button) {
width: 100%;
}
}
</style>