|
@@ -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%;
|
|
|
}
|
|
}
|