Browse Source

fix(big-screen): 全屏后 UI 自适应 100vh

- 容器从 min-height: 100vh 改为 width: 100vw + height: 100vh + overflow: hidden
- bs-header / bs-cards-row 用 flex: 0 0 auto(固定高度,不挤压)
- bs-row 改为 flex: 1 1 0 自适应剩余空间,panel 和 chart 同步 flex
- 替换硬编码 height: 320px 为 min-height: 280px
- 面板/标题 min-height: 0 允许内部 flex:1 chart 撑满
- ECharts grid 增加 containLabel: true 让 x/y 轴标签完整显示
- ResizeObserver 监听容器尺寸变化触发 echarts.resize,支持视口/全屏切换后自适应
- fullscreenchange 触发后延迟 100ms resize,处理布局稳定后绘制
yangjingjing 1 day ago
parent
commit
90873856f3
1 changed files with 69 additions and 36 deletions
  1. 69 36
      src/views/risk/bigScreen/index.vue

+ 69 - 36
src/views/risk/bigScreen/index.vue

@@ -329,7 +329,7 @@ function renderPersonnelPie(data: { name: string; value: number }[]) {
     },
     },
     legend: {
     legend: {
       type: 'scroll',
       type: 'scroll',
-      bottom: 0,
+      bottom: 4,
       left: 'center',
       left: 'center',
       textStyle: { color: '#aac8ff', fontSize: 12 },
       textStyle: { color: '#aac8ff', fontSize: 12 },
       itemWidth: 10,
       itemWidth: 10,
@@ -339,8 +339,8 @@ function renderPersonnelPie(data: { name: string; value: number }[]) {
     series: [
     series: [
       {
       {
         type: 'pie',
         type: 'pie',
-        radius: ['50%', '75%'],
-        center: ['50%', '42%'],
+        radius: ['45%', '70%'],
+        center: ['50%', '45%'],
         avoidLabelOverlap: true,
         avoidLabelOverlap: true,
         itemStyle: { borderColor: '#0a1a3a', borderWidth: 2 },
         itemStyle: { borderColor: '#0a1a3a', borderWidth: 2 },
         label: { show: false },
         label: { show: false },
@@ -366,7 +366,7 @@ function renderAccidentPie(data: { name: string; value: number }[]) {
     },
     },
     legend: {
     legend: {
       type: 'scroll',
       type: 'scroll',
-      bottom: 0,
+      bottom: 4,
       left: 'center',
       left: 'center',
       textStyle: { color: '#aac8ff', fontSize: 12 }
       textStyle: { color: '#aac8ff', fontSize: 12 }
     },
     },
@@ -374,8 +374,8 @@ function renderAccidentPie(data: { name: string; value: number }[]) {
     series: [
     series: [
       {
       {
         type: 'pie',
         type: 'pie',
-        radius: ['50%', '75%'],
-        center: ['50%', '42%'],
+        radius: ['45%', '70%'],
+        center: ['50%', '45%'],
         avoidLabelOverlap: true,
         avoidLabelOverlap: true,
         itemStyle: { borderColor: '#0a1a3a', borderWidth: 2 },
         itemStyle: { borderColor: '#0a1a3a', borderWidth: 2 },
         label: { show: false },
         label: { show: false },
@@ -404,7 +404,7 @@ function renderTrendLine(dates: string[], quality: number[], risk: number[]) {
       right: 16,
       right: 16,
       textStyle: { color: '#aac8ff' }
       textStyle: { color: '#aac8ff' }
     },
     },
-    grid: { left: 40, right: 24, top: 40, bottom: 30 },
+    grid: { left: 50, right: 28, top: 44, bottom: 36, containLabel: true },
     xAxis: {
     xAxis: {
       type: 'category',
       type: 'category',
       data: dates,
       data: dates,
@@ -468,7 +468,7 @@ function renderComplaintBar(months: string[], counts: number[]) {
       borderColor: '#3a8dff',
       borderColor: '#3a8dff',
       textStyle: { color: '#fff' }
       textStyle: { color: '#fff' }
     },
     },
-    grid: { left: 40, right: 16, top: 30, bottom: 30 },
+    grid: { left: 50, right: 20, top: 32, bottom: 36, containLabel: true },
     xAxis: {
     xAxis: {
       type: 'category',
       type: 'category',
       data: months,
       data: months,
@@ -499,6 +499,8 @@ function renderComplaintBar(months: string[], counts: number[]) {
 }
 }
 
 
 // ===== 生命周期 =====
 // ===== 生命周期 =====
+let ro: ResizeObserver | null = null;
+
 onBeforeUnmount(() => {
 onBeforeUnmount(() => {
   if (timer) {
   if (timer) {
     clearInterval(timer);
     clearInterval(timer);
@@ -510,6 +512,11 @@ onBeforeUnmount(() => {
   }
   }
   if (resizeHandler) {
   if (resizeHandler) {
     window.removeEventListener('resize', resizeHandler);
     window.removeEventListener('resize', resizeHandler);
+    resizeHandler = undefined;
+  }
+  if (ro) {
+    ro.disconnect();
+    ro = null;
   }
   }
   document.removeEventListener('fullscreenchange', onFullscreenChange);
   document.removeEventListener('fullscreenchange', onFullscreenChange);
   document.removeEventListener('keydown', onKeyDown);
   document.removeEventListener('keydown', onKeyDown);
@@ -521,8 +528,13 @@ onBeforeUnmount(() => {
 
 
 // ===== 浏览器全屏监听 + ESC 退出 =====
 // ===== 浏览器全屏监听 + ESC 退出 =====
 function onFullscreenChange() {
 function onFullscreenChange() {
-  // ESC 退出全屏时只更新状态,不做其他动作
-  // 监听浏览器原生全屏状态变化,留作扩展点
+  // ESC 退出全屏时全屏模式切换后立即 resize 所有 chart
+  setTimeout(() => {
+    personnelPieChart?.resize();
+    accidentPieChart?.resize();
+    trendLineChart?.resize();
+    complaintBarChart?.resize();
+  }, 100);
 }
 }
 
 
 function onKeyDown(e: KeyboardEvent) {
 function onKeyDown(e: KeyboardEvent) {
@@ -532,50 +544,66 @@ function onKeyDown(e: KeyboardEvent) {
   // F11 由浏览器原生处理
   // F11 由浏览器原生处理
 }
 }
 
 
+function resizeAllCharts() {
+  personnelPieChart?.resize();
+  accidentPieChart?.resize();
+  trendLineChart?.resize();
+  complaintBarChart?.resize();
+}
+
 onMounted(() => {
 onMounted(() => {
   loadAll();
   loadAll();
   tickClock();
   tickClock();
   timer = window.setInterval(tickClock, 1000);
   timer = window.setInterval(tickClock, 1000);
   streamTimer = window.setInterval(loadStream, 30000);
   streamTimer = window.setInterval(loadStream, 30000);
 
 
-  resizeHandler = () => {
-    personnelPieChart?.resize();
-    accidentPieChart?.resize();
-    trendLineChart?.resize();
-    complaintBarChart?.resize();
-  };
+  resizeHandler = resizeAllCharts;
   window.addEventListener('resize', resizeHandler);
   window.addEventListener('resize', resizeHandler);
   document.addEventListener('fullscreenchange', onFullscreenChange);
   document.addEventListener('fullscreenchange', onFullscreenChange);
   document.addEventListener('keydown', onKeyDown);
   document.addEventListener('keydown', onKeyDown);
 
 
+  // ResizeObserver: 监听容器尺寸自适应变化(视口/全屏切换时)
   setTimeout(() => {
   setTimeout(() => {
-    personnelPieChart?.resize();
-    accidentPieChart?.resize();
-    trendLineChart?.resize();
-    complaintBarChart?.resize();
-  }, 200);
+    if (typeof ResizeObserver !== 'undefined') {
+      ro = new ResizeObserver(() => {
+        // 防止高频调用, 加 rAF 节流
+        requestAnimationFrame(resizeAllCharts);
+      });
+      if (personnelPieRef.value) ro.observe(personnelPieRef.value);
+      if (accidentPieRef.value) ro.observe(accidentPieRef.value);
+      if (trendLineRef.value) ro.observe(trendLineRef.value);
+      if (complaintBarRef.value) ro.observe(complaintBarRef.value);
+    }
+  }, 0);
+
+  // 连续多次 resize,确保 echarts 在自适应布局稳定后绘制正确
+  setTimeout(resizeAllCharts, 50);
+  setTimeout(resizeAllCharts, 200);
+  setTimeout(resizeAllCharts, 600);
 });
 });
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
+// 100vh 全屏自适应: 大屏容器占满视口,内部分区用 calc 或 flex 自适应
 .big-screen {
 .big-screen {
-  width: 100%;
-  min-height: 100vh;
+  width: 100vw;
+  height: 100vh;
   background: radial-gradient(ellipse at top, #0a1a3a 0%, #050b1f 60%, #02060f 100%);
   background: radial-gradient(ellipse at top, #0a1a3a 0%, #050b1f 60%, #02060f 100%);
   color: #e6f0ff;
   color: #e6f0ff;
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
-  padding: 16px;
+  padding: 14px;
   box-sizing: border-box;
   box-sizing: border-box;
+  overflow: hidden;
   font-family: 'Microsoft YaHei', 'PingFang SC', Arial, sans-serif;
   font-family: 'Microsoft YaHei', 'PingFang SC', Arial, sans-serif;
 }
 }
 
 
 // ===== 顶部标题栏 =====
 // ===== 顶部标题栏 =====
 .bs-header {
 .bs-header {
-  height: 60px;
+  flex: 0 0 60px;
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
-  margin-bottom: 16px;
+  margin-bottom: 14px;
   padding: 0 16px;
   padding: 0 16px;
   position: relative;
   position: relative;
 
 
@@ -628,23 +656,24 @@ onMounted(() => {
 
 
 // ===== 卡片行 =====
 // ===== 卡片行 =====
 .bs-cards-row {
 .bs-cards-row {
+  flex: 0 0 auto;
   display: grid;
   display: grid;
   grid-template-columns: repeat(6, 1fr);
   grid-template-columns: repeat(6, 1fr);
   gap: 12px;
   gap: 12px;
-  margin-bottom: 16px;
+  margin-bottom: 14px;
 }
 }
 
 
 .bs-card {
 .bs-card {
   background: linear-gradient(135deg, rgba(20, 40, 80, 0.7), rgba(10, 26, 58, 0.5));
   background: linear-gradient(135deg, rgba(20, 40, 80, 0.7), rgba(10, 26, 58, 0.5));
   border: 1px solid rgba(58, 141, 255, 0.25);
   border: 1px solid rgba(58, 141, 255, 0.25);
   border-radius: 6px;
   border-radius: 6px;
-  padding: 14px 16px;
+  padding: 12px 14px;
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
-  gap: 8px;
+  gap: 6px;
   position: relative;
   position: relative;
   overflow: hidden;
   overflow: hidden;
-  min-height: 110px;
+  min-height: 100px;
 
 
   &::before {
   &::before {
     content: '';
     content: '';
@@ -708,35 +737,39 @@ onMounted(() => {
 .bs-row {
 .bs-row {
   display: grid;
   display: grid;
   gap: 12px;
   gap: 12px;
-  margin-bottom: 16px;
+  margin-bottom: 14px;
+  flex: 1 1 0;
+  min-height: 0;
 }
 }
 
 
 .bs-row-charts {
 .bs-row-charts {
   grid-template-columns: 1fr 1fr 1.4fr;
   grid-template-columns: 1fr 1fr 1.4fr;
-  height: 320px;
+  min-height: 280px;
 }
 }
 
 
 .bs-row-bottom {
 .bs-row-bottom {
   grid-template-columns: 1.4fr 1fr;
   grid-template-columns: 1.4fr 1fr;
-  height: 320px;
+  min-height: 260px;
 }
 }
 
 
 .bs-panel {
 .bs-panel {
   background: linear-gradient(180deg, rgba(20, 40, 80, 0.5), rgba(10, 26, 58, 0.5));
   background: linear-gradient(180deg, rgba(20, 40, 80, 0.5), rgba(10, 26, 58, 0.5));
   border: 1px solid rgba(58, 141, 255, 0.25);
   border: 1px solid rgba(58, 141, 255, 0.25);
   border-radius: 6px;
   border-radius: 6px;
-  padding: 12px 16px;
+  padding: 10px 14px;
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
   min-width: 0;
   min-width: 0;
+  min-height: 0;
   overflow: hidden;
   overflow: hidden;
 }
 }
 
 
 .bs-panel-title {
 .bs-panel-title {
+  flex: 0 0 auto;
   font-size: 15px;
   font-size: 15px;
   font-weight: 600;
   font-weight: 600;
   color: #aac8ff;
   color: #aac8ff;
-  padding-bottom: 8px;
+  padding-bottom: 6px;
   border-bottom: 1px solid rgba(58, 141, 255, 0.2);
   border-bottom: 1px solid rgba(58, 141, 255, 0.2);
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
@@ -753,7 +786,7 @@ onMounted(() => {
 }
 }
 
 
 .bs-chart {
 .bs-chart {
-  flex: 1;
+  flex: 1 1 0;
   min-height: 0;
   min-height: 0;
   width: 100%;
   width: 100%;
 }
 }