refactor: 容量看板标题与制式切换/刷新并入框架页头
This commit is contained in:
@@ -948,3 +948,10 @@ CellData 处理日志细化(`app/services/cell_data.py`):
|
|||||||
- 默认制式:看板进入默认 `rat='5g'`(原 4g)。
|
- 默认制式:看板进入默认 `rat='5g'`(原 4g)。
|
||||||
- 修复矮图表数值轴刻度重叠:图表高度仅 `clamp(108px,13vh,160px)`,上行/下行利用率分布 0-10% 桶计数极大(约 1.9w)且带千分位,ECharts 自动放 5-6 条刻度导致 Y 轴标签纵向叠压。新增 `abbrNum()`(≥1万→「x万」、≥1千→「xk」、≥1亿→「x亿」)与 `valueAxis()`(`splitNumber:3` + 缩写 formatter),套用到上/下行直方与制式分布的数值轴;频段标记小区横向柱的数值 x 轴同样加 `splitNumber:3`+缩写。
|
- 修复矮图表数值轴刻度重叠:图表高度仅 `clamp(108px,13vh,160px)`,上行/下行利用率分布 0-10% 桶计数极大(约 1.9w)且带千分位,ECharts 自动放 5-6 条刻度导致 Y 轴标签纵向叠压。新增 `abbrNum()`(≥1万→「x万」、≥1千→「xk」、≥1亿→「x亿」)与 `valueAxis()`(`splitNumber:3` + 缩写 formatter),套用到上/下行直方与制式分布的数值轴;频段标记小区横向柱的数值 x 轴同样加 `splitNumber:3`+缩写。
|
||||||
- 验证:`frontend` `vue-tsc --noEmit` 通过。
|
- 验证:`frontend` `vue-tsc --noEmit` 通过。
|
||||||
|
|
||||||
|
## 2026-06-26:容量看板标题/操作并入框架页头
|
||||||
|
|
||||||
|
- 参照「处理历史」做法,看板不再自绘标题栏:删除 `.cap-topbar`(标题/副标题/制式切换/刷新)。改用 `composables/pageHeader.ts` 的 `setPageHeader`/`resetPageHeader` 把内容注入 AppShell 顶部 `page-header`(与其他页一致,标题取路由 meta「容量看板」)。
|
||||||
|
- 注入内容:subtitle「高负荷小区分析 · 实时数据」;actions = 4G / 5G 两个按钮(激活=`type:primary, variant:solid`,未激活=`default+outline`,点击 `switchRat`)+「刷新」(icon `RefreshOutline`,`loading/disabled` 绑 `loadingOverview`)。因 `PageHeaderAction.type/variant` 非响应式,用 `watch([rat, ready], applyPageHeader)` 在制式切换/就绪时重建 actions;`onBeforeUnmount` 调 `resetPageHeader` 清理;未就绪(无结果表)时不注入。
|
||||||
|
- 清理:移除 `ratOptions` 及 `.cap-topbar/.cap-title/.cap-spark/.cap-sub/.cap-controls/.cap-refresh` 样式;`.cap-seg*` 保留(问题清单的筛选分段仍在用)。
|
||||||
|
- 验证:`frontend` `npm run build`(vue-tsc+vite) 通过。
|
||||||
|
|||||||
@@ -15,29 +15,6 @@
|
|||||||
|
|
||||||
<!-- 看板主体 -->
|
<!-- 看板主体 -->
|
||||||
<div v-else class="cap-body">
|
<div v-else class="cap-body">
|
||||||
<header class="cap-topbar">
|
|
||||||
<div class="cap-title-wrap">
|
|
||||||
<span class="cap-spark"></span>
|
|
||||||
<h2 class="cap-title">容量负荷看板</h2>
|
|
||||||
<span class="cap-sub">高负荷小区分析 · 实时数据</span>
|
|
||||||
</div>
|
|
||||||
<div class="cap-controls">
|
|
||||||
<div class="cap-seg">
|
|
||||||
<button
|
|
||||||
v-for="opt in ratOptions"
|
|
||||||
:key="opt.value"
|
|
||||||
type="button"
|
|
||||||
class="cap-seg-btn"
|
|
||||||
:class="{ active: rat === opt.value }"
|
|
||||||
@click="switchRat(opt.value)"
|
|
||||||
>{{ opt.label }}</button>
|
|
||||||
</div>
|
|
||||||
<button class="cap-refresh" type="button" :disabled="loadingOverview" title="刷新" @click="reload">
|
|
||||||
<n-icon><RefreshOutline /></n-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="cap-overview">
|
<div class="cap-overview">
|
||||||
<n-spin :show="loadingOverview">
|
<n-spin :show="loadingOverview">
|
||||||
<!-- 汇总卡片 -->
|
<!-- 汇总卡片 -->
|
||||||
@@ -207,7 +184,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, h, onMounted, reactive, ref, type Component } from 'vue';
|
import { computed, h, onBeforeUnmount, onMounted, reactive, ref, watch, type Component } from 'vue';
|
||||||
import { NTag, darkTheme, lightTheme, useMessage } from 'naive-ui';
|
import { NTag, darkTheme, lightTheme, useMessage } from 'naive-ui';
|
||||||
import type { DataTableColumns } from 'naive-ui';
|
import type { DataTableColumns } from 'naive-ui';
|
||||||
import type { EChartsOption } from 'echarts';
|
import type { EChartsOption } from 'echarts';
|
||||||
@@ -227,6 +204,7 @@ import {
|
|||||||
|
|
||||||
import { apiGet, downloadGet } from '../api/client';
|
import { apiGet, downloadGet } from '../api/client';
|
||||||
import { themeName } from '../composables/theme';
|
import { themeName } from '../composables/theme';
|
||||||
|
import { resetPageHeader, setPageHeader, type PageHeaderAction } from '../composables/pageHeader';
|
||||||
import EChart from './EChart.vue';
|
import EChart from './EChart.vue';
|
||||||
|
|
||||||
type Rat = '4g' | '5g';
|
type Rat = '4g' | '5g';
|
||||||
@@ -314,10 +292,6 @@ const chartHeight = 'clamp(108px, 13vh, 160px)';
|
|||||||
const loadingStatus = ref(true);
|
const loadingStatus = ref(true);
|
||||||
const ready = ref(false);
|
const ready = ref(false);
|
||||||
const rat = ref<Rat>('5g');
|
const rat = ref<Rat>('5g');
|
||||||
const ratOptions: { label: string; value: Rat }[] = [
|
|
||||||
{ label: '4G', value: '4g' },
|
|
||||||
{ label: '5G', value: '5g' }
|
|
||||||
];
|
|
||||||
|
|
||||||
const loadingOverview = ref(false);
|
const loadingOverview = ref(false);
|
||||||
const overview = ref<Overview | null>(null);
|
const overview = ref<Overview | null>(null);
|
||||||
@@ -668,6 +642,23 @@ async function openDetail(id: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------- 顶部框架页头(标题/制式切换/刷新) ----------
|
||||||
|
function buildHeaderActions(): PageHeaderAction[] {
|
||||||
|
return [
|
||||||
|
{ key: 'rat-4g', label: '4G', type: rat.value === '4g' ? 'primary' : 'default', variant: rat.value === '4g' ? 'solid' : 'outline', onClick: () => switchRat('4g') },
|
||||||
|
{ key: 'rat-5g', label: '5G', type: rat.value === '5g' ? 'primary' : 'default', variant: rat.value === '5g' ? 'solid' : 'outline', onClick: () => switchRat('5g') },
|
||||||
|
{ key: 'dashboard-refresh', label: '刷新', icon: RefreshOutline, variant: 'outline', loading: loadingOverview, disabled: loadingOverview, onClick: reload }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
function applyPageHeader() {
|
||||||
|
if (!ready.value) {
|
||||||
|
resetPageHeader();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPageHeader({ subtitle: '高负荷小区分析 · 实时数据', actions: buildHeaderActions() });
|
||||||
|
}
|
||||||
|
watch([rat, ready], applyPageHeader);
|
||||||
|
|
||||||
function switchRat(value: Rat) {
|
function switchRat(value: Rat) {
|
||||||
if (rat.value === value) return;
|
if (rat.value === value) return;
|
||||||
rat.value = value;
|
rat.value = value;
|
||||||
@@ -695,6 +686,7 @@ function reload() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(loadStatus);
|
onMounted(loadStatus);
|
||||||
|
onBeforeUnmount(resetPageHeader);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -792,24 +784,6 @@ onMounted(loadStatus);
|
|||||||
|
|
||||||
.cap-body { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 12px; }
|
.cap-body { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 12px; }
|
||||||
|
|
||||||
.cap-topbar {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
.cap-title-wrap { display: flex; align-items: center; gap: 12px; }
|
|
||||||
.cap-spark {
|
|
||||||
width: 6px; height: 24px; border-radius: 3px;
|
|
||||||
background: linear-gradient(180deg, #22d3ee, #818cf8);
|
|
||||||
box-shadow: 0 0 16px rgba(56, 189, 248, 0.7);
|
|
||||||
}
|
|
||||||
.cap-title { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: 1px; color: var(--cap-text-strong); }
|
|
||||||
.cap-sub { font-size: 12px; color: var(--cap-text-muted); }
|
|
||||||
.cap-controls { display: flex; align-items: center; gap: 12px; }
|
|
||||||
|
|
||||||
.cap-seg {
|
.cap-seg {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
@@ -836,16 +810,6 @@ onMounted(loadStatus);
|
|||||||
background: linear-gradient(135deg, #22d3ee, #38bdf8);
|
background: linear-gradient(135deg, #22d3ee, #38bdf8);
|
||||||
box-shadow: 0 0 18px rgba(56, 189, 248, 0.45);
|
box-shadow: 0 0 18px rgba(56, 189, 248, 0.45);
|
||||||
}
|
}
|
||||||
.cap-refresh {
|
|
||||||
display: inline-flex; align-items: center; justify-content: center;
|
|
||||||
width: 32px; height: 32px; border-radius: 9px;
|
|
||||||
border: 1px solid var(--cap-control-border);
|
|
||||||
background: var(--cap-control-bg);
|
|
||||||
color: var(--cap-text-sub); cursor: pointer; font-size: 16px;
|
|
||||||
transition: all 0.18s ease;
|
|
||||||
}
|
|
||||||
.cap-refresh:hover:not(:disabled) { color: #06b6d4; border-color: rgba(56, 189, 248, 0.5); box-shadow: 0 0 16px rgba(56, 189, 248, 0.3); }
|
|
||||||
.cap-refresh:disabled { opacity: 0.5; cursor: wait; }
|
|
||||||
|
|
||||||
/* 概览区(卡片 + 图表),固定高度,由 n-spin 包裹 */
|
/* 概览区(卡片 + 图表),固定高度,由 n-spin 包裹 */
|
||||||
.cap-overview { flex: 0 0 auto; }
|
.cap-overview { flex: 0 0 auto; }
|
||||||
|
|||||||
Reference in New Issue
Block a user