From 974c13543c4534d4783eb521a5ba2af68f8b007b Mon Sep 17 00:00:00 2001
From: wr_1023 <wyun001@163.com>
Date: 星期四, 03 九月 2026 21:51:13 +0800
Subject: [PATCH] 修复首页内容

---
 src/modules/helper/config.ts                       |   10 
 src/modules/report/views/screen/overview.vue       |    2 
 src/modules/report/views/screen/participation.vue  |    2 
 src/modules/report/views/screen/index.vue          |   12 
 src/plugins/github/config.ts                       |    6 
 src/modules/report/views/screen/carbon.vue         |    2 
 src/modules/report/views/screen/resource.vue       |    2 
 src/modules/report/data/screen.ts                  |   10 
 src/modules/report/components/screen-map-panel.vue |  267 ++++++++++++++++++++++
 src/modules/report/views/screen/operation.vue      |    2 
 src/modules/report/views/screen/map.vue            |  356 +++++++++++++++++++++++++++++
 11 files changed, 650 insertions(+), 21 deletions(-)

diff --git a/src/modules/helper/config.ts b/src/modules/helper/config.ts
index 504d8f2..fbdcab5 100644
--- a/src/modules/helper/config.ts
+++ b/src/modules/helper/config.ts
@@ -7,11 +7,11 @@
 			index: 'https://cool-js.com',
 			api: 'https://service.cool-js.com/api'
 		},
