Files
timi-server/src/pages/setting/DashboardSetting.vue
2026-04-13 14:55:50 +08:00

266 lines
6.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="page">
<section class="card">
<div class="head">
<p class="tag">服务器</p>
<h2 class="header">数据刷新与采集</h2>
<p class="desc">用于配置服务器仪表板的请求频率和 metrics 参数</p>
</div>
<div class="group">
<p class="label">当前状态刷新频率</p>
<t-input v-model="snapshotRefreshText" type="number" clearable placeholder="默认 3 秒" />
</div>
<div class="group">
<p class="label">历史采样刷新频率</p>
<t-input v-model="historyRefreshText" type="number" clearable placeholder="默认 10 秒" />
</div>
<div class="group">
<p class="label">当前状态采集指标</p>
<div class="metrics">
<t-button
v-for="metric in snapshotMetricOptions"
:key="metric.value"
size="small"
theme="primary"
:variant="isSnapshotMetricChecked(metric.value) ? 'base' : 'outline'"
@click="toggleSnapshotMetric(metric.value)"
>
<span v-text="metric.label" />
</t-button>
</div>
</div>
<div class="group">
<p class="label">历史采样采集指标</p>
<div class="metrics">
<t-button
v-for="metric in historyMetricOptions"
:key="metric.value"
size="small"
theme="primary"
:variant="isHistoryMetricChecked(metric.value) ? 'base' : 'outline'"
@click="toggleHistoryMetric(metric.value)"
>
<span v-text="metric.label" />
</t-button>
</div>
</div>
<t-button block theme="primary" @click="saveServerDashboardSetting">
保存服务器配置
</t-button>
<t-button block variant="outline" @click="resetServerDashboardSetting">
恢复默认
</t-button>
</section>
<section class="card">
<div class="head">
<p class="tag">Docker</p>
<h2 class="header">配置待定</h2>
<p class="desc">后续将支持 Docker 仪表板采集项和展示策略配置</p>
</div>
</section>
<section class="card">
<div class="head">
<p class="tag">UPS</p>
<h2 class="header">配置待定</h2>
<p class="desc">后续将支持 UPS 仪表板采集项和告警策略配置</p>
</div>
</section>
</div>
</template>
<script setup lang="ts">
import { Toast } from "tdesign-mobile-vue";
import {
useSettingStore,
type DashboardHistoryMetric,
type DashboardSnapshotMetric,
type ServerDashboardSetting
} from "@/store/settingStore";
defineOptions({
name: "DashboardSetting"
});
const settingStore = useSettingStore();
const snapshotMetricOptions: Array<{ label: string; value: DashboardSnapshotMetric }> = [
{ label: "系统", value: "os" },
{ label: "CPU", value: "cpu" },
{ label: "内存", value: "memory" },
{ label: "JVM", value: "jvm" },
{ label: "网络", value: "network" },
{ label: "硬件", value: "hardware" },
{ label: "磁盘", value: "storage" }
];
const historyMetricOptions: Array<{ label: string; value: DashboardHistoryMetric }> = [
{ label: "CPU", value: "cpu" },
{ label: "内存", value: "memory" },
{ label: "JVM", value: "jvm" },
{ label: "网络", value: "network" }
];
const snapshotRefreshText = ref("");
const historyRefreshText = ref("");
const selectedSnapshotMetrics = ref<DashboardSnapshotMetric[]>([]);
const selectedHistoryMetrics = ref<DashboardHistoryMetric[]>([]);
watch(
() => settingStore.dashboard.server,
(setting) => {
snapshotRefreshText.value = String(setting.snapshotRefreshSeconds);
historyRefreshText.value = String(setting.historyRefreshSeconds);
selectedSnapshotMetrics.value = [...setting.snapshotMetrics];
selectedHistoryMetrics.value = [...setting.historyMetrics];
},
{ immediate: true }
);
function isSnapshotMetricChecked(metric: DashboardSnapshotMetric): boolean {
return selectedSnapshotMetrics.value.includes(metric);
}
function isHistoryMetricChecked(metric: DashboardHistoryMetric): boolean {
return selectedHistoryMetrics.value.includes(metric);
}
function toggleSnapshotMetric(metric: DashboardSnapshotMetric): void {
if (isSnapshotMetricChecked(metric)) {
if (selectedSnapshotMetrics.value.length <= 1) {
Toast({
theme: "warning",
message: "当前状态至少保留一个采集指标"
});
return;
}
selectedSnapshotMetrics.value = selectedSnapshotMetrics.value.filter((item) => item !== metric);
return;
}
selectedSnapshotMetrics.value = [...selectedSnapshotMetrics.value, metric];
}
function toggleHistoryMetric(metric: DashboardHistoryMetric): void {
if (isHistoryMetricChecked(metric)) {
if (selectedHistoryMetrics.value.length <= 1) {
Toast({
theme: "warning",
message: "历史采样至少保留一个采集指标"
});
return;
}
selectedHistoryMetrics.value = selectedHistoryMetrics.value.filter((item) => item !== metric);
return;
}
selectedHistoryMetrics.value = [...selectedHistoryMetrics.value, metric];
}
function saveServerDashboardSetting(): void {
const nextSetting: Partial<ServerDashboardSetting> = {
snapshotRefreshSeconds: normalizeSecondValue(snapshotRefreshText.value, 3),
historyRefreshSeconds: normalizeSecondValue(historyRefreshText.value, 10),
snapshotMetrics: selectedSnapshotMetrics.value,
historyMetrics: selectedHistoryMetrics.value
};
settingStore.setServerDashboard(nextSetting);
Toast({
theme: "success",
message: "服务器仪表板配置已保存"
});
}
function resetServerDashboardSetting(): void {
settingStore.setServerDashboard({
snapshotRefreshSeconds: 3,
historyRefreshSeconds: 10,
snapshotMetrics: ["os", "cpu", "memory", "jvm", "network", "hardware", "storage"],
historyMetrics: ["cpu", "memory", "jvm", "network"]
});
Toast({
theme: "success",
message: "已恢复默认配置"
});
}
function normalizeSecondValue(value: string, fallback: number): number {
const numberValue = Number(value);
if (Number.isNaN(numberValue)) {
return fallback;
}
return Math.min(Math.max(Math.floor(numberValue), 1), 120);
}
</script>
<style scoped lang="less">
.page {
gap: 1rem;
display: flex;
padding: 1rem;
flex-direction: column;
.card {
gap: 1rem;
display: flex;
padding: 1rem;
border-radius: 1rem;
flex-direction: column;
border: 1px solid var(--app-line);
background: var(--app-card);
box-shadow: 0 .35rem 1rem rgba(17, 32, 56, .05);
}
.head,
.group {
gap: .5rem;
display: flex;
flex-direction: column;
}
.tag,
.label,
.desc {
margin: 0;
color: var(--app-sub);
}
.tag,
.label {
font-size: .875rem;
}
.header {
margin: 0;
font-size: 1.25rem;
}
.desc {
line-height: 1.6;
}
.metrics {
gap: .75rem;
display: flex;
flex-wrap: wrap;
}
}
:global(.theme-dark) .page {
.card {
box-shadow: 0 .35rem 1rem rgba(0, 0, 0, .2);
}
}
</style>