feat: redesign responsive control room ui
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import apiClient, { getApiErrorMessage } from "@/api/client";
|
||||
import { useViewport } from "@/composables/useViewport";
|
||||
import type { SystemLog } from "@/types";
|
||||
@@ -22,6 +22,10 @@ const logLevelOptions = Object.entries(logLevelLabelMap).map(([value, label]) =>
|
||||
label
|
||||
}));
|
||||
|
||||
const totalWarnings = computed(() => logs.value.filter((item) => item.level === 2).length);
|
||||
const totalErrors = computed(() => logs.value.filter((item) => item.level === 3).length);
|
||||
const newestLogTime = computed(() => (logs.value[0] ? formatDate(logs.value[0].createdAt) : "-"));
|
||||
|
||||
async function loadLogs() {
|
||||
loading.value = true;
|
||||
loadError.value = "";
|
||||
@@ -67,18 +71,50 @@ onMounted(loadLogs);
|
||||
<div class="page-stack">
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<div class="page-kicker">Diagnostics</div>
|
||||
<h1 class="page-title">系统日志</h1>
|
||||
<p class="page-subtitle">
|
||||
支持按直播间、任务和日志级别筛选,便于快速定位解析失败、巡检异常和 ffmpeg 输出问题。
|
||||
面向排障的原始事件流。这里保留最近发生的关键日志,可以按直播间、任务和级别快速收缩范围。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-toolbar">
|
||||
<el-button @click="loadLogs">刷新日志</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-alert v-if="loadError" class="page-error-alert" type="error" :closable="false" show-icon :title="loadError" />
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-card__label">当前加载</div>
|
||||
<div class="stat-card__value">{{ logs.length }}</div>
|
||||
<div class="stat-card__hint">按当前筛选条件拉取到的日志条目。</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card__label">Warning</div>
|
||||
<div class="stat-card__value">{{ totalWarnings }}</div>
|
||||
<div class="stat-card__hint">用于快速观察近期是否出现成批预警。</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card__label">Error</div>
|
||||
<div class="stat-card__value">{{ totalErrors }}</div>
|
||||
<div class="stat-card__hint">优先定位对录制流程有破坏性的异常事件。</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card__label">最新事件</div>
|
||||
<div class="stat-card__value" style="font-size: 18px; letter-spacing: -0.03em;">{{ newestLogTime }}</div>
|
||||
<div class="stat-card__hint">默认按时间倒序,优先展示最近发生的事件。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-card class="surface-card filter-card" shadow="never">
|
||||
<h3 class="section-title">日志过滤</h3>
|
||||
<p class="section-subtitle">默认拉取最近 200 条日志,可按直播间 ID、任务 ID 和级别缩小范围。</p>
|
||||
<div class="toolbar-row">
|
||||
<div>
|
||||
<h3 class="section-title">日志过滤</h3>
|
||||
<p class="section-subtitle">默认拉取最近 200 条日志,移动端也保留同一套过滤维度。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form class="filter-form">
|
||||
<el-form-item label="直播间 ID">
|
||||
@@ -101,20 +137,51 @@ onMounted(loadLogs);
|
||||
<el-input-number v-model="filters.take" :min="1" :max="500" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadLogs">查询</el-button>
|
||||
<el-button type="primary" @click="loadLogs">应用筛选</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card class="surface-card table-card" shadow="never">
|
||||
<h3 class="section-title">日志列表</h3>
|
||||
<p class="section-subtitle">按时间倒序排列,优先展示最近发生的事件。</p>
|
||||
<div class="toolbar-row">
|
||||
<div>
|
||||
<h3 class="section-title">日志列表</h3>
|
||||
<p class="section-subtitle">桌面端保留高密度表格,移动端切成事件卡片,便于单手排查。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-scroll-shell logs-table-shell">
|
||||
<div v-if="isMobile" class="data-card-list">
|
||||
<article v-for="row in logs" :key="row.id" class="data-card">
|
||||
<div class="data-card__header">
|
||||
<div>
|
||||
<div class="data-card__title">{{ row.message }}</div>
|
||||
<div class="data-card__subtitle">{{ row.category }}</div>
|
||||
</div>
|
||||
<el-tag :type="levelTagType(row.level)">{{ logLevelLabelMap[row.level] }}</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="data-card__grid">
|
||||
<div>
|
||||
<dt>时间</dt>
|
||||
<dd>{{ formatDate(row.createdAt) }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>任务</dt>
|
||||
<dd class="monospace">{{ row.recordTaskId || "-" }}</dd>
|
||||
</div>
|
||||
<div style="grid-column: 1 / -1;">
|
||||
<dt>详情</dt>
|
||||
<dd class="log-detail monospace">{{ row.detail || "-" }}</dd>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div v-else class="table-scroll-shell logs-table-shell">
|
||||
<el-table
|
||||
:data="logs"
|
||||
v-loading="loading"
|
||||
:height="isMobile ? undefined : 720"
|
||||
:height="720"
|
||||
class="premium-table logs-table"
|
||||
table-layout="auto"
|
||||
>
|
||||
@@ -135,7 +202,7 @@ onMounted(loadLogs);
|
||||
</el-table-column>
|
||||
<el-table-column label="时间" width="180">
|
||||
<template #default="{ row }">
|
||||
<span class="log-date-text">{{ formatDate(row.createdAt) }}</span>
|
||||
<span class="table-date-text">{{ formatDate(row.createdAt) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -145,20 +212,6 @@ onMounted(loadLogs);
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page-stack {
|
||||
display: grid;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.page-error-alert {
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.filter-card :deep(.el-card__body),
|
||||
.table-card :deep(.el-card__body) {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.filter-form {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
@@ -166,30 +219,11 @@ onMounted(loadLogs);
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.filter-form :deep(.el-form-item:last-child) {
|
||||
align-self: end;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
@@ -208,13 +242,9 @@ onMounted(loadLogs);
|
||||
.filter-form {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.filter-form :deep(.el-form-item:last-child) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
@media (max-width: 767px) {
|
||||
.filter-form {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user