-		toolbar: {
-			h5: false,
-			order: 1,
-			component: import('./components/ai-code/btn.vue')
-		},
+		// toolbar: {
+		// 	h5: false,
+		// 	order: 1,
+		// 	component: import('./components/ai-code/btn.vue')
+		// },
 		pages: [
 			{
 				path: '/helper/ai-code',
diff --git a/src/modules/report/components/screen-map-panel.vue b/src/modules/report/components/screen-map-panel.vue
new file mode 100755
index 0000000..4a7969c
--- /dev/null
+++ b/src/modules/report/components/screen-map-panel.vue
@@ -0,0 +1,267 @@
+<template>
+	<div class="map-panel">
+		<!-- 椤堕儴锛氬浘灞傚垏鎹� + 瀹炴椂缁熻 -->
+		<div class="map-panel__bar">
+			<cl-select-button v-model="layer" :options="layerOptions" small />
+			<div class="map-panel__stat">
+				<span class="map-panel__pulse" :style="{ background: statColor }" />
+				<strong>{{ list.length }}</strong>
+				<span class="map-panel__unit">{{ statUnit }}</span>
+				<span class="map-panel__update">{{ lastUpdate }}</span>
+			</div>
+		</div>
+
+		<!-- 鍦板浘涓讳綋 -->
+		<div class="map-panel__map">
+			<tdt-map :center="center" :zoom="zoom" :controls="controls" :loadConfig="MAP_LOAD">
+				<tdt-marker
+					v-for="item in list"
+					:key="keyOf(item)"
+					:position="pos(item)"
+					:icon="iconOf(item)"
+					:title="nameOf(item)"
+				/>
+			</tdt-map>
+		</div>
+
+		<!-- 鍥句緥 -->
+		<div class="map-panel__legend">
+			<div v-for="l in activeLegends" :key="l.name" class="map-panel__legend-item">
+				<i :style="{ background: l.color }" />{{ l.name }}
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'screen-map-panel'
+});
+
+import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
+import { TdtMap, TdtMarker, type LngLat } from 'vue-tianditu2';
+import {
+	DEFAULT_CENTER,
+	MAP_LOAD,
+	SORTER_ICON,
+	VEHICLE_ICON,
+	formatTime,
+	siteIcon,
+	useMapsApi
+} from '../../maps/hooks/use-map';
+
+type LayerKey = 'site' | 'vehicle' | 'sorter';
+
+/** 涓夊浘灞傜粺涓�閰嶇疆锛氬彇鏁� / 鍞竴閿� / 鍚嶇О / 鍥炬爣 / 鍥句緥 */
+const LAYERS: Record<
+	LayerKey,
+	{
+		unit: string;
+		color: string;
+		load: () => Promise<any[]>;
+		key: (e: any) => string;
+		name: (e: any) => string;
+		icon: (e: any) => any;
+		legends: { name: string; color: string }[];
+	}
+> = {
+	site: {
+		unit: '涓珯鐐�',
+		color: '#16a34a',
+		load: () => api.sites(),
+		key: (e: any) => e.id,
+		name: (e: any) => e.businessName,
+		icon: (e: any) => siteIcon(e.siteType),
+		legends: [
+			{ name: '鏀堕泦鐐�', color: '#16a34a' },
+			{ name: '鍘嬬缉绔�', color: '#f59e0b' }
+		]
+	},
+	vehicle: {
+		unit: '杈嗗湪绾�',
+		color: '#7c3aed',
+		load: () => api.vehicleOnline(),
+		key: (e: any) => e.vehicleNo,
+		name: (e: any) => e.vehicleNo,
+		icon: () => VEHICLE_ICON,
+		legends: [{ name: '鍦ㄧ嚎杞﹁締', color: '#7c3aed' }]
+	},
+	sorter: {
+		unit: '浜哄湪绾�',
+		color: '#2563eb',
+		load: () => api.sorterOnline(),
+		key: (e: any) => e.idCard,
+		name: (e: any) => e.name || e.idCard,
+		icon: () => SORTER_ICON,
+		legends: [{ name: '鍦ㄧ嚎鍒嗘嫞鍛�', color: '#2563eb' }]
+	}
+};
+
+const api = useMapsApi();
+
+const layer = ref<LayerKey>('site');
+const list = ref<any[]>([]);
+const center = ref<LngLat>(DEFAULT_CENTER);
+const zoom = ref(12);
+const lastUpdate = ref('');
+
+const controls = ['Zoom', 'Scale'];
+
+const layerOptions = [
+	{ label: '绔欑偣', value: 'site' },
+	{ label: '杞﹁締', value: 'vehicle' },
+	{ label: '鍒嗘嫞鍛�', value: 'sorter' }
+];
+
+const cfg = computed(() => LAYERS[layer.value]);
+const activeLegends = computed(() => cfg.value.legends);
+const statColor = computed(() => cfg.value.color);
+const statUnit = computed(() => cfg.value.unit);
+
+function pos(item: any): LngLat {
+	return [Number(item.longitude), Number(item.latitude)];
+}
+function keyOf(item: any) {
+	return cfg.value.key(item);
+}
+function nameOf(item: any) {
+	return cfg.value.name(item);
+}
+function iconOf(item: any) {
+	return cfg.value.icon(item);
+}
+
+async function load() {
+	const res = await cfg.value.load();
+	list.value = Array.isArray(res) ? res : [];
+	lastUpdate.value = formatTime(new Date());
+	if (list.value[0]) {
+		center.value = pos(list.value[0]);
+		zoom.value = 13;
+	}
+}
+
+// 杞﹁締 / 鍒嗘嫞鍛樹负瀹炴椂浣嶇疆锛�30s 杞锛涚珯鐐规槸闈欐�佸彴璐︼紝涓嶈疆璇�
+let timer: number | undefined;
+function startPolling() {
+	stopPolling();
+	if (layer.value !== 'site') {
+		timer = window.setInterval(load, 30000);
+	}
+}
+function stopPolling() {
+	if (timer) {
+		clearInterval(timer);
+		timer = undefined;
+	}
+}
+
+watch(layer, () => {
+	load();
+	startPolling();
+});
+
+onMounted(async () => {
+	await load();
+	startPolling();
+});
+
+onBeforeUnmount(stopPolling);
+</script>
+
+<style lang="scss" scoped>
+.map-panel {
+	display: flex;
+	flex-direction: column;
+	height: 100%;
+	min-height: 0;
+
+	&__bar {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		gap: 12px;
+		padding: 0 4px 8px;
+		flex-shrink: 0;
+	}
+
+	&__stat {
+		display: flex;
+		align-items: baseline;
+		gap: 6px;
+		font-size: 13px;
+		color: var(--el-text-color-secondary);
+
+		strong {
+			font-size: 22px;
+			font-weight: 700;
+			color: var(--el-text-color-primary);
+		}
+	}
+
+	&__pulse {
+		width: 8px;
+		height: 8px;
+		border-radius: 50%;
+		align-self: center;
+		animation: map-pulse 1.6s ease-in-out infinite;
+	}
+
+	&__unit {
+		font-size: 12px;
+	}
+
+	&__update {
+		margin-left: 4px;
+		font-size: 11px;
+		color: var(--el-text-color-secondary);
+		opacity: 0.75;
+	}
+
+	&__map {
+		flex: 1;
+		min-height: 0;
+		border-radius: 8px;
+		overflow: hidden;
+
+		> * {
+			width: 100%;
+			height: 100%;
+		}
+	}
+
+	&__legend {
+		display: flex;
+		align-items: center;
+		gap: 16px;
+		padding-top: 8px;
+		flex-shrink: 0;
+	}
+
+	&__legend-item {
+		display: flex;
+		align-items: center;
+		gap: 6px;
+		font-size: 12px;
+		color: var(--el-text-color-secondary);
+
+		i {
+			width: 8px;
+			height: 8px;
+			border-radius: 50%;
+		}
+	}
+}
+
+@keyframes map-pulse {
+	0%,
+	100% {
+		opacity: 1;
+		transform: scale(1);
+	}
+	50% {
+		opacity: 0.4;
+		transform: scale(0.75);
+	}
+}
+</style>
diff --git a/src/modules/report/data/screen.ts b/src/modules/report/data/screen.ts
index a8b56f3..8b74804 100644
--- a/src/modules/report/data/screen.ts
+++ b/src/modules/report/data/screen.ts
@@ -1,5 +1,5 @@
 export type TimeRange = 'today' | 'week' | 'month' | 'quarter' | 'year';
-export type ScreenKey = 'overview' | 'resource' | 'carbon' | 'participation' | 'operation';
+export type ScreenKey = 'overview' | 'resource' | 'carbon' | 'participation' | 'operation' | 'map';
 export type RankMetric = 'weight' | 'people' | 'pointPerCapita' | 'weightPerCapita' | 'carbon';
 
 /** 棣栭〉鑿滃崟鍥哄畾涓� /report/screen/index锛屽瓙椤甸�氳繃 ?page= 鍒囨崲锛岄伩鍏嶇寮�棣栭〉鍏ュ彛 */
@@ -10,14 +10,16 @@
 	{ key: 'resource', index: '02', label: '鍨冨溇璧勬簮鍖栧垎鏋�', audience: '鍚庡嫟 / 鐜繚' },
 	{ key: 'carbon', index: '03', label: '纰冲噺鎺掍笌缁胯壊璐$尞', audience: '鍙岀⒊ / ESG' },
 	{ key: 'participation', index: '04', label: '甯堢敓鍙備笌涓庣⒊绉垎', audience: '瀛﹀伐 / 鍥㈠' },
-	{ key: 'operation', index: '05', label: '鍥炴敹杩愯惀鐩戞帶', audience: '杩愯惀鍏徃' }
+	{ key: 'operation', index: '05', label: '鍥炴敹杩愯惀鐩戞帶', audience: '杩愯惀鍏徃' },
+	{ key: 'map', index: '06', label: '鍦扮悊淇℃伅鎬昏', audience: '鎸囨尌涓績' }
 ];
 
 /** 浠庤矾鐢辫В鏋愬綋鍓嶅ぇ灞忓瓙椤碉紱index / 绌� / 闈炴硶鍊煎潎鍥炶惤鍒� overview */
 export function resolveScreenKey(page?: string | string[] | null, queryPage?: string | string[] | null): ScreenKey {
 	const raw = String(queryPage || page || 'overview');
-	if (raw === 'index') return 'overview';
-	return SCREEN_NAV.some(e => e.key === raw) ? (raw as ScreenKey) : 'overview';
+	if (raw === 'index') return 'map';	//if (raw === 'index') return 'overview';
+	return SCREEN_NAV.some(e => e.key === raw) ? (raw as ScreenKey) : 'map';
+	//return SCREEN_NAV.some(e => e.key === raw) ? (raw as ScreenKey) : 'overview';
 }
 
 export const TIME_RANGE_OPTIONS: { label: string; value: TimeRange }[] = [
diff --git a/src/modules/report/views/screen/carbon.vue b/src/modules/report/views/screen/carbon.vue
index 8c06102..afd9980 100644
--- a/src/modules/report/views/screen/carbon.vue
+++ b/src/modules/report/views/screen/carbon.vue
@@ -146,7 +146,7 @@
 </script>
 
 <style lang="scss" scoped>
-@import '../../styles/screen-kpi.scss';
+@use '../../styles/screen-kpi' as *;
 
 .screen-page {
 	.mid-row {
diff --git a/src/modules/report/views/screen/index.vue b/src/modules/report/views/screen/index.vue
index cd0b453..2bda8a0 100644
--- a/src/modules/report/views/screen/index.vue
+++ b/src/modules/report/views/screen/index.vue
@@ -12,6 +12,7 @@
 import { computed } from 'vue';
 import { useRoute } from 'vue-router';
 import ScreenShell from '../../components/screen-shell.vue';
+import Map from './map.vue';
 import Overview from './overview.vue';
 import Resource from './resource.vue';
 import Carbon from './carbon.vue';
@@ -29,11 +30,14 @@
 	resource: Resource,
 	carbon: Carbon,
 	participation: Participation,
-	operation: Operation
+	operation: Operation,
+	map: Map
 };
 
 /** 鍋滅暀鍦� /report/screen/index锛岀敤 query.page锛堟垨鍔ㄦ�佽矾鐢� :page锛夊垏鎹㈠瓙椤碉紝涓嶅啀寮哄埗璺宠蛋 */
-const currentKey = computed<ScreenKey>(() =>
-	resolveScreenKey(route.params.page as string, route.query.page as string)
-);
+const currentKey = computed<ScreenKey>(() =>{
+	const key = resolveScreenKey(route.params.page as string, route.query.page as string);
+	console.log('[screen] params.page=', route.params.page, 'query.page=', route.query.page, '=> key=', key);
+	return key;
+});
 </script>
diff --git a/src/modules/report/views/screen/map.vue b/src/modules/report/views/screen/map.vue
new file mode 100755
index 0000000..cd966e8
--- /dev/null
+++ b/src/modules/report/views/screen/map.vue
@@ -0,0 +1,356 @@
+<template>
+	<div class="screen-page dash">
+		<!-- KPI 鎸囨爣鏉� -->
+		<div class="kpi-grid" style="--kpi-cols: 8">
+			<kpi-card v-for="item in kpis" :key="item.label" v-bind="item" />
+		</div>
+
+		<!-- 涓讳綋涓夋爮锛氬乏鍥捐〃 / 涓湴鍥� / 鍙冲浘琛� -->
+		<div class="dash-body">
+			<!-- 宸︽爮 -->
+			<div class="dash-col dash-col--left">
+				<panel-card
+					title="鍨冨溇鍒嗙被缁撴瀯"
+					:demo="isDemoSection('wasteMix')"
+					class="dash-card dash-card--flex"
+				>
+					<template #extra>
+						<cl-select-button v-model="mixMetric" :options="mixOptions" small />
+					</template>
+					<v-chart :option="mixOption" autoresize class="chart" />
+				</panel-card>
+
+				<panel-card
+					title="纰冲噺鎺掓潵婧�"
+					:demo="isDemoSection('recycleItems')"
+					class="dash-card dash-card--flex"
+				>
+					<v-chart :option="carbonOption" autoresize class="chart" />
+				</panel-card>
+			</div>
+
+			<!-- 涓爮锛氬湴鍥句富瑙嗚 -->
+			<div class="dash-col dash-col--center">
+				<panel-card title="鍏ㄥ煙鎬佸娍涓�寮犲浘" sub="绔欑偣 / 杞﹁締 / 鍒嗘嫞鍛樺疄鏃跺垎甯�" class="dash-card dash-card--map">
+					<screen-map-panel />
+				</panel-card>
+			</div>
+
+			<!-- 鍙虫爮 -->
+			<div class="dash-col dash-col--right">
+				<panel-card
+					title="鍥炴敹瓒嬪娍"
+					:demo="isDemoSection('trend')"
+					class="dash-card dash-card--flex"
+				>
+					<template #extra>
+						<cl-select-button v-model="trendMetric" :options="trendOptions" small />
+					</template>
+					<v-chart :option="trendOption" autoresize class="chart" />
+				</panel-card>
+
+				<panel-card
+					title="鍚勭被鍥炴敹閲�"
+					:demo="isDemoSection('recycleItems')"
+					class="dash-card dash-card--flex"
+				>
+					<template #extra>
+						<cl-select-button v-model="itemMetric" :options="itemOptions" small />
+					</template>
+					<v-chart :option="itemOption" autoresize class="chart" />
+				</panel-card>
+			</div>
+		</div>
+
+		<!-- 搴曢儴锛氱幇鍦虹洃鎺� -->
+		<div class="dash-foot">
+			<panel-card title="鐜板満鐩戞帶" sub="閲嶇偣绔欑偣瀹炴椂鐢婚潰锛岄甯у悗鏆傚仠鎷夋祦浠ヨ妭鐪佸甫瀹�">
+				<template #extra>
+					<router-link class="camera-more" to="/monitor/device">鏇村</router-link>
+				</template>
+				<CameraMonitor :limit="6" />
+			</panel-card>
+		</div>
+	</div>
+</template>
+
+<script lang="ts" setup>
+defineOptions({
+	name: 'report-screen-map'
+});
+
+import { computed, ref } from 'vue';
+import KpiCard from '../../components/kpi-card.vue';
+import PanelCard from '../../components/panel-card.vue';
+import ScreenMapPanel from '../../components/screen-map-panel.vue';
+import CameraMonitor from '../../../monitor/views/cameras.vue';
+import { useChartTheme, useScreenData, useScreenFieldSource } from '../../hooks/use-screen';
+
+const { data } = useScreenData();
+const { isDemoSection } = useScreenFieldSource();
+const { textColor, mutedColor, splitColor, primary, baseGrid, axisStyle, tooltip } = useChartTheme();
+
+const mixMetric = ref<'weight' | 'value' | 'count'>('weight');
+const trendMetric = ref<'weight' | 'value' | 'carbon'>('weight');
+const itemMetric = ref<'weight' | 'value' | 'carbon'>('weight');
+
+const mixOptions = [
+	{ label: '閲嶉噺', value: 'weight' },
+	{ label: '浠峰��', value: 'value' },
+	{ label: '鏁伴噺', value: 'count' }
+];
+
+const trendOptions = [
+	{ label: '閲嶉噺', value: 'weight' },
+	{ label: '浠峰��', value: 'value' },
+	{ label: '纰冲噺鎺�', value: 'carbon' }
+];
+
+const itemOptions = [
+	{ label: '閲嶉噺', value: 'weight' },
+	{ label: '浠峰��', value: 'value' },
+	{ label: '纰冲噺鎺�', value: 'carbon' }
+];
+
+/** 椤堕儴 KPI锛氬彇鎬昏鍩熸牳蹇� 8 椤� */
+const kpis = computed(() => [
+	{ field: 'kpis.todayWeightKg', label: '浠婃棩鍥炴敹閲�', value: data.value.kpis.todayWeightKg, suffix: 'kg', fixed: 1 },
+	{ field: 'kpis.periodWeightTon', label: '鏈尯闂村洖鏀堕噺', value: data.value.kpis.periodWeightTon, suffix: '鍚�', fixed: 2 },
+	{ field: 'kpis.resourceRate', label: '璧勬簮鍖栫巼', value: data.value.kpis.resourceRate, suffix: '%', fixed: 1 },
+	{ field: 'kpis.periodValue', label: '鍥炴敹浠峰��', value: data.value.kpis.periodValue, suffix: '鍏�', fixed: 0 },
+	{ field: 'kpis.periodCarbonTon', label: '纰冲噺鎺�', value: data.value.kpis.periodCarbonTon, suffix: '鍚–O鈧俥', fixed: 2 },
+	{ field: 'kpis.totalWeightTon', label: '绱鍥炴敹閲�', value: data.value.kpis.totalWeightTon, suffix: '鍚�', fixed: 1 },
+	{ field: 'kpis.totalParticipants', label: '绱鍙備笌浜烘暟', value: data.value.kpis.totalParticipants, suffix: '浜�', fixed: 0 },
+	{ field: 'kpis.totalPoints', label: '绱纰崇Н鍒�', value: data.value.kpis.totalPoints, suffix: '鍒�', fixed: 0 }
+]);
+
+const trendUnit = computed(() => {
+	if (trendMetric.value === 'value') return '鍏�';
+	if (trendMetric.value === 'carbon') return 'kgCO鈧俥';
+	return 'kg';
+});
+
+const itemUnit = computed(() => {
+	if (itemMetric.value === 'value') return '鍏�';
+	if (itemMetric.value === 'carbon') return 'kgCO鈧俥';
+	return 'kg';
+});
+
+/** 鍨冨溇鍒嗙被缁撴瀯锛堥ゼ锛� */
+const mixOption = computed(() => ({
+	tooltip: { trigger: 'item', formatter: '{b}<br/>{c} ({d}%)', ...tooltip() },
+	series: [
+		{
+			type: 'pie',
+			radius: ['38%', '62%'],
+			center: ['50%', '50%'],
+			padAngle: 3,
+			itemStyle: { borderRadius: 6 },
+			label: { color: textColor.value, formatter: '{b}\n{d}%' },
+			data: data.value.wasteMix.map(e => ({
+				name: e.name,
+				value: e[mixMetric.value],
+				itemStyle: { color: e.color }
+			}))
+		}
+	]
+}));
+
+/** 纰冲噺鎺掓潵婧愶紙楗硷級 */
+const carbonOption = computed(() => ({
+	tooltip: { trigger: 'item', formatter: '{b}<br/>{c} kgCO鈧俥 ({d}%)', ...tooltip() },
+	series: [
+		{
+			type: 'pie',
+			radius: ['36%', '60%'],
+			center: ['50%', '50%'],
+			padAngle: 3,
+			itemStyle: { borderRadius: 6 },
+			label: { color: textColor.value, formatter: '{b}\n{d}%' },
+			data: data.value.recycleItems.map(e => ({
+				name: e.name + '鍥炴敹',
+				value: e.carbon,
+				itemStyle: { color: e.color }
+			}))
+		}
+	]
+}));
+
+/** 鍥炴敹瓒嬪娍锛堟姌绾匡級 */
+const trendOption = computed(() => ({
+	grid: baseGrid(),
+	tooltip: { trigger: 'axis', ...tooltip() },
+	xAxis: { type: 'category', data: data.value.axis, ...axisStyle() },
+	yAxis: {
+		type: 'value',
+		name: trendUnit.value,
+		nameTextStyle: { color: mutedColor.value },
+		splitLine: { lineStyle: { color: splitColor.value, type: 'dashed' } },
+		axisLabel: { color: mutedColor.value },
+		axisLine: { show: false },
+		axisTick: { show: false }
+	},
+	series: [
+		{
+			type: 'line',
+			smooth: true,
+			showSymbol: false,
+			data: data.value.trend[trendMetric.value],
+			itemStyle: { color: primary.value },
+			areaStyle: { color: primary.value, opacity: 0.12 }
+		}
+	]
+}));
+
+/** 鍚勭被鍥炴敹閲忥紙妯悜鏉″舰锛� */
+const itemOption = computed(() => {
+	// 鍙嶈浆璁╂暟鍊兼渶澶х殑鎺掑湪椤堕儴
+	const rows = [...data.value.recycleItems].reverse();
+	return {
+		grid: { containLabel: true, left: 8, right: 24, top: 12, bottom: 8 },
+		tooltip: {
+			trigger: 'axis',
+			axisPointer: { type: 'shadow' },
+			valueFormatter: (v: number) => `${Number(v || 0).toLocaleString()} ${itemUnit.value}`,
+			...tooltip()
+		},
+		xAxis: {
+			type: 'value',
+			name: itemUnit.value,
+			nameTextStyle: { color: mutedColor.value },
+			splitLine: { lineStyle: { color: splitColor.value, type: 'dashed' } },
+			axisLabel: { color: mutedColor.value },
+			axisLine: { show: false },
+			axisTick: { show: false }
+		},
+		yAxis: {
+			type: 'category',
+			data: rows.map(e => e.name),
+			axisLabel: { color: textColor.value },
+			axisLine: { show: false },
+			axisTick: { show: false }
+		},
+		series: [
+			{
+				type: 'bar',
+				barWidth: 14,
+				data: rows.map(e => Number(e[itemMetric.value] || 0)),
+				itemStyle: {
+					borderRadius: [0, 8, 8, 0],
+					// 娌跨敤鍚勭被鑷韩閰嶈壊锛屼笌宸︿晶纰冲噺鎺掗ゼ鍥捐瑙変竴鑷�
+					color: (params: any) => rows[params.dataIndex]?.color || primary.value
+				}
+			}
+		]
+	};
+});
+</script>
+
+<style lang="scss" scoped>
+@use '../../styles/screen-kpi' as *;
+
+.dash {
+	display: flex;
+	flex-direction: column;
+	gap: 10px;
+	height: 100%;
+	min-height: 0;
+	overflow: auto;
+
+	/* 涓讳綋涓夋爮 */
+	&-body {
+		display: grid;
+		grid-template-columns: 24% 46% 30%;
+		gap: 10px;
+		flex: 1;
+		min-height: 420px;
+	}
+
+	&-col {
+		display: flex;
+		flex-direction: column;
+		gap: 10px;
+		min-width: 0;
+		min-height: 0;
+	}
+
+	&-card {
+		min-height: 0;
+
+		/* 宸﹀彸鏍忓浘琛ㄥ崱鍧囧垎楂樺害 */
+		&--flex {
+			flex: 1;
+			display: flex;
+			flex-direction: column;
+
+			:deep(.panel-card__body) {
+				flex: 1;
+				min-height: 0;
+			}
+		}
+
+		&--map {
+			flex: 1;
+			display: flex;
+			flex-direction: column;
+
+			:deep(.panel-card__body) {
+				flex: 1;
+				min-height: 0;
+				padding: 10px;
+			}
+		}
+	}
+
+	/* 搴曢儴鐩戞帶锛氬帇缂╁悗缁� CameraMonitor 涓�涓揣鍑戦珮搴� */
+	&-foot {
+		flex-shrink: 0;
+		display: flex;
+		flex-direction: column;
+		height: clamp(200px, 20vh, 260px);
+
+		:deep(.panel-card) {
+			flex: 1;
+			display: flex;
+			flex-direction: column;
+			min-height: 0;
+		}
+
+		:deep(.panel-card__head) {
+			padding-top: 6px;
+			padding-bottom: 6px;
+		}
+
+		:deep(.panel-card__body) {
+			flex: 1;
+			min-height: 0;
+			overflow: hidden;
+			padding: 6px;
+		}
+
+		.camera-more {
+			font-size: 12px;
+			color: var(--el-color-primary);
+			text-decoration: none;
+		}
+	}
+
+	.chart {
+		height: 100%;
+		min-height: 180px;
+		width: 100%;
+	}
+}
+
+/* 绐勫睆鍏滃簳锛氫笁鏍忔敼鍗曟爮绾靛悜 */
+@media (max-width: 1200px) {
+	.dash-body {
+		grid-template-columns: 1fr;
+		min-height: 0;
+
+		.dash-col--center {
+			min-height: 420px;
+		}
+	}
+}
+</style>
\ No newline at end of file
diff --git a/src/modules/report/views/screen/operation.vue b/src/modules/report/views/screen/operation.vue
index 8356869..a75df31 100644
--- a/src/modules/report/views/screen/operation.vue
+++ b/src/modules/report/views/screen/operation.vue
@@ -168,7 +168,7 @@
 </script>
 
 <style lang="scss" scoped>
-@import '../../styles/screen-kpi.scss';
+@use '../../styles/screen-kpi' as *;
 
 .screen-page {
 	.mid-row {
diff --git a/src/modules/report/views/screen/overview.vue b/src/modules/report/views/screen/overview.vue
index 5227902..c35bd22 100644
--- a/src/modules/report/views/screen/overview.vue
+++ b/src/modules/report/views/screen/overview.vue
@@ -169,7 +169,7 @@
 </script>
 
 <style lang="scss" scoped>
-@import '../../styles/screen-kpi.scss';
+@use '../../styles/screen-kpi' as *;
 
 .screen-page {
 	.charts-row,
diff --git a/src/modules/report/views/screen/participation.vue b/src/modules/report/views/screen/participation.vue
index aeba5a7..5b839c9 100644
--- a/src/modules/report/views/screen/participation.vue
+++ b/src/modules/report/views/screen/participation.vue
@@ -179,7 +179,7 @@
 </script>
 
 <style lang="scss" scoped>
-@import '../../styles/screen-kpi.scss';
+@use '../../styles/screen-kpi' as *;
 
 .screen-page {
 	.mid-row {
diff --git a/src/modules/report/views/screen/resource.vue b/src/modules/report/views/screen/resource.vue
index 61364cd..eefd114 100644
--- a/src/modules/report/views/screen/resource.vue
+++ b/src/modules/report/views/screen/resource.vue
@@ -129,7 +129,7 @@
 </script>
 
 <style lang="scss" scoped>
-@import '../../styles/screen-kpi.scss';
+@use '../../styles/screen-kpi' as *;
 
 .screen-page {
 	.mid-row {
diff --git a/src/plugins/github/config.ts b/src/plugins/github/config.ts
index 200ba8f..9eb3e39 100644
--- a/src/plugins/github/config.ts
+++ b/src/plugins/github/config.ts
@@ -2,8 +2,8 @@
 
 export default (): ModuleConfig => {
 	return {
-		toolbar: {
-			component: import('./components/code.vue')
-		}
+		// toolbar: {
+		// 	component: import('./components/code.vue')
+		// }
 	};
 };

--
Gitblit v1.9.1