From 7b2f826b63591021166ddd29abf768ff4e283a22 Mon Sep 17 00:00:00 2001 From: LiuJiaNan <15703339975@163.com> Date: Thu, 23 Jul 2026 15:23:09 +0800 Subject: [PATCH] =?UTF-8?q?refactor(map):=20branchOffice=20=E6=89=80?= =?UTF-8?q?=E6=9C=89=E7=BB=9F=E8=AE=A1=E9=A1=B5=20=E6=8C=89=E5=9D=97?= =?UTF-8?q?=E6=8B=86=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- .../FengBi/ClosedAreaStatsPanel/index.js | 67 +++++ .../FengBi/ClosedAreaStatsPanel/index.less | 72 +++++ .../FengBi/EntryExitRecordPanel/index.js | 69 +++++ .../FengBi/EntryExitRecordPanel/index.less | 49 ++++ .../FengBi/EntryExitTrendPanel/index.js | 205 +++++++++++++++ .../FengBi/EntryExitTrendPanel/index.less | 31 +++ .../Content/branchOffice/FengBi/echarts.js | 140 ---------- .../Content/branchOffice/FengBi/index.js | 180 +------------ .../Content/branchOffice/FengBi/index.less | 175 ------------ .../DepartmentResponsibilityPanel/index.js | 248 ++++++++++++++++++ .../DepartmentResponsibilityPanel/index.less | 12 + .../IndexLeft/RelatedPartyStatsPanel/index.js | 116 ++++++++ .../RelatedPartyStatsPanel/index.less | 30 +++ .../IndexLeft/RiskHazardPanel/index.js | 61 +++++ .../IndexLeft/RiskHazardPanel/index.less | 53 ++++ .../IndexLeft/WeatherPreventionPanel/index.js | 131 +++++++++ .../WeatherPreventionPanel/index.less | 99 +++++++ .../Content/branchOffice/IndexLeft/echarts.js | 138 ---------- .../Content/branchOffice/IndexLeft/index.js | 226 +--------------- .../Content/branchOffice/IndexLeft/index.less | 229 ---------------- .../IndexRight/AreaAlarmPanel/index.js | 64 +++++ .../IndexRight/AreaAlarmPanel/index.less | 30 +++ .../ClosedAreaPersonnelPanel/index.js | 245 +++++++++++++++++ .../ClosedAreaPersonnelPanel/index.less | 12 + .../GateAndClosedAreaPanel/index.js | 74 ++++++ .../GateAndClosedAreaPanel/index.less | 27 ++ .../IndexRight/KeyWorkStatsPanel/index.js | 97 +++++++ .../IndexRight/KeyWorkStatsPanel/index.less | 30 +++ .../branchOffice/IndexRight/echarts.js | 195 -------------- .../Content/branchOffice/IndexRight/index.js | 200 +------------- .../branchOffice/IndexRight/index.less | 129 --------- .../MenJin/ClosedAreaStatsPanel/index.js | 55 ++++ .../MenJin/ClosedAreaStatsPanel/index.less | 65 +++++ .../MenJin/EntryExitTrendPanel/index.js | 202 ++++++++++++++ .../MenJin/EntryExitTrendPanel/index.less | 31 +++ .../MenJin/GateApplicationPanel/index.js | 125 +++++++++ .../MenJin/GateApplicationPanel/index.less | 53 ++++ .../Content/branchOffice/MenJin/echarts.js | 140 ---------- .../Content/branchOffice/MenJin/index.js | 174 +----------- .../Content/branchOffice/MenJin/index.less | 168 ------------ .../QiXiang/WeatherAlarmPanel/index.js | 46 ++++ .../QiXiang/WeatherAlarmPanel/index.less | 30 +++ .../QiXiang/WeatherPreventionPanel/index.js | 122 +++++++++ .../QiXiang/WeatherPreventionPanel/index.less | 99 +++++++ .../Content/branchOffice/QiXiang/index.js | 154 +---------- .../Content/branchOffice/QiXiang/index.less | 153 ----------- .../RenYuan/AlarmHandlingPanel/index.js | 52 ++++ .../RenYuan/AlarmHandlingPanel/index.less | 42 +++ .../RenYuan/AlarmTypeChartPanel/index.js | 75 ++++++ .../RenYuan/AlarmTypeChartPanel/index.less | 8 + .../RenYuan/ClosedAreaPersonnelPanel/index.js | 187 +++++++++++++ .../ClosedAreaPersonnelPanel/index.less | 12 + .../RenYuan/PersonnelLocationPanel/index.js | 50 ++++ .../RenYuan/PersonnelLocationPanel/index.less | 34 +++ .../Content/branchOffice/RenYuan/echarts.js | 171 ------------ .../Content/branchOffice/RenYuan/index.js | 144 +--------- .../Content/branchOffice/RenYuan/index.less | 106 -------- .../Content/branchOffice/Title/index.js | 15 -- .../Content/branchOffice/Title/index.less | 25 -- 60 files changed, 3169 insertions(+), 2805 deletions(-) create mode 100644 src/pages/Container/Map/components/Content/branchOffice/FengBi/ClosedAreaStatsPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/FengBi/ClosedAreaStatsPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitRecordPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitRecordPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitTrendPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitTrendPanel/index.less delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/FengBi/echarts.js delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/FengBi/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexLeft/DepartmentResponsibilityPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexLeft/DepartmentResponsibilityPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RelatedPartyStatsPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RelatedPartyStatsPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RiskHazardPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RiskHazardPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.less delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexLeft/echarts.js delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexLeft/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexRight/AreaAlarmPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexRight/AreaAlarmPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexRight/ClosedAreaPersonnelPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexRight/ClosedAreaPersonnelPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexRight/GateAndClosedAreaPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexRight/GateAndClosedAreaPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexRight/KeyWorkStatsPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexRight/KeyWorkStatsPanel/index.less delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexRight/echarts.js delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/IndexRight/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/MenJin/ClosedAreaStatsPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/MenJin/ClosedAreaStatsPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/MenJin/EntryExitTrendPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/MenJin/EntryExitTrendPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/MenJin/GateApplicationPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/MenJin/GateApplicationPanel/index.less delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/MenJin/echarts.js delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/MenJin/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherAlarmPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherAlarmPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.less delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/QiXiang/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmHandlingPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmHandlingPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmTypeChartPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmTypeChartPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/RenYuan/ClosedAreaPersonnelPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/RenYuan/ClosedAreaPersonnelPanel/index.less create mode 100644 src/pages/Container/Map/components/Content/branchOffice/RenYuan/PersonnelLocationPanel/index.js create mode 100644 src/pages/Container/Map/components/Content/branchOffice/RenYuan/PersonnelLocationPanel/index.less delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/RenYuan/echarts.js delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/RenYuan/index.less delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/Title/index.js delete mode 100644 src/pages/Container/Map/components/Content/branchOffice/Title/index.less diff --git a/package.json b/package.json index 0e36ba9..4add90e 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "react-transition-group": "^4.4.5", "sass": "^1.97.0", "sass-loader": "^16.0.6", - "zy-react-library": "^2.0.13" + "zy-react-library": "^2.0.15" }, "devDependencies": { "@antfu/eslint-config": "^5.4.1", diff --git a/src/pages/Container/Map/components/Content/branchOffice/FengBi/ClosedAreaStatsPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/FengBi/ClosedAreaStatsPanel/index.js new file mode 100644 index 0000000..65f04eb --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/FengBi/ClosedAreaStatsPanel/index.js @@ -0,0 +1,67 @@ +import CountUp from "react-countup"; +import areaIcon from "~/assets/images/map_bi/content/icon29.png"; +import quayIcon from "~/assets/images/map_bi/content/icon30.png"; +import cornerIcon from "~/assets/images/map_bi/content/icon32.png"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const statistics = [ + { img: areaIcon, title: "堆场区", peopleCount: 123, vehicleCount: 45 }, + { img: quayIcon, title: "码头区", peopleCount: 141, vehicleCount: 43 }, +]; + +/** 负责展示封闭区域内的人员和车辆统计。 */ +function ClosedAreaStatsPanel() { + return ( + +
+ {statistics.map(item => ( +
+
+ +
+
+
+ {item.title} +
+
+ + +
+
+ + + + +
+ ))} +
+
+ ); +} + +/** 封闭区域统计项的单个数值展示。 */ +function Statistic({ label, value }) { + return ( +
+
{label}
+
+ +
+
+ ); +} + +/** 区域卡片四角的装饰图标。 */ +function Corner({ className }) { + return ( +
+ +
+ ); +} + +export default ClosedAreaStatsPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/FengBi/ClosedAreaStatsPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/FengBi/ClosedAreaStatsPanel/index.less new file mode 100644 index 0000000..af65722 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/FengBi/ClosedAreaStatsPanel/index.less @@ -0,0 +1,72 @@ +.branch-office-closed-area-stats__content { + display: flex; + flex-wrap: wrap; + gap: 10px 6px; + justify-content: space-between; + padding: 10px 15px; +} + +.branch-office-closed-area-stats__item { + position: relative; + display: flex; + flex-basis: calc(50% - 10px); + align-items: center; + padding: 5px; + color: #fff; + font-size: 12px; + background-color: rgba(1, 56, 154, 0.5); + border: 1px solid rgba(32, 145, 243, 0.36); +} + +.branch-office-closed-area-stats__image img { + width: 57px; + height: 56px; +} + +.branch-office-closed-area-stats__info { + margin-left: 20px; + text-align: center; +} + +.branch-office-closed-area-stats__title { + color: #0096ff; + font-size: 14px; + font-weight: bold; +} + +.branch-office-closed-area-stats__values { + display: flex; + gap: 20px; + align-items: center; + justify-content: space-between; + margin-top: 10px; +} + +.branch-office-closed-area-stats__corner { + position: absolute; + width: 12px; + height: 12px; +} + +.branch-office-closed-area-stats__corner--top-left { + top: -9px; + left: -4px; +} + +.branch-office-closed-area-stats__corner--top-right { + top: -4px; + right: -9px; + transform: rotate(90deg); +} + +.branch-office-closed-area-stats__corner--bottom-left { + bottom: -4px; + left: -9px; + transform: rotate(270deg); +} + +.branch-office-closed-area-stats__corner--bottom-right { + right: -4px; + bottom: -9px; + transform: rotate(180deg); +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitRecordPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitRecordPanel/index.js new file mode 100644 index 0000000..ac3ed8b --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitRecordPanel/index.js @@ -0,0 +1,69 @@ +import { useState } from "react"; +import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const tabs = ["人员", "车辆"]; +const records = [ + { NAME: "零号门", IN: "0", OUT: "0" }, + { NAME: "一号门", IN: "15", OUT: "12" }, + { NAME: "二号门", IN: "8", OUT: "10" }, + { NAME: "三号门", IN: "22", OUT: "18" }, + { NAME: "四号门", IN: "5", OUT: "7" }, + { NAME: "五号门", IN: "30", OUT: "25" }, + { NAME: "六号门", IN: "12", OUT: "14" }, + { NAME: "七号门", IN: "18", OUT: "16" }, + { NAME: "八号门", IN: "9", OUT: "11" }, + { NAME: "九号门", IN: "25", OUT: "20" }, +]; + +/** 负责展示封闭区域进出记录及分类标签状态。 */ +function EntryExitRecordPanel() { + const [activeIndex, setActiveIndex] = useState(0); + + return ( + + {tabs.map((item, index) => ( +
setActiveIndex(index)} + > + {item} +
+ ))} + + )} + > +
+
+
+
封闭区域名称
+
进入数
+
外出数
+
+
+ + {records.map((item, index) => ( +
+
{item.NAME}
+
{item.IN}
+
{item.OUT}
+
+ ))} +
+
+
+
+
+ ); +} + +export default EntryExitRecordPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitRecordPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitRecordPanel/index.less new file mode 100644 index 0000000..49aaccf --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitRecordPanel/index.less @@ -0,0 +1,49 @@ +.branch-office-closed-area__record { + margin-top: 10px; +} + +.branch-office-closed-area-record__tabs { + display: flex; +} + +.branch-office-closed-area-record__tab { + padding: 2px 12px; + color: #fff; + font-size: 12px; + cursor: pointer; + background-color: rgba(36, 115, 239, 0.27); + border: 1px solid #5d718c; + border-radius: 2px; +} + +.branch-office-closed-area-record__tab--active { + background-color: #2473ef; + border-color: #2473ef; +} + +.branch-office-closed-area-record__content { + padding: 10px 15px; +} + +.branch-office-closed-area-record__body { + height: 350px; + overflow-y: hidden; +} + +.branch-office-closed-area-record__row { + display: grid; + grid-template-columns: 2fr 1fr 1fr; + background-color: rgba(42, 86, 158, 0.53); +} + +.branch-office-closed-area-record__body +.branch-office-closed-area-record__row:nth-child(odd) { + background-color: transparent; +} + +.branch-office-closed-area-record__row div { + padding: 5px; + color: #fff; + font-size: 12px; + text-align: center; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitTrendPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitTrendPanel/index.js new file mode 100644 index 0000000..8c1d5be --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitTrendPanel/index.js @@ -0,0 +1,205 @@ +import { useMount } from "ahooks"; +import * as echarts from "echarts"; +import { useRef, useState } from "react"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const tabs = ["人员", "车辆"]; +const records = [ + { NAME: "零号门", IN: "0", OUT: "0" }, + { NAME: "一号门", IN: "15", OUT: "12" }, + { NAME: "二号门", IN: "8", OUT: "10" }, + { NAME: "三号门", IN: "22", OUT: "18" }, + { NAME: "四号门", IN: "5", OUT: "7" }, + { NAME: "五号门", IN: "30", OUT: "25" }, + { NAME: "六号门", IN: "12", OUT: "14" }, + { NAME: "七号门", IN: "18", OUT: "16" }, + { NAME: "八号门", IN: "9", OUT: "11" }, + { NAME: "九号门", IN: "25", OUT: "20" }, +]; + +/** 负责展示封闭区域进出趋势并管理图表实例生命周期。 */ +function EntryExitTrendPanel() { + const [activeIndex, setActiveIndex] = useState(0); + const chartInstance = useRef(null); + const chartRef = useRef(null); + + const initEcharts = (mainRef, chartInstance, data) => { + chartInstance.current = echarts.init(mainRef.current); + + const inData = data.map(item => Number.parseInt(item.IN)); + const outData = data.map(item => Number.parseInt(item.OUT)); + const names = data.map(item => item.NAME); + + const option = { + tooltip: { + trigger: "axis", + axisPointer: { + type: "none", + }, + }, + legend: { + data: ["进", "出"], + top: "0%", + right: "0%", + textStyle: { + color: "#fff", + fontSize: 14, + }, + itemWidth: 12, + itemHeight: 10, + }, + dataZoom: [ + { + type: "inside", + xAxisIndex: 0, + start: 0, + end: 50, + zoomOnMouseWheel: "shift", + moveOnMouseWheel: true, + moveOnMouseMove: true, + }, + { + type: "slider", + height: 6, + bottom: "7%", + show: true, + start: 0, + end: 50, + handleSize: 5, + handleStyle: { + color: "#DCE2E8", + }, + xAxisIndex: [0], + filterMode: "filter", + showDetail: false, + }, + ], + grid: { + left: "10%", + right: "5%", + top: "10%", + bottom: "20%", + }, + xAxis: [ + { + type: "category", + data: names, + axisTick: { + alignWithLabel: true, + }, + axisLabel: { + color: "#fff", + }, + }, + ], + yAxis: { + axisLine: { + show: false, + }, + axisTick: { + show: false, + }, + axisLabel: { + color: "#fff", + }, + splitLine: { + show: true, + lineStyle: { + type: "dashed", + }, + }, + }, + series: [ + { + name: "进", + type: "bar", + backgroundStyle: { + color: "rgba(216, 229, 247, 0.55)", + borderRadius: [8, 8, 0, 0], + }, + itemStyle: { + borderRadius: [12, 12, 0, 0], + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 1, + color: "rgba(121,103,255,0.7)", + }, + { + offset: 0, + color: "#7967FF", + }, + ]), + }, + barWidth: "10", + data: inData, + }, + { + name: "出", + type: "bar", + backgroundStyle: { + color: "rgba(216, 229, 247, 0.55)", + borderRadius: [8, 8, 0, 0], + }, + itemStyle: { + borderRadius: [12, 12, 0, 0], + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 1, + color: "rgba(255,198,124,0.7)", + }, + { + offset: 0, + color: "#FFC67C", + }, + ]), + }, + barWidth: "10", + data: outData, + }, + ], + }; + + chartInstance.current.setOption(option); + }; + + useMount(() => { + initEcharts(chartRef, chartInstance, records); + + return () => { + if (chartInstance.current) { + chartInstance.current.dispose(); + chartInstance.current = null; + } + }; + }); + + return ( + + {tabs.map((item, index) => ( +
setActiveIndex(index)} + > + {item} +
+ ))} + + )} + > +
+
+
+ + ); +} + +export default EntryExitTrendPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitTrendPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitTrendPanel/index.less new file mode 100644 index 0000000..dd44f38 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/FengBi/EntryExitTrendPanel/index.less @@ -0,0 +1,31 @@ +.branch-office-closed-area__trend { + margin-top: 10px; +} + +.branch-office-closed-area-trend__tabs { + display: flex; +} + +.branch-office-closed-area-trend__tab { + padding: 2px 12px; + color: #fff; + font-size: 12px; + cursor: pointer; + background-color: rgba(36, 115, 239, 0.27); + border: 1px solid #5d718c; + border-radius: 2px; +} + +.branch-office-closed-area-trend__tab--active { + background-color: #2473ef; + border-color: #2473ef; +} + +.branch-office-closed-area-trend__content { + padding: 10px 15px; +} + +.branch-office-closed-area-trend__chart { + width: 100%; + height: 300px; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/FengBi/echarts.js b/src/pages/Container/Map/components/Content/branchOffice/FengBi/echarts.js deleted file mode 100644 index 1d27f68..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/FengBi/echarts.js +++ /dev/null @@ -1,140 +0,0 @@ -import * as echarts from "echarts"; - -export const initEcharts1 = (mainRef, chartInstance, data) => { - chartInstance.current = echarts.init(mainRef.current); - - const inData = data.map(item => Number.parseInt(item.IN)); - const outData = data.map(item => Number.parseInt(item.OUT)); - const names = data.map(item => item.NAME); - - const option = { - tooltip: { - trigger: "axis", - axisPointer: { - type: "none", - }, - }, - legend: { - data: ["进", "出"], - top: "0%", - right: "0%", - textStyle: { - color: "#fff", - fontSize: 14, - }, - itemWidth: 12, - itemHeight: 10, - }, - dataZoom: [ - { - type: "inside", - xAxisIndex: 0, - start: 0, - end: 50, - zoomOnMouseWheel: "shift", - moveOnMouseWheel: true, - moveOnMouseMove: true, - }, - { - type: "slider", - height: 6, - bottom: "7%", - show: true, - start: 0, - end: 50, - handleSize: 5, - handleStyle: { - color: "#DCE2E8", - }, - xAxisIndex: [0], - filterMode: "filter", - showDetail: false, - }, - ], - grid: { - left: "10%", - right: "5%", - top: "10%", - bottom: "20%", - }, - xAxis: [ - { - type: "category", - data: names, - axisTick: { - alignWithLabel: true, - }, - axisLabel: { - color: "#fff", - }, - }, - ], - yAxis: { - axisLine: { - show: false, - }, - axisTick: { - show: false, - }, - axisLabel: { - color: "#fff", - }, - splitLine: { - show: true, - lineStyle: { - type: "dashed", - }, - }, - }, - series: [ - { - name: "进", - type: "bar", - backgroundStyle: { - color: "rgba(216, 229, 247, 0.55)", - borderRadius: [8, 8, 0, 0], - }, - itemStyle: { - borderRadius: [12, 12, 0, 0], - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { - offset: 1, - color: "rgba(121,103,255,0.7)", - }, - { - offset: 0, - color: "#7967FF", - }, - ]), - }, - barWidth: "10", - data: inData, - }, - { - name: "出", - type: "bar", - backgroundStyle: { - color: "rgba(216, 229, 247, 0.55)", - borderRadius: [8, 8, 0, 0], - }, - itemStyle: { - borderRadius: [12, 12, 0, 0], - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { - offset: 1, - color: "rgba(255,198,124,0.7)", - }, - { - offset: 0, - color: "#FFC67C", - }, - ]), - }, - barWidth: "10", - data: outData, - }, - ], - }; - - chartInstance.current.setOption(option); -}; diff --git a/src/pages/Container/Map/components/Content/branchOffice/FengBi/index.js b/src/pages/Container/Map/components/Content/branchOffice/FengBi/index.js index 2b5a379..e982502 100644 --- a/src/pages/Container/Map/components/Content/branchOffice/FengBi/index.js +++ b/src/pages/Container/Map/components/Content/branchOffice/FengBi/index.js @@ -1,178 +1,14 @@ -import { useMount } from "ahooks"; -import { useRef, useState } from "react"; -import CountUp from "react-countup"; -import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import icon29 from "~/assets/images/map_bi/content/icon29.png"; -import icon30 from "~/assets/images/map_bi/content/icon30.png"; -import icon32 from "~/assets/images/map_bi/content/icon32.png"; -import Title from "~/pages/Container/Map/components/Content/branchOffice/Title"; -import { initEcharts1 } from "./echarts"; -import "./index.less"; - -const block2OptionsNavList = ["人员", "车辆"]; -const block3OptionsNavList = ["人员", "车辆"]; +import ClosedAreaStatsPanel from "./ClosedAreaStatsPanel"; +import EntryExitRecordPanel from "./EntryExitRecordPanel"; +import EntryExitTrendPanel from "./EntryExitTrendPanel"; +/** 分公司封闭区域模块仅负责按展示顺序组合业务区块。 */ function FengBi() { - const [block1List, setBlock1List] = useState([ - { img: icon29, title: "堆场区", count1: 123, count2: 45 }, - { img: icon30, title: "码头区", count1: 141, count2: 43 }, - ]); - const [block2Index, setBlock2Index] = useState(0); - const [block3Index, setBlock3Index] = useState(0); - const [block3List, setBlock3List] = useState([ - { NAME: "零号门", IN: "0", OUT: "0" }, - { NAME: "一号门", IN: "15", OUT: "12" }, - { NAME: "二号门", IN: "8", OUT: "10" }, - { NAME: "三号门", IN: "22", OUT: "18" }, - { NAME: "四号门", IN: "5", OUT: "7" }, - { NAME: "五号门", IN: "30", OUT: "25" }, - { NAME: "六号门", IN: "12", OUT: "14" }, - { NAME: "七号门", IN: "18", OUT: "16" }, - { NAME: "八号门", IN: "9", OUT: "11" }, - { NAME: "九号门", IN: "25", OUT: "20" }, - ]); - - const chartInstance = useRef(null); - const main1Ref = useRef(null); - - useMount(() => { - initEcharts1(main1Ref, chartInstance, [ - { NAME: "零号门", IN: "0", OUT: "0" }, - { NAME: "一号门", IN: "15", OUT: "12" }, - { NAME: "二号门", IN: "8", OUT: "10" }, - { NAME: "三号门", IN: "22", OUT: "18" }, - { NAME: "四号门", IN: "5", OUT: "7" }, - { NAME: "五号门", IN: "30", OUT: "25" }, - { NAME: "六号门", IN: "12", OUT: "14" }, - { NAME: "七号门", IN: "18", OUT: "16" }, - { NAME: "八号门", IN: "9", OUT: "11" }, - { NAME: "九号门", IN: "25", OUT: "20" }, - ]); - - return () => { - if (chartInstance.current) { - chartInstance.current.dispose(); - chartInstance.current = null; - } - }; - }); - - const block2OptionsClick = (index) => { - if (index === block2Index) - return; - setBlock2Index(index); - }; - - const block3OptionsClick = (index) => { - if (index === block3Index) - return; - setBlock3Index(index); - }; - return ( -
-
- - <div className="options"> - { - block1List.map((item, index) => ( - <div className="option" key={index}> - <div className="img"> - <img src={item.img} alt="" /> - </div> - <div className="infos"> - <div className="title">{item.title}</div> - <div className="info"> - <div> - <div className="label">人数</div> - <div className="value"> - <CountUp end={+item.count1}></CountUp> - </div> - </div> - <div> - <div className="label">车辆数</div> - <div className="value"> - <CountUp end={+item.count2}></CountUp> - </div> - </div> - </div> - </div> - <div className="corner-img top-left-img"> - <img src={icon32} alt="" /> - </div> - <div className="corner-img top-right-img"> - <img src={icon32} alt="" /> - </div> - <div className="corner-img bottom-left-img"> - <img src={icon32} alt="" /> - </div> - <div className="corner-img bottom-right-img"> - <img src={icon32} alt="" /> - </div> - </div> - )) - } - </div> - </div> - <div className="block2"> - <Title - title="封闭区域进出情况统计" - extra={( - <div className="tabs"> - {block2OptionsNavList.map((item, index) => ( - <div - key={index} - className={`tab ${index === block2Index ? "active" : ""}`} - onClick={() => block2OptionsClick(index)} - > - {item} - </div> - ))} - </div> - )} - /> - <div className="options"> - <div ref={main1Ref} style={{ width: "100%", height: "300px" }} /> - </div> - </div> - <div className="block3"> - <Title - title="封闭区域进出情况统计" - extra={( - <div className="tabs"> - {block3OptionsNavList.map((item, index) => ( - <div - key={index} - className={`tab ${index === block3Index ? "active" : ""}`} - onClick={() => block3OptionsClick(index)} - > - {item} - </div> - ))} - </div> - )} - /> - <div className="options"> - <div className="table"> - <div className="tr"> - <div className="td">封闭区域名称</div> - <div className="td">进入数</div> - <div className="td">外出数</div> - </div> - <div className="scroll"> - <SeamlessScroll list={block3List} step={0.5}> - {block3List.map((item, index) => ( - <div key={index} className="tr"> - <div className="td">{item.NAME}</div> - <div className="td">{item.IN}</div> - <div className="td">{item.OUT}</div> - </div> - ))} - </SeamlessScroll> - </div> - </div> - </div> - </div> + <div className="branch-office-closed-area"> + <ClosedAreaStatsPanel /> + <EntryExitTrendPanel /> + <EntryExitRecordPanel /> </div> ); } diff --git a/src/pages/Container/Map/components/Content/branchOffice/FengBi/index.less b/src/pages/Container/Map/components/Content/branchOffice/FengBi/index.less deleted file mode 100644 index 1a10a44..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/FengBi/index.less +++ /dev/null @@ -1,175 +0,0 @@ -.branch_office_fengbi { - .tabs-style() { - display: flex; - - .tab { - background-color: rgba(36, 115, 239, 0.27); - padding: 2px 12px; - border-radius: 2px; - color: #fff; - font-size: 12px; - border: 1px solid #5d718c; - cursor: pointer; - - &.active { - background-color: #2473ef; - border: 1px solid #2473ef; - } - } - } - - .block1 { - background-color: rgba(12, 28, 88, 0.4); - - .options { - display: flex; - justify-content: space-between; - flex-wrap: wrap; - gap: 10px 6px; - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - - .option { - flex-basis: calc(50% - 10px); - background-color: rgba(1, 56, 154, 0.5); - border: 1px solid rgba(32, 145, 243, 0.36); - padding: 5px; - color: #fff; - display: flex; - align-items: center; - font-size: 12px; - position: relative; - - .img { - img { - width: 57px; - height: 56px; - } - } - - .infos { - margin-left: 20px; - text-align: center; - - .title { - color: #0096FF; - font-size: 14px; - font-weight: bold; - } - - .info { - margin-top: 10px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 20px; - } - } - - .corner-img { - position: absolute; - width: 12px; - height: 12px; - - &.top-left-img { - top: -9px; - left: -4px; - } - - &.top-right-img { - transform: rotate(90deg); - top: -4px; - right: -9px; - } - - &.bottom-left-img { - transform: rotate(270deg); - bottom: -4px; - left: -9px; - } - - &.bottom-right-img { - transform: rotate(180deg); - bottom: -9px; - right: -4px; - } - } - } - } - } - - .block2 { - background-color: rgba(12, 28, 88, 0.4); - margin-top: 10px; - - .tabs { - .tabs-style(); - } - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - } - } - - .block3 { - background-color: rgba(12, 28, 88, 0.4); - margin-top: 10px; - - .tabs { - .tabs-style(); - } - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .table { - .scroll { - height: 350px; - overflow-y: hidden; - - .tr { - &:nth-child(odd) { - background-color: transparent; - } - } - } - - .tr { - display: grid; - grid-template-columns: 2fr 1fr 1fr; - background-color: rgba(42, 86, 158, 0.53); - - .td { - text-align: center; - font-size: 12px; - color: #fff; - padding: 5px; - - &.green { - color: #7ccf41; - } - - &.yellow { - color: #ffcb05; - } - } - } - } - } - } -} diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/DepartmentResponsibilityPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/DepartmentResponsibilityPanel/index.js new file mode 100644 index 0000000..1a02399 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/DepartmentResponsibilityPanel/index.js @@ -0,0 +1,248 @@ +import { useMount } from "ahooks"; +import * as echarts from "echarts"; +import { useRef } from "react"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const records = [ + { + departmentName: "技术部", + riskCoverageRate: 95, + hiddenInvestigationRate: 92, + hiddenRectificationRate: 90, + checkTaskCompletionRate: 94, + }, + { + departmentName: "销售部", + riskCoverageRate: 88, + hiddenInvestigationRate: 85, + hiddenRectificationRate: 87, + checkTaskCompletionRate: 89, + }, + { + departmentName: "市场部", + riskCoverageRate: 92, + hiddenInvestigationRate: 90, + hiddenRectificationRate: 88, + checkTaskCompletionRate: 91, + }, + { + departmentName: "运营部", + riskCoverageRate: 96, + hiddenInvestigationRate: 94, + hiddenRectificationRate: 95, + checkTaskCompletionRate: 93, + }, + { + departmentName: "客服部", + riskCoverageRate: 85, + hiddenInvestigationRate: 83, + hiddenRectificationRate: 86, + checkTaskCompletionRate: 84, + }, + { + departmentName: "财务部", + riskCoverageRate: 98, + hiddenInvestigationRate: 96, + hiddenRectificationRate: 97, + checkTaskCompletionRate: 97, + }, + { + departmentName: "人事部", + riskCoverageRate: 87, + hiddenInvestigationRate: 84, + hiddenRectificationRate: 85, + checkTaskCompletionRate: 86, + }, + { + departmentName: "研发部", + riskCoverageRate: 94, + hiddenInvestigationRate: 91, + hiddenRectificationRate: 93, + checkTaskCompletionRate: 92, + }, + { + departmentName: "质量部", + riskCoverageRate: 90, + hiddenInvestigationRate: 88, + hiddenRectificationRate: 89, + checkTaskCompletionRate: 88, + }, + { + departmentName: "采购部", + riskCoverageRate: 86, + hiddenInvestigationRate: 84, + hiddenRectificationRate: 83, + checkTaskCompletionRate: 85, + }, +]; + +/** 负责展示部门职责落实趋势,并管理图表实例生命周期。 */ +function DepartmentResponsibilityPanel() { + const chartInstance = useRef(null); + const chartRef = useRef(null); + + const initEcharts = (mainRef, chartInstance, data) => { + chartInstance.current = echarts.init(mainRef.current); + + const option = { + color: ["#7d4449", "#009944", "#167ce4", "#00ffff"], + tooltip: { + trigger: "axis", + }, + legend: { + textStyle: { + color: "#fff", + fontSize: "12px", + }, + top: 8, + right: 10, + }, + grid: { + left: "4%", + right: "4%", + bottom: "10%", + top: "26%", + containLabel: true, + }, + dataZoom: [ + { + type: "inside", + xAxisIndex: 0, + start: 0, + end: 50, + zoomOnMouseWheel: "shift", + moveOnMouseWheel: true, + moveOnMouseMove: true, + }, + { + type: "slider", + height: 6, + bottom: 0, + show: true, + start: 0, + end: 50, + handleSize: 3, + handleStyle: { + color: "#DCE2E8", + }, + xAxisIndex: [0], + filterMode: "filter", + showDetail: false, + }, + ], + xAxis: { + data: data.map(item => item.departmentName), + type: "category", + boundaryGap: false, + axisLine: { + symbol: "none", + lineStyle: { + color: "#fff", + }, + }, + axisTick: { + show: false, + }, + axisLabel: { + interval: 0, + color: "#fff", + fontSize: 12, + }, + }, + yAxis: { + type: "value", + axisLabel: { + color: "#fff", + fontSize: 12, + padding: [0, 10, 0, 0], + }, + splitLine: { + lineStyle: { + color: "rgba(255,255,255,0.79)", + type: "dashed", + }, + }, + }, + series: [ + { + name: "风险点检查覆盖率", + type: "line", + data: data.map(item => item.riskCoverageRate), + smooth: true, + lineStyle: { + width: 2, + }, + showSymbol: false, + symbol: "circle", + symbolSize: 6, + }, + { + name: "隐患清单排查率", + data: data.map(item => item.hiddenInvestigationRate), + type: "line", + smooth: true, + lineStyle: { + width: 2, + }, + showSymbol: false, + symbol: "circle", + symbolSize: 6, + }, + { + name: "隐崽整改率", + data: data.map(item => item.hiddenRectificationRate), + type: "line", + smooth: true, + lineStyle: { + width: 2, + }, + showSymbol: false, + symbol: "circle", + symbolSize: 6, + }, + { + name: "检查任务完成率", + data: data.map(item => item.checkTaskCompletionRate), + type: "line", + smooth: true, + lineStyle: { + width: 2, + }, + showSymbol: false, + symbol: "circle", + symbolSize: 6, + }, + ], + }; + + chartInstance.current.setOption(option); + }; + + useMount(() => { + initEcharts(chartRef, chartInstance, records); + + return () => { + if (chartInstance.current) { + chartInstance.current.dispose(); + chartInstance.current = null; + } + }; + }); + + return ( + <Panel + title="部门职责落实情况" + className="branch-office-index-left__responsibility" + > + <div className="branch-office-department-responsibility__content"> + <div + ref={chartRef} + className="branch-office-department-responsibility__chart" + /> + </div> + </Panel> + ); +} + +export default DepartmentResponsibilityPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/DepartmentResponsibilityPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/DepartmentResponsibilityPanel/index.less new file mode 100644 index 0000000..3262b07 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/DepartmentResponsibilityPanel/index.less @@ -0,0 +1,12 @@ +.branch-office-index-left__responsibility { + margin-top: 10px; +} + +.branch-office-department-responsibility__content { + padding: 10px 15px; +} + +.branch-office-department-responsibility__chart { + width: 100%; + height: 200px; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RelatedPartyStatsPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RelatedPartyStatsPanel/index.js new file mode 100644 index 0000000..047926c --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RelatedPartyStatsPanel/index.js @@ -0,0 +1,116 @@ +import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const records = [ + { + relatedPartyCategory: "承包商", + firstLevelUnits: 15, + firstLevelPersonnel: 120, + secondLevelUnits: 25, + secondLevelPersonnel: 320, + }, + { + relatedPartyCategory: "供应商", + firstLevelUnits: 8, + firstLevelPersonnel: 85, + secondLevelUnits: 18, + secondLevelPersonnel: 210, + }, + { + relatedPartyCategory: "分包商", + firstLevelUnits: 12, + firstLevelPersonnel: 95, + secondLevelUnits: 30, + secondLevelPersonnel: 450, + }, + { + relatedPartyCategory: "监理单位", + firstLevelUnits: 5, + firstLevelPersonnel: 40, + secondLevelUnits: 10, + secondLevelPersonnel: 80, + }, + { + relatedPartyCategory: "设计单位", + firstLevelUnits: 6, + firstLevelPersonnel: 55, + secondLevelUnits: 12, + secondLevelPersonnel: 120, + }, + { + relatedPartyCategory: "施工单位", + firstLevelUnits: 18, + firstLevelPersonnel: 200, + secondLevelUnits: 40, + secondLevelPersonnel: 680, + }, + { + relatedPartyCategory: "检测机构", + firstLevelUnits: 3, + firstLevelPersonnel: 25, + secondLevelUnits: 8, + secondLevelPersonnel: 65, + }, + { + relatedPartyCategory: "咨询公司", + firstLevelUnits: 7, + firstLevelPersonnel: 60, + secondLevelUnits: 15, + secondLevelPersonnel: 180, + }, + { + relatedPartyCategory: "运维单位", + firstLevelUnits: 9, + firstLevelPersonnel: 75, + secondLevelUnits: 22, + secondLevelPersonnel: 320, + }, + { + relatedPartyCategory: "其他单位", + firstLevelUnits: 4, + firstLevelPersonnel: 35, + secondLevelUnits: 14, + secondLevelPersonnel: 150, + }, +]; + +/** 负责展示相关方单位及其人员统计。 */ +function RelatedPartyStatsPanel() { + return ( + <Panel + title="相关方单位统计" + className="branch-office-index-left__related-party" + > + <div className="branch-office-related-party-stats__content"> + <div className="branch-office-related-party-stats__table"> + <div className="branch-office-related-party-stats__row"> + <div>相关方类别</div> + <div>一级单位数</div> + <div>一级人数</div> + <div>二级单位数</div> + <div>二级人数</div> + </div> + <div className="branch-office-related-party-stats__body"> + <SeamlessScroll list={records} step={0.5}> + {records.map((item, index) => ( + <div + className="branch-office-related-party-stats__row" + key={index} + > + <div>{item.relatedPartyCategory}</div> + <div>{item.firstLevelUnits}</div> + <div>{item.firstLevelPersonnel}</div> + <div>{item.secondLevelUnits}</div> + <div>{item.secondLevelPersonnel}</div> + </div> + ))} + </SeamlessScroll> + </div> + </div> + </div> + </Panel> + ); +} + +export default RelatedPartyStatsPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RelatedPartyStatsPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RelatedPartyStatsPanel/index.less new file mode 100644 index 0000000..b9382ad --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RelatedPartyStatsPanel/index.less @@ -0,0 +1,30 @@ +.branch-office-index-left__related-party { + margin-top: 10px; +} + +.branch-office-related-party-stats__content { + padding: 10px 15px; +} + +.branch-office-related-party-stats__table { + margin-top: 5px; +} + +.branch-office-related-party-stats__body { + height: 60px; + overflow-y: hidden; +} + +.branch-office-related-party-stats__row { + display: grid; + grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr; + margin-top: 5px; + background-color: rgba(17, 51, 112, 0.8); +} + +.branch-office-related-party-stats__row div { + padding: 5px; + color: #fff; + font-size: 12px; + text-align: center; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RiskHazardPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RiskHazardPanel/index.js new file mode 100644 index 0000000..72571ae --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RiskHazardPanel/index.js @@ -0,0 +1,61 @@ +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const records = [ + { levelName: "重大风险", riskCount: 21, notRiskCount: 3 }, + { levelName: "较大风险", riskCount: 21, notRiskCount: 3 }, + { levelName: "一般风险", riskCount: 21, notRiskCount: 3 }, + { levelName: "低风险", riskCount: 21, notRiskCount: 3 }, +]; + +const levelColor = { + 重大风险: "#ff0000", + 较大风险: "#ff3c00", + 一般风险: "#e5e72f", + 低风险: "#0e7dfa", +}; + +/** 负责展示不同风险等级的风险点与隐患覆盖情况。 */ +function RiskHazardPanel() { + return ( + <Panel title="风险点隐患" className="branch-office-index-left__risk-hazard"> + <div className="branch-office-risk-hazard__content"> + {records.map((item) => { + const color = levelColor[item.levelName]; + return ( + <div + className="branch-office-risk-hazard__item" + key={item.levelName} + > + <div + className="branch-office-risk-hazard__decoration" + style={{ backgroundColor: color }} + /> + <div + className="branch-office-risk-hazard__title" + style={{ color, borderColor: color }} + > + {item.levelName} + </div> + <div className="branch-office-risk-hazard__info"> + <div> + 风险点数: + {item.riskCount} + </div> + <div className="branch-office-risk-hazard__details"> + <div>检查覆盖率:100%</div> + <div> + 未覆盖风险点数: + {item.notRiskCount} + </div> + </div> + </div> + </div> + ); + })} + </div> + </Panel> + ); +} + +export default RiskHazardPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RiskHazardPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RiskHazardPanel/index.less new file mode 100644 index 0000000..41ccad9 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/RiskHazardPanel/index.less @@ -0,0 +1,53 @@ +.branch-office-index-left__risk-hazard { + margin-top: 10px; +} + +.branch-office-risk-hazard__content { + padding: 10px 15px; +} + +.branch-office-risk-hazard__item { + position: relative; + display: flex; + gap: 19px; + align-items: center; + padding: 6px 10px; + margin-top: 5px; + background-color: rgba(1, 37, 74, 0.47); + border: 1px solid rgba(0, 168, 255, 0.36); + border-radius: 4px; +} + +.branch-office-risk-hazard__item:first-child { + margin-top: 0; +} + +.branch-office-risk-hazard__decoration { + position: absolute; + top: 50%; + left: 0; + width: 2px; + height: 15px; + transform: translateY(-50%); +} + +.branch-office-risk-hazard__title { + flex-basis: 85px; + padding: 10px 7px; + font-weight: bold; + text-align: center; + border-width: 1px; + border-style: solid; + border-radius: 4px; +} + +.branch-office-risk-hazard__info { + flex: 1; + color: #fff; + font-size: 14px; +} + +.branch-office-risk-hazard__details { + display: flex; + justify-content: space-between; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.js new file mode 100644 index 0000000..77f174c --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.js @@ -0,0 +1,131 @@ +import { Connect } from "@cqsjjb/jjb-dva-runtime"; +import { Spin } from "antd"; +import { useEffect, useState } from "react"; +import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; +import temperatureIcon from "~/assets/images/public/bigScreen/img10.png"; +import windIcon from "~/assets/images/public/bigScreen/img11.png"; +import { NS_BI_STATISTICS } from "~/enumerate/namespace"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import { getAlertColor, getWeatherIcon } from "~/utils/weather"; +import "./index.less"; + +const records = [ + { department: "技术部", requiredDevices: 150, anchoredDevices: 142 }, + { department: "销售部", requiredDevices: 80, anchoredDevices: 76 }, + { department: "市场部", requiredDevices: 60, anchoredDevices: 58 }, + { department: "运营部", requiredDevices: 120, anchoredDevices: 115 }, + { department: "客服部", requiredDevices: 90, anchoredDevices: 88 }, + { department: "财务部", requiredDevices: 30, anchoredDevices: 29 }, + { department: "人事部", requiredDevices: 25, anchoredDevices: 24 }, + { department: "研发部", requiredDevices: 200, anchoredDevices: 195 }, + { department: "质量部", requiredDevices: 45, anchoredDevices: 42 }, + { department: "采购部", requiredDevices: 35, anchoredDevices: 33 }, +]; + +/** 负责查询并展示天气、预警和部门设备锚定情况。 */ +function WeatherPreventionPanel(props) { + const [weather, setWeather] = useState({}); + const [alerts, setAlerts] = useState([]); + + useEffect(() => { + const loadWeather = async () => { + const { result = {} } = await props.getWeather(); + setWeather(result.now || {}); + setAlerts(Array.isArray(result.alerts) ? result.alerts : []); + }; + loadWeather(); + }, []); + + return ( + <Panel title="天气预防情况"> + <Spin spinning={props.biStatistics.getWeatherLoading}> + <div className="branch-office-weather-prevention__content"> + <div className="branch-office-weather-prevention__weather"> + <div className="branch-office-weather-prevention__condition"> + <div className="branch-office-weather-prevention__condition-image"> + <img src={getWeatherIcon(weather.text)} alt="" /> + </div> + <div className="branch-office-weather-prevention__condition-text"> + {weather.text || "暂无"} + </div> + </div> + <div className="branch-office-weather-prevention__metrics"> + <WeatherMetric + icon={temperatureIcon} + label="温度:" + value={weather.temp ? `${weather.temp}℃` : "--"} + /> + <WeatherMetric + icon={windIcon} + label="风速:" + value={weather.wind_class || "--"} + /> + </div> + </div> + <div className="branch-office-weather-prevention__alerts"> + <SeamlessScroll + list={alerts} + step={0.5} + limitScrollNum={2} + singleHeight={22} + > + {alerts.map((item, index) => ( + <div + className="branch-office-weather-prevention__alert" + key={index} + style={{ color: getAlertColor(item.level) }} + > + {item.title} + </div> + ))} + </SeamlessScroll> + </div> + <DepartmentTable records={records} /> + </div> + </Spin> + </Panel> + ); +} + +/** 天气区块中的单个基础指标。 */ +function WeatherMetric({ icon, label, value }) { + return ( + <div className="branch-office-weather-prevention__metric"> + <img src={icon} alt="" /> + <div> + <div className="branch-office-weather-prevention__metric-label"> + {label} + </div> + <div className="branch-office-weather-prevention__metric-value"> + {value} + </div> + </div> + </div> + ); +} + +/** 天气预防区块内的部门设备锚定列表。 */ +function DepartmentTable({ records }) { + return ( + <div className="branch-office-weather-prevention__table"> + <div className="branch-office-weather-prevention__row"> + <div>部门名称</div> + <div>需锚定设备数</div> + <div>已锚定设备数</div> + </div> + <div className="branch-office-weather-prevention__table-body"> + <SeamlessScroll list={records} step={0.5}> + {records.map((item, index) => ( + <div className="branch-office-weather-prevention__row" key={index}> + <div>{item.department}</div> + <div>{item.requiredDevices}</div> + <div>{item.anchoredDevices}</div> + </div> + ))} + </SeamlessScroll> + </div> + </div> + ); +} + +export default Connect([NS_BI_STATISTICS], true)(WeatherPreventionPanel); diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.less new file mode 100644 index 0000000..9608156 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.less @@ -0,0 +1,99 @@ +.branch-office-weather-prevention__content { + padding: 10px 15px; +} + +.branch-office-weather-prevention__weather { + display: flex; + gap: 40px; + align-items: center; +} + +.branch-office-weather-prevention__condition { + display: flex; + gap: 5px; + align-items: center; + padding: 10px; +} + +.branch-office-weather-prevention__condition-image img { + width: 40px; + height: 40px; +} + +.branch-office-weather-prevention__condition-text { + color: #fff; + letter-spacing: 5px; + writing-mode: vertical-lr; +} + +.branch-office-weather-prevention__metrics { + display: flex; + flex: 1; + align-items: center; + justify-content: space-between; +} + +.branch-office-weather-prevention__metric { + display: flex; + flex: 1; + gap: 10px; + align-items: center; +} + +.branch-office-weather-prevention__metric img { + width: 42px; + height: 42px; +} + +.branch-office-weather-prevention__metric-label { + color: #fff; + font-size: 12px; +} + +.branch-office-weather-prevention__metric-value { + color: #00e7ff; + font-size: 18px; + font-weight: bold; +} + +.branch-office-weather-prevention__alerts { + height: 20px; + margin-top: 10px; + overflow-y: hidden; +} + +.branch-office-weather-prevention__alert { + font-size: 14px; + font-weight: bold; +} + +.branch-office-weather-prevention__table { + margin-top: 5px; +} + +.branch-office-weather-prevention__table-body { + height: 60px; + overflow-y: hidden; +} + +.branch-office-weather-prevention__row { + display: grid; + grid-template-columns: repeat(3, 1fr); + margin-top: 5px; + background-image: linear-gradient(to bottom, + rgba(0, 42, 85, 0.38), + rgba(1, 37, 74, 0.47), + rgba(4, 38, 87, 0)); + border: 1px solid; + border-bottom: none; + border-image: linear-gradient(to top, + rgba(8, 41, 87, 0.5), + rgba(64, 152, 255, 0.5)) 1; +} + +.branch-office-weather-prevention__row div { + padding: 5px; + color: #fff; + font-size: 12px; + text-align: center; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/echarts.js b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/echarts.js deleted file mode 100644 index f8e2b27..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/echarts.js +++ /dev/null @@ -1,138 +0,0 @@ -import * as echarts from "echarts"; - -export const initEcharts1 = (mainRef, chartInstance, data) => { - chartInstance.current = echarts.init(mainRef.current); - - const option = { - color: ["#7d4449", "#009944", "#167ce4", "#00ffff"], - tooltip: { - trigger: "axis", - }, - legend: { - textStyle: { - color: "#fff", - fontSize: "12px", - }, - top: 8, - right: 10, - }, - grid: { - left: "4%", - right: "4%", - bottom: "10%", - top: "26%", - containLabel: true, - }, - dataZoom: [ - { - type: "inside", - xAxisIndex: 0, - start: 0, - end: 50, - zoomOnMouseWheel: "shift", - moveOnMouseWheel: true, - moveOnMouseMove: true, - }, - { - type: "slider", - height: 6, - bottom: 0, - show: true, - start: 0, - end: 50, - handleSize: 3, - handleStyle: { - color: "#DCE2E8", - }, - xAxisIndex: [0], - filterMode: "filter", - showDetail: false, - }, - ], - xAxis: { - data: data.map(item => item.departmentName), - type: "category", - boundaryGap: false, - axisLine: { - symbol: "none", - lineStyle: { - color: "#fff", - }, - }, - axisTick: { - show: false, - }, - axisLabel: { - interval: 0, - color: "#fff", - fontSize: 12, - }, - }, - yAxis: { - type: "value", - axisLabel: { - color: "#fff", - fontSize: 12, - padding: [0, 10, 0, 0], - }, - splitLine: { - lineStyle: { - color: "rgba(255,255,255,0.79)", - type: "dashed", - }, - }, - }, - series: [ - { - name: "风险点检查覆盖率", - type: "line", - data: data.map(item => item.riskCoverageRate), - smooth: true, - lineStyle: { - width: 2, - }, - showSymbol: false, - symbol: "circle", - symbolSize: 6, - }, - { - name: "隐患清单排查率", - data: data.map(item => item.hiddenInvestigationRate), - type: "line", - smooth: true, - lineStyle: { - width: 2, - }, - showSymbol: false, - symbol: "circle", - symbolSize: 6, - }, - { - name: "隐崽整改率", - data: data.map(item => item.hiddenRectificationRate), - type: "line", - smooth: true, - lineStyle: { - width: 2, - }, - showSymbol: false, - symbol: "circle", - symbolSize: 6, - }, - { - name: "检查任务完成率", - data: data.map(item => item.checkTaskCompletionRate), - type: "line", - smooth: true, - lineStyle: { - width: 2, - }, - showSymbol: false, - symbol: "circle", - symbolSize: 6, - }, - ], - }; - - chartInstance.current.setOption(option); -}; diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/index.js index c0edc65..bff0509 100644 --- a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/index.js +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/index.js @@ -1,220 +1,18 @@ -import { Connect } from "@cqsjjb/jjb-dva-runtime"; -import { useMount } from "ahooks"; -import { useEffect, useRef, useState } from "react"; -import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import icon1 from "~/assets/images/public/bigScreen/img10.png"; -import icon2 from "~/assets/images/public/bigScreen/img11.png"; -import { NS_BI_STATISTICS } from "~/enumerate/namespace"; -import Title from "~/pages/Container/Map/components/Content/branchOffice/Title"; -import { getAlertColor, getWeatherIcon } from "~/utils/weather"; -import { initEcharts1 } from "./echarts"; -import "./index.less"; - -function IndexLeft(props) { - const [weatherData, setWeatherData] = useState({}); - const [alert, setAlert] = useState([]); - const [block1List, setBlock1List] = useState([ - { department: "技术部", requiredDevices: 150, anchoredDevices: 142 }, - { department: "销售部", requiredDevices: 80, anchoredDevices: 76 }, - { department: "市场部", requiredDevices: 60, anchoredDevices: 58 }, - { department: "运营部", requiredDevices: 120, anchoredDevices: 115 }, - { department: "客服部", requiredDevices: 90, anchoredDevices: 88 }, - { department: "财务部", requiredDevices: 30, anchoredDevices: 29 }, - { department: "人事部", requiredDevices: 25, anchoredDevices: 24 }, - { department: "研发部", requiredDevices: 200, anchoredDevices: 195 }, - { department: "质量部", requiredDevices: 45, anchoredDevices: 42 }, - { department: "采购部", requiredDevices: 35, anchoredDevices: 33 }, - ]); - const [block2List, setBlock2List] = useState([ - { levelName: "重大风险", riskCount: 21, notRiskCount: 3 }, - { levelName: "较大风险", riskCount: 21, notRiskCount: 3 }, - { levelName: "一般风险", riskCount: 21, notRiskCount: 3 }, - { levelName: "低风险", riskCount: 21, notRiskCount: 3 }, - ]); - const [block4List, setBlock4List] = useState([ - { relatedPartyCategory: "承包商", firstLevelUnits: 15, firstLevelPersonnel: 120, secondLevelUnits: 25, secondLevelPersonnel: 320 }, - { relatedPartyCategory: "供应商", firstLevelUnits: 8, firstLevelPersonnel: 85, secondLevelUnits: 18, secondLevelPersonnel: 210 }, - { relatedPartyCategory: "分包商", firstLevelUnits: 12, firstLevelPersonnel: 95, secondLevelUnits: 30, secondLevelPersonnel: 450 }, - { relatedPartyCategory: "监理单位", firstLevelUnits: 5, firstLevelPersonnel: 40, secondLevelUnits: 10, secondLevelPersonnel: 80 }, - { relatedPartyCategory: "设计单位", firstLevelUnits: 6, firstLevelPersonnel: 55, secondLevelUnits: 12, secondLevelPersonnel: 120 }, - { relatedPartyCategory: "施工单位", firstLevelUnits: 18, firstLevelPersonnel: 200, secondLevelUnits: 40, secondLevelPersonnel: 680 }, - { relatedPartyCategory: "检测机构", firstLevelUnits: 3, firstLevelPersonnel: 25, secondLevelUnits: 8, secondLevelPersonnel: 65 }, - { relatedPartyCategory: "咨询公司", firstLevelUnits: 7, firstLevelPersonnel: 60, secondLevelUnits: 15, secondLevelPersonnel: 180 }, - { relatedPartyCategory: "运维单位", firstLevelUnits: 9, firstLevelPersonnel: 75, secondLevelUnits: 22, secondLevelPersonnel: 320 }, - { relatedPartyCategory: "其他单位", firstLevelUnits: 4, firstLevelPersonnel: 35, secondLevelUnits: 14, secondLevelPersonnel: 150 }, - ]); - - const chartInstance = useRef(null); - const main1Ref = useRef(null); - - const getWeatherData = async () => { - const { result = {} } = await props.getWeather(); - setWeatherData(result.now); - setAlert(Array.isArray(result.alerts) ? result.alerts : []); - }; - - const getLevelColor = (level) => { - return { 重大风险: "#FF0000", 较大风险: "#FF3C00", 一般风险: "#E5E72F", 低风险: "#0E7DFA" }[level]; - }; - - useMount(() => { - initEcharts1(main1Ref, chartInstance, [ - { departmentName: "技术部", riskCoverageRate: 95, hiddenInvestigationRate: 92, hiddenRectificationRate: 90, checkTaskCompletionRate: 94 }, - { departmentName: "销售部", riskCoverageRate: 88, hiddenInvestigationRate: 85, hiddenRectificationRate: 87, checkTaskCompletionRate: 89 }, - { departmentName: "市场部", riskCoverageRate: 92, hiddenInvestigationRate: 90, hiddenRectificationRate: 88, checkTaskCompletionRate: 91 }, - { departmentName: "运营部", riskCoverageRate: 96, hiddenInvestigationRate: 94, hiddenRectificationRate: 95, checkTaskCompletionRate: 93 }, - { departmentName: "客服部", riskCoverageRate: 85, hiddenInvestigationRate: 83, hiddenRectificationRate: 86, checkTaskCompletionRate: 84 }, - { departmentName: "财务部", riskCoverageRate: 98, hiddenInvestigationRate: 96, hiddenRectificationRate: 97, checkTaskCompletionRate: 97 }, - { departmentName: "人事部", riskCoverageRate: 87, hiddenInvestigationRate: 84, hiddenRectificationRate: 85, checkTaskCompletionRate: 86 }, - { departmentName: "研发部", riskCoverageRate: 94, hiddenInvestigationRate: 91, hiddenRectificationRate: 93, checkTaskCompletionRate: 92 }, - { departmentName: "质量部", riskCoverageRate: 90, hiddenInvestigationRate: 88, hiddenRectificationRate: 89, checkTaskCompletionRate: 88 }, - { departmentName: "采购部", riskCoverageRate: 86, hiddenInvestigationRate: 84, hiddenRectificationRate: 83, checkTaskCompletionRate: 85 }, - ]); - - return () => { - if (chartInstance.current) { - chartInstance.current.dispose(); - chartInstance.current = null; - } - }; - }); - - useEffect(() => { - getWeatherData(); - }, []); +import DepartmentResponsibilityPanel from "./DepartmentResponsibilityPanel"; +import RelatedPartyStatsPanel from "./RelatedPartyStatsPanel"; +import RiskHazardPanel from "./RiskHazardPanel"; +import WeatherPreventionPanel from "./WeatherPreventionPanel"; +/** 分公司首页左侧区域仅负责按展示顺序组合业务区块。 */ +function IndexLeft() { return ( - <div className="branch_office_index_left"> - <div className="block1"> - <Title title="天气预防情况" /> - <div className="options"> - <div className="weather"> - <div className="icon"> - <div className="img"> - <img src={getWeatherIcon(weatherData.text)} alt="" /> - </div> - <div className="text">{weatherData.text || "暂无"}</div> - </div> - <div className="items"> - <div className="item"> - <div className="img"> - <img src={icon1} alt="" /> - </div> - <div className="info"> - <div className="label">温度:</div> - <div className="value">{weatherData.temp ? `${weatherData.temp}℃` : "--"}</div> - </div> - </div> - <div className="item"> - <div className="img"> - <img src={icon2} alt="" /> - </div> - <div className="info"> - <div className="label">风速:</div> - <div className="value">{weatherData.wind_class || "--"}</div> - </div> - </div> - </div> - </div> - <div className="alert"> - <div className="scroll"> - <SeamlessScroll list={alert} step={0.5} limitScrollNum={2} singleHeight={22}> - { - alert.map((item, index) => ( - <div className="item" key={index}> - <div className="title" style={{ color: getAlertColor(item.level) }}>{item.title}</div> - </div> - )) - } - </SeamlessScroll> - </div> - </div> - <div className="table"> - <div className="tr"> - <div className="td">部门名称</div> - <div className="td">需锚定设备数</div> - <div className="td">已锚定设备数</div> - </div> - <div className="scroll"> - <SeamlessScroll list={block1List} step={0.5}> - {block1List.map((item, index) => ( - <div key={index} className="tr"> - <div className="td">{item.department}</div> - <div className="td">{item.requiredDevices}</div> - <div className="td">{item.anchoredDevices}</div> - </div> - ))} - </SeamlessScroll> - </div> - </div> - </div> - </div> - <div className="block2"> - <Title title="风险点隐患" /> - <div className="options"> - <div className="items"> - {block2List.map((item, index) => ( - <div className="item" key={index}> - <div className="decoration" style={{ backgroundColor: getLevelColor(item.levelName) }} /> - <div className="title" style={{ color: getLevelColor(item.levelName), borderColor: getLevelColor(item.levelName) }}> - {item.levelName} - </div> - <div className="info"> - <div> - <span>风险点数:</span> - <span>{item.riskCount}</span> - </div> - <div className="bottom"> - <div> - <span>检查覆盖率:</span> - <span>{`${(item.riskCount / item.riskCount * 100).toFixed(0)}%`}</span> - </div> - <div> - <span>未覆盖风险点数:</span> - <span>{item.notRiskCount}</span> - </div> - </div> - </div> - </div> - ))} - </div> - </div> - </div> - <div className="block3"> - <Title title="部门职责落实情况" /> - <div className="options"> - <div ref={main1Ref} style={{ width: "100%", height: "200px" }} /> - </div> - </div> - <div className="block4"> - <Title title="相关方单位统计" /> - <div className="options"> - <div className="table"> - <div className="tr"> - <div className="td">相关方类别</div> - <div className="td">一级单位数</div> - <div className="td">一级人数</div> - <div className="td">二级单位数</div> - <div className="td">二级人数</div> - </div> - <div className="scroll"> - <SeamlessScroll list={block4List} step={0.5}> - {block4List.map((item, index) => ( - <div key={index} className="tr"> - <div className="td">{item.relatedPartyCategory}</div> - <div className="td">{item.firstLevelUnits}</div> - <div className="td">{item.firstLevelPersonnel}</div> - <div className="td">{item.secondLevelUnits}</div> - <div className="td">{item.secondLevelPersonnel}</div> - </div> - ))} - </SeamlessScroll> - </div> - </div> - </div> - </div> + <div className="branch-office-index-left"> + <WeatherPreventionPanel /> + <RiskHazardPanel /> + <DepartmentResponsibilityPanel /> + <RelatedPartyStatsPanel /> </div> ); } -export default Connect([NS_BI_STATISTICS], true)(IndexLeft); +export default IndexLeft; diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/index.less b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/index.less deleted file mode 100644 index 4f60fe2..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/index.less +++ /dev/null @@ -1,229 +0,0 @@ -.branch_office_index_left { - .block1 { - background-color: rgba(12, 28, 88, 0.4); - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .weather { - display: flex; - align-items: center; - gap: 40px; - - .icon { - padding: 10px; - display: flex; - align-items: center; - gap: 5px; - - .img { - img { - width: 40px; - height: 40px; - } - } - - .text { - color: #fff; - writing-mode: vertical-lr; - letter-spacing: 5px; - } - } - - .items { - flex: 1; - display: flex; - align-items: center; - justify-content: space-between; - - .item { - flex: 1; - display: flex; - align-items: center; - gap: 10px; - - .img { - img { - width: 42px; - height: 42px; - } - } - - .info { - .label { - font-size: 12px; - color: #fff; - } - - .value { - font-size: 18px; - color: #00e7ff; - font-weight: bold; - } - } - } - } - } - - .alert { - margin-top: 10px; - - .scroll { - height: 20px; - overflow-y: hidden; - - .item { - .title { - font-weight: bold; - font-size: 14px; - } - } - } - } - - .table { - margin-top: 5px; - - .scroll { - height: 60px; - overflow-y: hidden; - } - - .tr { - margin-top: 5px; - display: grid; - grid-template-columns: repeat(3, 1fr); - background-image: linear-gradient(to bottom, rgba(0, 42, 85, 0.38), rgba(1, 37, 74, 0.47), rgba(4, 38, 87, 0)); - border: 1px solid; - border-image: linear-gradient(to top, rgba(8, 41, 87, 0.5), rgba(64, 152, 255, 0.5)) 1; - border-bottom: none; - - .td { - text-align: center; - font-size: 12px; - color: #fff; - padding: 5px; - } - } - } - } - } - - .block2 { - background-color: rgba(12, 28, 88, 0.4); - margin-top: 10px; - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .items { - .item { - position: relative; - margin-top: 5px; - display: flex; - align-items: center; - gap: 19px; - border: 1px solid rgba(0, 168, 255, 0.36); - background-color: rgba(1, 37, 74, 0.47); - border-radius: 4px; - padding: 6px 10px; - - &:first-child { - margin-top: 0; - } - - .decoration { - width: 2px; - height: 15px; - position: absolute; - left: 0; - top: 50%; - transform: translateY(-50%); - } - - .title { - border-radius: 4px; - padding: 10px 7px; - font-weight: bold; - border-width: 1px; - border-style: solid; - flex-basis: 85px; - text-align: center; - } - - .info { - flex: 1; - color: #fff; - font-size: 14px; - - .bottom { - display: flex; - justify-content: space-between; - } - } - } - } - } - } - - .block3 { - background-color: rgba(12, 28, 88, 0.4); - margin-top: 10px; - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - } - } - - .block4 { - background-color: rgba(12, 28, 88, 0.4); - margin-top: 10px; - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .table { - margin-top: 5px; - - .scroll { - height: 60px; - overflow-y: hidden; - } - - .tr { - margin-top: 5px; - display: grid; - grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr; - background-color: rgba(17, 51, 112, 0.8); - - .td { - text-align: center; - font-size: 12px; - color: #fff; - padding: 5px; - } - } - } - } - } -} diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/AreaAlarmPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/AreaAlarmPanel/index.js new file mode 100644 index 0000000..cc422c1 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/AreaAlarmPanel/index.js @@ -0,0 +1,64 @@ +import { Connect } from "@cqsjjb/jjb-dva-runtime"; +import { Spin } from "antd"; +import { useContext, useEffect, useState } from "react"; +import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; +import { NS_BI_STATISTICS } from "~/enumerate/namespace"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import { Context } from "~/pages/Container/Map/js/context"; +import "./index.less"; + +const disposalStatus = { 10: "报警中", 20: "未处置", 30: "已消警" }; + +/** 负责查询并滚动展示封闭区域告警。 */ +function AreaAlarmPanel(props) { + const { portArea, currentBranchOffice } = useContext(Context); + const [records, setRecords] = useState([]); + + useEffect(() => { + const loadRecords = async () => { + const { data = [] } = await props.getAlarmRecordScreenIotDeviceAlarmPage({ + portArea, + corpinfoId: currentBranchOffice, + pageIndex: 1, + pageSize: 9999, + }); + setRecords(data); + }; + loadRecords(); + }, []); + + return ( + <Panel title="区域告警" className="branch-office-index-right__area-alarm"> + <div className="branch-office-area-alarm__content"> + <div className="branch-office-area-alarm__table"> + <div className="branch-office-area-alarm__row"> + <div>区域名称</div> + <div>告警类型</div> + <div>告警时间</div> + <div>处置状态</div> + </div> + <div className="branch-office-area-alarm__body"> + <Spin + spinning={ + props.biStatistics.getAlarmRecordScreenIotDeviceAlarmPageLoading + } + > + <SeamlessScroll list={records} step={0.5}> + {records.map((item, index) => ( + <div className="branch-office-area-alarm__row" key={index}> + <div>{item.fireRegionName}</div> + <div>{item.alarmTypeName}</div> + <div>{item.alarmTime}</div> + <div>{disposalStatus[item.status]}</div> + </div> + ))} + </SeamlessScroll> + </Spin> + </div> + </div> + </div> + </Panel> + ); +} + +export default Connect([NS_BI_STATISTICS], true)(AreaAlarmPanel); diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/AreaAlarmPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/AreaAlarmPanel/index.less new file mode 100644 index 0000000..2105d6b --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/AreaAlarmPanel/index.less @@ -0,0 +1,30 @@ +.branch-office-index-right__area-alarm { + margin-top: 10px; +} + +.branch-office-area-alarm__content { + padding: 10px 15px; +} + +.branch-office-area-alarm__table { + margin-top: 5px; +} + +.branch-office-area-alarm__body { + height: 165px; + overflow-y: hidden; +} + +.branch-office-area-alarm__row { + display: grid; + grid-template-columns: 1.5fr 1fr 2fr 1fr; + margin-top: 5px; + background-color: rgba(17, 51, 112, 0.8); +} + +.branch-office-area-alarm__row div { + padding: 5px; + color: #fff; + font-size: 12px; + text-align: center; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/ClosedAreaPersonnelPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/ClosedAreaPersonnelPanel/index.js new file mode 100644 index 0000000..3ed3ec1 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/ClosedAreaPersonnelPanel/index.js @@ -0,0 +1,245 @@ +import { useMount } from "ahooks"; +import * as echarts from "echarts"; +import { useRef } from "react"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const records = [ + { name: "技术部1", unitCount: 95, personnelCount: 92 }, + { name: "技术部2", unitCount: 95, personnelCount: 92 }, + { name: "技术部3", unitCount: 95, personnelCount: 92 }, + { name: "技术部4", unitCount: 95, personnelCount: 92 }, + { name: "技术部5", unitCount: 95, personnelCount: 92 }, + { name: "技术部6", unitCount: 95, personnelCount: 92 }, + { name: "技术部7", unitCount: 95, personnelCount: 92 }, + { name: "技术部8", unitCount: 95, personnelCount: 92 }, + { name: "技术部9", unitCount: 95, personnelCount: 92 }, + { name: "技术部0", unitCount: 95, personnelCount: 92 }, +]; + +/** 负责展示封闭区域人员状态图表,并管理图表实例生命周期。 */ +function ClosedAreaPersonnelPanel() { + const chartInstance = useRef(null); + const chartRef = useRef(null); + + const initEcharts = (mainRef, chartInstance, data) => { + chartInstance.current = echarts.init(mainRef.current); + + const option = { + color: ["#F1C416", "#0AFCFF"], + tooltip: { + trigger: "axis", + axisPointer: { + type: "shadow", + label: { + backgroundColor: "#6a7985", + }, + }, + }, + grid: { + left: "1%", + right: "4%", + bottom: "6%", + top: 30, + padding: "0 0 10 0", + containLabel: true, + }, + legend: { + right: 10, + top: 0, + itemGap: 16, + itemWidth: 18, + itemHeight: 10, + textStyle: { + color: "#fff", + fontStyle: "normal", + fontFamily: "微软雅黑", + fontSize: 12, + }, + }, + dataZoom: [ + { + type: "inside", + xAxisIndex: 0, + start: 0, + end: 50, + zoomOnMouseWheel: "shift", + moveOnMouseWheel: true, + moveOnMouseMove: true, + }, + { + type: "slider", + height: 6, + bottom: 0, + show: true, + start: 0, + end: 50, + handleSize: 3, + handleStyle: { + color: "#DCE2E8", + }, + xAxisIndex: [0], + filterMode: "filter", + showDetail: false, + }, + ], + xAxis: [ + { + type: "category", + boundaryGap: true, + data: data.map(item => item.name), + axisLabel: { + interval: 0, + margin: 15, + textStyle: { + color: "#fff", + fontStyle: "normal", + fontFamily: "微软雅黑", + fontSize: 12, + }, + }, + axisTick: { + show: false, + }, + axisLine: { + lineStyle: { + color: "#fff", + opacity: 0.2, + }, + }, + splitLine: { + show: false, + }, + }, + ], + yAxis: [ + { + type: "value", + splitNumber: 5, + axisLabel: { + textStyle: { + color: "#fff", + fontStyle: "normal", + fontFamily: "微软雅黑", + fontSize: 12, + }, + }, + axisLine: { + show: false, + }, + axisTick: { + show: false, + }, + splitLine: { + show: true, + lineStyle: { + color: ["#fff"], + opacity: 0.06, + }, + }, + }, + ], + series: [ + { + name: "单位人员数", + type: "pictorialBar", + symbol: "roundRect", + symbolOffset: [-5, 0], + symbolMargin: "1", + barWidth: "10%", + barMaxWidth: "20%", + animationDelay: (dataIndex, params) => { + return params.index * 50; + }, + itemStyle: { + normal: { + color: () => { + return new echarts.graphic.LinearGradient(0, 0, 1, 1, [ + { + offset: 0, + color: "#F1C416", + }, + { + offset: 1, + color: "#FF2F01", + }, + ]); + }, + }, + }, + z: 1, + barGap: 0, + symbolRepeat: true, + symbolSize: [14, 5], + data: data.map(item => item.unitCount), + animationEasing: "elasticOut", + stack: "2", + }, + { + name: "相关方人员数", + type: "pictorialBar", + symbol: "roundRect", + barWidth: "10%", + symbolOffset: [5, 0], + barMaxWidth: "20%", + symbolMargin: "1", + animationDelay: (dataIndex, params) => { + return params.index * 50; + }, + itemStyle: { + normal: { + color: () => { + return new echarts.graphic.LinearGradient(0, 0, 1, 1, [ + { + offset: 0, + color: "#0AFCFF", + }, + { + offset: 1, + color: "#0CA1FE", + }, + ]); + }, + }, + }, + z: 1, + barGap: 0, + symbolRepeat: true, + symbolSize: [14, 5], + data: data.map(item => item.personnelCount), + animationEasing: "elasticOut", + stack: "1", + }, + ], + }; + + chartInstance.current.setOption(option); + }; + + useMount(() => { + initEcharts(chartRef, chartInstance, records); + + return () => { + if (chartInstance.current) { + chartInstance.current.dispose(); + chartInstance.current = null; + } + }; + }); + + return ( + <Panel + title="封闭区域人员状态" + className="branch-office-index-right__personnel" + > + <div className="branch-office-closed-area-personnel__content"> + <div + ref={chartRef} + className="branch-office-closed-area-personnel__chart" + /> + </div> + </Panel> + ); +} + +export default ClosedAreaPersonnelPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/ClosedAreaPersonnelPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/ClosedAreaPersonnelPanel/index.less new file mode 100644 index 0000000..49dc520 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/ClosedAreaPersonnelPanel/index.less @@ -0,0 +1,12 @@ +.branch-office-index-right__personnel { + margin-top: 10px; +} + +.branch-office-closed-area-personnel__content { + padding: 10px 15px; +} + +.branch-office-closed-area-personnel__chart { + width: 100%; + height: 200px; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/GateAndClosedAreaPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/GateAndClosedAreaPanel/index.js new file mode 100644 index 0000000..12256f6 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/GateAndClosedAreaPanel/index.js @@ -0,0 +1,74 @@ +import gateBackground from "~/assets/images/map_bi/content/bg4.png"; +import areaBackground from "~/assets/images/map_bi/content/bg5.png"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const records = [ + { + title: "口门进出统计", + label1: "人数", + label2: "车数", + count1: 123, + count2: 123, + background: gateBackground, + }, + { + title: "进入申请待审批", + label1: "人数", + label2: "车数", + count1: 123, + count2: 123, + background: gateBackground, + }, + { + title: "封闭区域人员情况", + label1: "区域数", + label2: "人数", + count1: 123, + count2: 123, + background: areaBackground, + }, + { + title: "封闭区域作业情况", + label1: "区域数", + label2: "作业数", + count1: 123, + count2: 123, + background: areaBackground, + }, +]; + +/** 负责展示口门和封闭区域的基础统计。 */ +function GateAndClosedAreaPanel() { + return ( + <Panel title="口门及封闭区域"> + <div className="branch-office-gate-closed-area__content"> + {records.map(item => ( + <div + className="branch-office-gate-closed-area__item" + key={item.title} + style={{ backgroundImage: `url(${item.background})` }} + > + <div className="branch-office-gate-closed-area__title"> + {item.title} + </div> + <div className="branch-office-gate-closed-area__values"> + <div> + {item.label1} + : + {item.count1} + </div> + <div> + {item.label2} + : + {item.count2} + </div> + </div> + </div> + ))} + </div> + </Panel> + ); +} + +export default GateAndClosedAreaPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/GateAndClosedAreaPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/GateAndClosedAreaPanel/index.less new file mode 100644 index 0000000..1cc1923 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/GateAndClosedAreaPanel/index.less @@ -0,0 +1,27 @@ +.branch-office-gate-closed-area__content { + display: flex; + flex-wrap: wrap; + gap: 10px 20px; + align-items: center; + justify-content: space-between; + padding: 10px 15px; + color: #fff; + font-size: 12px; +} + +.branch-office-gate-closed-area__item { + width: calc(50% - 10px); + padding: 5px 20px; +} + +.branch-office-gate-closed-area__title { + font-weight: bold; + text-align: center; +} + +.branch-office-gate-closed-area__values { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 10px; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/KeyWorkStatsPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/KeyWorkStatsPanel/index.js new file mode 100644 index 0000000..a071142 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/KeyWorkStatsPanel/index.js @@ -0,0 +1,97 @@ +import { Connect } from "@cqsjjb/jjb-dva-runtime"; +import { Spin } from "antd"; +import { useContext, useEffect, useState } from "react"; +import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; +import { NS_BI_STATISTICS } from "~/enumerate/namespace"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import { Context } from "~/pages/Container/Map/js/context"; +import "./index.less"; + +/** 负责合并两类作业统计并展示部门维度数据。 */ +function KeyWorkStatsPanel(props) { + const { portArea, currentBranchOffice } = useContext(Context); + const [records, setRecords] = useState([]); + + useEffect(() => { + const loadRecords = async () => { + const [{ data: eightWorkData = [] }, { data: keyProjectData = [] }] + = await Promise.all([ + props.getEightWorkInfoScreenDepartmentStatistics({ + portArea, + corpinfoId: currentBranchOffice, + }), + props.getKeyProjectLargeScreenDepartmentStatistics({ + portArea, + corpinfoId: currentBranchOffice, + }), + ]); + const departmentMap = new Map( + keyProjectData.map(item => [ + item.departmentId, + { + departmentName: item.departmentName, + fourNewHomeworkCount: item.fourNewHomeworkCount, + eightWorkCount: 0, + morePeopleCount: item.morePeopleCount, + }, + ]), + ); + eightWorkData.forEach((item) => { + const department = departmentMap.get(item.departmentId) || { + departmentName: item.departmentName, + fourNewHomeworkCount: 0, + eightWorkCount: 0, + morePeopleCount: 0, + }; + department.eightWorkCount = item.workCount || 0; + departmentMap.set(item.departmentId, department); + }); + setRecords([...departmentMap.values()]); + }; + loadRecords(); + }, []); + + return ( + <Panel + title="重点作业情况统计" + className="branch-office-index-right__key-work" + > + <div className="branch-office-key-work-stats__content"> + <div className="branch-office-key-work-stats__table"> + <div className="branch-office-key-work-stats__row"> + <div>部门名称</div> + <div>三人以上作业数</div> + <div>危险作业数</div> + <div>四新作业数</div> + </div> + <div className="branch-office-key-work-stats__body"> + <Spin + spinning={ + props.biStatistics + .getKeyProjectLargeScreenDepartmentStatisticsLoading + || props.biStatistics + .getEightWorkInfoScreenDepartmentStatisticsLoading + } + > + <SeamlessScroll list={records} step={0.5}> + {records.map((item, index) => ( + <div + className="branch-office-key-work-stats__row" + key={index} + > + <div>{item.departmentName}</div> + <div>{item.morePeopleCount}</div> + <div>{item.eightWorkCount}</div> + <div>{item.fourNewHomeworkCount}</div> + </div> + ))} + </SeamlessScroll> + </Spin> + </div> + </div> + </div> + </Panel> + ); +} + +export default Connect([NS_BI_STATISTICS], true)(KeyWorkStatsPanel); diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/KeyWorkStatsPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/KeyWorkStatsPanel/index.less new file mode 100644 index 0000000..0d2c255 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/KeyWorkStatsPanel/index.less @@ -0,0 +1,30 @@ +.branch-office-index-right__key-work { + margin-top: 10px; +} + +.branch-office-key-work-stats__content { + padding: 10px 15px; +} + +.branch-office-key-work-stats__table { + margin-top: 5px; +} + +.branch-office-key-work-stats__body { + height: 165px; + overflow-y: hidden; +} + +.branch-office-key-work-stats__row { + display: grid; + grid-template-columns: repeat(4, 1fr); + margin-top: 5px; + background-color: rgba(17, 51, 112, 0.8); +} + +.branch-office-key-work-stats__row div { + padding: 5px; + color: #fff; + font-size: 12px; + text-align: center; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/echarts.js b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/echarts.js deleted file mode 100644 index 7590c50..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/echarts.js +++ /dev/null @@ -1,195 +0,0 @@ -import * as echarts from "echarts"; - -export const initEcharts1 = (mainRef, chartInstance, data) => { - chartInstance.current = echarts.init(mainRef.current); - - const option = { - color: ["#F1C416", "#0AFCFF"], - tooltip: { - trigger: "axis", - axisPointer: { - type: "shadow", - label: { - backgroundColor: "#6a7985", - }, - }, - }, - grid: { - left: "1%", - right: "4%", - bottom: "6%", - top: 30, - padding: "0 0 10 0", - containLabel: true, - }, - legend: { - right: 10, - top: 0, - itemGap: 16, - itemWidth: 18, - itemHeight: 10, - textStyle: { - color: "#fff", - fontStyle: "normal", - fontFamily: "微软雅黑", - fontSize: 12, - }, - }, - dataZoom: [ - { - type: "inside", - xAxisIndex: 0, - start: 0, - end: 50, - zoomOnMouseWheel: "shift", - moveOnMouseWheel: true, - moveOnMouseMove: true, - }, - { - type: "slider", - height: 6, - bottom: 0, - show: true, - start: 0, - end: 50, - handleSize: 3, - handleStyle: { - color: "#DCE2E8", - }, - xAxisIndex: [0], - filterMode: "filter", - showDetail: false, - }, - ], - xAxis: [ - { - type: "category", - boundaryGap: true, - data: data.map(item => item.name), - axisLabel: { - interval: 0, - margin: 15, - textStyle: { - color: "#fff", - fontStyle: "normal", - fontFamily: "微软雅黑", - fontSize: 12, - }, - }, - axisTick: { - show: false, - }, - axisLine: { - lineStyle: { - color: "#fff", - opacity: 0.2, - }, - }, - splitLine: { - show: false, - }, - }, - ], - yAxis: [ - { - type: "value", - splitNumber: 5, - axisLabel: { - textStyle: { - color: "#fff", - fontStyle: "normal", - fontFamily: "微软雅黑", - fontSize: 12, - }, - }, - axisLine: { - show: false, - }, - axisTick: { - show: false, - }, - splitLine: { - show: true, - lineStyle: { - color: ["#fff"], - opacity: 0.06, - }, - }, - }, - ], - series: [ - { - name: "单位人员数", - type: "pictorialBar", - symbol: "roundRect", - symbolOffset: [-5, 0], - symbolMargin: "1", - barWidth: "10%", - barMaxWidth: "20%", - animationDelay: (dataIndex, params) => { - return params.index * 50; - }, - itemStyle: { - normal: { - color: () => { - return new echarts.graphic.LinearGradient(0, 0, 1, 1, [ - { - offset: 0, - color: "#F1C416", - }, - { - offset: 1, - color: "#FF2F01", - }, - ]); - }, - }, - }, - z: 1, - barGap: 0, - symbolRepeat: true, - symbolSize: [14, 5], - data: data.map(item => item.unitCount), - animationEasing: "elasticOut", - stack: "2", - }, - { - name: "相关方人员数", - type: "pictorialBar", - symbol: "roundRect", - barWidth: "10%", - symbolOffset: [5, 0], - barMaxWidth: "20%", - symbolMargin: "1", - animationDelay: (dataIndex, params) => { - return params.index * 50; - }, - itemStyle: { - normal: { - color: (params) => { - return new echarts.graphic.LinearGradient(0, 0, 1, 1, [ - { - offset: 0, - color: "#0AFCFF", - }, - { - offset: 1, - color: "#0CA1FE", - }, - ]); - }, - }, - }, - z: 1, - barGap: 0, - symbolRepeat: true, - symbolSize: [14, 5], - data: data.map(item => item.personnelCount), - animationEasing: "elasticOut", - stack: "1", - }, - ], - }; - - chartInstance.current.setOption(option); -}; diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/index.js index 6447a22..d53d2d6 100644 --- a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/index.js +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/index.js @@ -1,194 +1,18 @@ -import { Connect } from "@cqsjjb/jjb-dva-runtime"; -import { useMount } from "ahooks"; -import { Spin } from "antd"; -import { useContext, useEffect, useRef, useState } from "react"; -import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import bg4 from "~/assets/images/map_bi/content/bg4.png"; -import bg5 from "~/assets/images/map_bi/content/bg5.png"; -import { NS_BI_STATISTICS } from "~/enumerate/namespace"; -import Title from "~/pages/Container/Map/components/Content/branchOffice/Title"; -import { Context } from "~/pages/Container/Map/js/context"; -import { initEcharts1 } from "./echarts"; -import "./index.less"; - -function IndexRight(props) { - const { portArea, currentBranchOffice } = useContext(Context); - - const [block1List, setBlock1List] = useState([ - { title: "口门进出统计", label1: "人数", label2: "车数", count1: 123, count2: 123, bgImg: bg4 }, - { title: "进入申请待审批", label1: "人数", label2: "车数", count1: 123, count2: 123, bgImg: bg4 }, - { title: "封闭区域人员情况", label1: "区域数", label2: "人数", count1: 123, count2: 123, bgImg: bg5 }, - { title: "封闭区域作业情况", label1: "区域数", label2: "作业数", count1: 123, count2: 123, bgImg: bg5 }, - ]); - const [block3List, setBlock3List] = useState([]); - const [block4List, setBlock4List] = useState([]); - - const chartInstance = useRef(null); - const main1Ref = useRef(null); - - useMount(() => { - initEcharts1(main1Ref, chartInstance, [ - { name: "技术部1", unitCount: 95, personnelCount: 92 }, - { name: "技术部2", unitCount: 95, personnelCount: 92 }, - { name: "技术部3", unitCount: 95, personnelCount: 92 }, - { name: "技术部4", unitCount: 95, personnelCount: 92 }, - { name: "技术部5", unitCount: 95, personnelCount: 92 }, - { name: "技术部6", unitCount: 95, personnelCount: 92 }, - { name: "技术部7", unitCount: 95, personnelCount: 92 }, - { name: "技术部8", unitCount: 95, personnelCount: 92 }, - { name: "技术部9", unitCount: 95, personnelCount: 92 }, - { name: "技术部0", unitCount: 95, personnelCount: 92 }, - ]); - - return () => { - if (chartInstance.current) { - chartInstance.current.dispose(); - chartInstance.current = null; - } - }; - }); - - const getAlarmRecordScreenIotDeviceAlarmPage = async () => { - const { data } = await props.getAlarmRecordScreenIotDeviceAlarmPage({ - portArea, - corpinfoId: currentBranchOffice, - pageIndex: 1, - pageSize: 9999, - }); - setBlock3List(data); - }; - - const getBlock4List = async () => { - const [{ data: eightWorkData = [] }, { data: keyProjectData = [] }] - = await Promise.all([ - props.getEightWorkInfoScreenDepartmentStatistics({ - portArea, - corpinfoId: currentBranchOffice, - }), - props.getKeyProjectLargeScreenDepartmentStatistics({ - portArea, - corpinfoId: currentBranchOffice, - }), - ]); - const departmentMap = new Map( - keyProjectData.map(item => [ - item.departmentId, - { - departmentName: item.departmentName, - fourNewHomeworkCount: item.fourNewHomeworkCount, - eightWorkCount: 0, - morePeopleCount: item.morePeopleCount, - }, - ]), - ); - eightWorkData.forEach((item) => { - const department = departmentMap.get(item.departmentId) || { - departmentName: item.departmentName, - fourNewHomeworkCount: 0, - eightWorkCount: 0, - morePeopleCount: 0, - }; - department.eightWorkCount = item.workCount || 0; - departmentMap.set(item.departmentId, department); - }); - setBlock4List([...departmentMap.values()]); - }; - - useEffect(() => { - getAlarmRecordScreenIotDeviceAlarmPage(); - getBlock4List(); - }, []); +import AreaAlarmPanel from "./AreaAlarmPanel"; +import ClosedAreaPersonnelPanel from "./ClosedAreaPersonnelPanel"; +import GateAndClosedAreaPanel from "./GateAndClosedAreaPanel"; +import KeyWorkStatsPanel from "./KeyWorkStatsPanel"; +/** 分公司首页右侧区域仅负责按展示顺序组合业务区块。 */ +function IndexRight() { return ( - <div className="branch_office_index_right"> - <div className="block1"> - <Title title="口门及封闭区域" /> - <div className="options"> - <div className="items"> - { - block1List.map((item, index) => ( - <div className="item" key={index} style={{ backgroundImage: `url(${item.bgImg})` }}> - <div className="title">{item.title}</div> - <div className="info"> - <div> - {item.label1} - : - {item.count1} - </div> - <div> - {item.label2} - : - {item.count2} - </div> - </div> - </div> - )) - } - </div> - </div> - </div> - <div className="block2"> - <Title title="封闭区域人员状态" /> - <div className="options"> - <div ref={main1Ref} style={{ width: "100%", height: "200px" }} /> - </div> - </div> - <div className="block3"> - <Title title="区域告警" /> - <div className="options"> - <div className="table"> - <div className="tr"> - <div className="td">区域名称</div> - <div className="td">告警类型</div> - <div className="td">告警时间</div> - <div className="td">处置状态</div> - </div> - <div className="scroll"> - <Spin spinning={props.biStatistics.getAlarmRecordScreenIotDeviceAlarmPageLoading}> - <SeamlessScroll list={block3List} step={0.5}> - {block3List.map((item, index) => ( - <div key={index} className="tr"> - <div className="td">{item.fireRegionName}</div> - <div className="td">{item.alarmTypeName}</div> - <div className="td">{item.alarmTime}</div> - <div className="td">{{ 10: "报警中", 20: "未处置", 30: "已消警" }[item.status]}</div> - </div> - ))} - </SeamlessScroll> - </Spin> - </div> - </div> - </div> - </div> - <div className="block4"> - <Title title="重点作业情况统计" /> - <div className="options"> - <div className="table"> - <div className="tr"> - <div className="td">部门名称</div> - <div className="td">三人以上作业数</div> - <div className="td">危险作业数</div> - <div className="td">四新作业数</div> - </div> - <div className="scroll"> - <Spin spinning={props.biStatistics.getKeyProjectLargeScreenDepartmentStatisticsLoading || props.biStatistics.getEightWorkInfoScreenDepartmentStatisticsLoading}> - <SeamlessScroll list={block4List} step={0.5}> - {block4List.map((item, index) => ( - <div key={index} className="tr"> - <div className="td">{item.departmentName}</div> - <div className="td">{item.morePeopleCount}</div> - <div className="td">{item.eightWorkCount}</div> - <div className="td">{item.fourNewHomeworkCount}</div> - </div> - ))} - </SeamlessScroll> - </Spin> - </div> - </div> - </div> - </div> + <div className="branch-office-index-right"> + <GateAndClosedAreaPanel /> + <ClosedAreaPersonnelPanel /> + <AreaAlarmPanel /> + <KeyWorkStatsPanel /> </div> ); } -export default Connect([NS_BI_STATISTICS], true)(IndexRight); +export default IndexRight; diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/index.less b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/index.less deleted file mode 100644 index b7f939a..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/index.less +++ /dev/null @@ -1,129 +0,0 @@ -.branch_office_index_right { - .block1 { - background-color: rgba(12, 28, 88, 0.4); - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .items { - color: #fff; - font-size: 12px; - display: flex; - align-items: center; - flex-wrap: wrap; - justify-content: space-between; - gap: 10px 20px; - - .item { - width: calc(50% - 10px); - padding: 5px 20px; - - .title { - font-weight: bold; - text-align: center; - } - - .info { - margin-top: 10px; - display: flex; - align-items: center; - justify-content: space-between; - } - } - } - } - } - - .block2 { - background-color: rgba(12, 28, 88, 0.4); - margin-top: 10px; - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - } - } - - .block3 { - background-color: rgba(12, 28, 88, 0.4); - margin-top: 10px; - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .table { - margin-top: 5px; - - .scroll { - height: 165px; - overflow-y: hidden; - } - - .tr { - margin-top: 5px; - display: grid; - grid-template-columns: 1.5fr 1fr 2fr 1fr; - background-color: rgba(17, 51, 112, 0.8); - - .td { - text-align: center; - font-size: 12px; - color: #fff; - padding: 5px; - } - } - } - } - } - - .block4 { - background-color: rgba(12, 28, 88, 0.4); - margin-top: 10px; - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .table { - margin-top: 5px; - - .scroll { - height: 165px; - overflow-y: hidden; - } - - .tr { - margin-top: 5px; - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr; - background-color: rgba(17, 51, 112, 0.8); - - .td { - text-align: center; - font-size: 12px; - color: #fff; - padding: 5px; - } - } - } - } - } -} diff --git a/src/pages/Container/Map/components/Content/branchOffice/MenJin/ClosedAreaStatsPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/MenJin/ClosedAreaStatsPanel/index.js new file mode 100644 index 0000000..c80f982 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/MenJin/ClosedAreaStatsPanel/index.js @@ -0,0 +1,55 @@ +import CountUp from "react-countup"; +import closedAreaIcon from "~/assets/images/map_bi/content/ico15.png"; +import vehiclePassageIcon from "~/assets/images/map_bi/content/ico16.png"; +import pedestrianPassageIcon from "~/assets/images/map_bi/content/ico17.png"; +import peopleIcon from "~/assets/images/map_bi/content/ico18.png"; +import cornerIcon from "~/assets/images/map_bi/content/icon32.png"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const records = [ + { label: "封闭区域数", count: 123, icon: closedAreaIcon }, + { label: "车行通道数", count: 123, icon: vehiclePassageIcon }, + { label: "人行通道数", count: 123, icon: pedestrianPassageIcon }, + { label: "封闭区域当前人数", count: 123, icon: peopleIcon }, +]; + +/** 负责展示封闭区域及通道基础统计。 */ +function ClosedAreaStatsPanel() { + return ( + <Panel title="封闭区域统计"> + <div className="branch-office-gate-stats__content"> + {records.map(item => ( + <div className="branch-office-gate-stats__item" key={item.label}> + <div className="branch-office-gate-stats__image"> + <img src={item.icon} alt="" /> + </div> + <div className="branch-office-gate-stats__info"> + <div className="branch-office-gate-stats__label"> + {item.label} + </div> + <div className="branch-office-gate-stats__count"> + <CountUp end={+item.count} /> + </div> + </div> + <Corner className="branch-office-gate-stats__corner--top-left" /> + <Corner className="branch-office-gate-stats__corner--top-right" /> + <Corner className="branch-office-gate-stats__corner--bottom-left" /> + <Corner className="branch-office-gate-stats__corner--bottom-right" /> + </div> + ))} + </div> + </Panel> + ); +} + +/** 封闭区域统计卡片的四角装饰图标。 */ +function Corner({ className }) { + return ( + <div className={`branch-office-gate-stats__corner ${className}`}> + <img src={cornerIcon} alt="" /> + </div> + ); +} + +export default ClosedAreaStatsPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/MenJin/ClosedAreaStatsPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/MenJin/ClosedAreaStatsPanel/index.less new file mode 100644 index 0000000..f2ae9b4 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/MenJin/ClosedAreaStatsPanel/index.less @@ -0,0 +1,65 @@ +.branch-office-gate-stats__content { + display: flex; + flex-wrap: wrap; + gap: 10px 20px; + align-items: center; + justify-content: space-between; + padding: 10px 15px; +} + +.branch-office-gate-stats__item { + position: relative; + display: flex; + width: calc(50% - 10px); + align-items: center; + padding: 9px 0 9px 9px; + color: #fff; + background-color: rgba(1, 56, 154, 0.26); + border: 1px solid #2091f3; +} + +.branch-office-gate-stats__image { + padding: 8px; + margin-right: 10px; + background-color: #102561; + border: 1px solid #19455b; + border-radius: 4px; +} + +.branch-office-gate-stats__image img { + width: 35px; + height: 30px; +} + +.branch-office-gate-stats__label { + font-weight: bold; +} + +.branch-office-gate-stats__corner { + position: absolute; + width: 12px; + height: 12px; +} + +.branch-office-gate-stats__corner--top-left { + top: -10px; + left: -3px; +} + +.branch-office-gate-stats__corner--top-right { + top: -3px; + right: -10px; + transform: rotate(90deg); +} + +.branch-office-gate-stats__corner--bottom-left { + bottom: -3px; + left: -10px; + transform: rotate(270deg); +} + +.branch-office-gate-stats__corner--bottom-right { + right: -3px; + bottom: -10px; + transform: rotate(180deg); +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/MenJin/EntryExitTrendPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/MenJin/EntryExitTrendPanel/index.js new file mode 100644 index 0000000..98cfc93 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/MenJin/EntryExitTrendPanel/index.js @@ -0,0 +1,202 @@ +import { useMount } from "ahooks"; +import * as echarts from "echarts"; +import { useRef, useState } from "react"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const tabs = ["人员", "车辆"]; +const records = [ + { NAME: "零号门", IN: "0", OUT: "0" }, + { NAME: "一号门", IN: "15", OUT: "12" }, + { NAME: "二号门", IN: "8", OUT: "10" }, + { NAME: "三号门", IN: "22", OUT: "18" }, + { NAME: "四号门", IN: "5", OUT: "7" }, + { NAME: "五号门", IN: "30", OUT: "25" }, + { NAME: "六号门", IN: "12", OUT: "14" }, + { NAME: "七号门", IN: "18", OUT: "16" }, + { NAME: "八号门", IN: "9", OUT: "11" }, + { NAME: "九号门", IN: "25", OUT: "20" }, +]; + +/** 负责展示封闭区域进出趋势并管理图表实例生命周期。 */ +function EntryExitTrendPanel() { + const [activeIndex, setActiveIndex] = useState(0); + const chartInstance = useRef(null); + const chartRef = useRef(null); + + const initEcharts = (mainRef, chartInstance, data) => { + chartInstance.current = echarts.init(mainRef.current); + + const inData = data.map(item => Number.parseInt(item.IN)); + const outData = data.map(item => Number.parseInt(item.OUT)); + const names = data.map(item => item.NAME); + + const option = { + tooltip: { + trigger: "axis", + axisPointer: { + type: "none", + }, + }, + legend: { + data: ["进", "出"], + top: "0%", + right: "0%", + textStyle: { + color: "#fff", + fontSize: 14, + }, + itemWidth: 12, + itemHeight: 10, + }, + dataZoom: [ + { + type: "inside", + xAxisIndex: 0, + start: 0, + end: 50, + zoomOnMouseWheel: "shift", + moveOnMouseWheel: true, + moveOnMouseMove: true, + }, + { + type: "slider", + height: 6, + bottom: "7%", + show: true, + start: 0, + end: 50, + handleSize: 5, + handleStyle: { + color: "#DCE2E8", + }, + xAxisIndex: [0], + filterMode: "filter", + showDetail: false, + }, + ], + grid: { + left: "10%", + right: "5%", + top: "10%", + bottom: "20%", + }, + xAxis: [ + { + type: "category", + data: names, + axisTick: { + alignWithLabel: true, + }, + axisLabel: { + color: "#fff", + }, + }, + ], + yAxis: { + axisLine: { + show: false, + }, + axisTick: { + show: false, + }, + axisLabel: { + color: "#fff", + }, + splitLine: { + show: true, + lineStyle: { + type: "dashed", + }, + }, + }, + series: [ + { + name: "进", + type: "bar", + backgroundStyle: { + color: "rgba(216, 229, 247, 0.55)", + borderRadius: [8, 8, 0, 0], + }, + itemStyle: { + borderRadius: [12, 12, 0, 0], + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 1, + color: "rgba(121,103,255,0.7)", + }, + { + offset: 0, + color: "#7967FF", + }, + ]), + }, + barWidth: "10", + data: inData, + }, + { + name: "出", + type: "bar", + backgroundStyle: { + color: "rgba(216, 229, 247, 0.55)", + borderRadius: [8, 8, 0, 0], + }, + itemStyle: { + borderRadius: [12, 12, 0, 0], + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 1, + color: "rgba(255,198,124,0.7)", + }, + { + offset: 0, + color: "#FFC67C", + }, + ]), + }, + barWidth: "10", + data: outData, + }, + ], + }; + + chartInstance.current.setOption(option); + }; + + useMount(() => { + initEcharts(chartRef, chartInstance, records); + + return () => { + if (chartInstance.current) { + chartInstance.current.dispose(); + chartInstance.current = null; + } + }; + }); + + return ( + <Panel + title="封闭区域进出情况统计" + className="branch-office-gate__trend" + extra={( + <div className="branch-office-gate-trend__tabs"> + {tabs.map((item, index) => ( + <div + className={`branch-office-gate-trend__tab ${index === activeIndex ? "branch-office-gate-trend__tab--active" : ""}`} + key={item} + onClick={() => setActiveIndex(index)} + > + {item} + </div> + ))} + </div> + )} + > + <div className="branch-office-gate-trend__content"> + <div ref={chartRef} className="branch-office-gate-trend__chart" /> + </div> + </Panel> + ); +} + +export default EntryExitTrendPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/MenJin/EntryExitTrendPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/MenJin/EntryExitTrendPanel/index.less new file mode 100644 index 0000000..07c1d89 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/MenJin/EntryExitTrendPanel/index.less @@ -0,0 +1,31 @@ +.branch-office-gate__trend { + margin-top: 10px; +} + +.branch-office-gate-trend__tabs { + display: flex; +} + +.branch-office-gate-trend__tab { + padding: 2px 12px; + color: #fff; + font-size: 12px; + cursor: pointer; + background-color: rgba(36, 115, 239, 0.27); + border: 1px solid #5d718c; + border-radius: 2px; +} + +.branch-office-gate-trend__tab--active { + background-color: #2473ef; + border-color: #2473ef; +} + +.branch-office-gate-trend__content { + padding: 10px 15px; +} + +.branch-office-gate-trend__chart { + width: 100%; + height: 300px; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/MenJin/GateApplicationPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/MenJin/GateApplicationPanel/index.js new file mode 100644 index 0000000..a8bcfc2 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/MenJin/GateApplicationPanel/index.js @@ -0,0 +1,125 @@ +import { useState } from "react"; +import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const tabs = ["人员", "车辆"]; +const records = [ + { + permissionRange: "A区", + status: "1", + applicant: "张三", + approver: "李主管", + }, + { + permissionRange: "B区", + status: "1", + applicant: "李四", + approver: "王经理", + }, + { + permissionRange: "C区", + status: "1", + applicant: "王五", + approver: "赵主任", + }, + { + permissionRange: "D区", + status: "0", + applicant: "赵六", + approver: "钱总监", + }, + { + permissionRange: "E区", + status: "0", + applicant: "钱七", + approver: "孙部长", + }, + { + permissionRange: "F区", + status: "0", + applicant: "孙八", + approver: "周经理", + }, + { + permissionRange: "G区", + status: "0", + applicant: "周九", + approver: "吴主任", + }, + { + permissionRange: "H区", + status: "1", + applicant: "吴十", + approver: "郑主管", + }, + { + permissionRange: "I区", + status: "0", + applicant: "郑一", + approver: "王总监", + }, + { + permissionRange: "J区", + status: "1", + applicant: "王二", + approver: "张部长", + }, +]; + +/** 负责展示口门申请记录及人员、车辆分类标签状态。 */ +function GateApplicationPanel() { + const [activeIndex, setActiveIndex] = useState(0); + + return ( + <Panel + title="口门申请记录" + className="branch-office-gate__application" + extra={( + <div className="branch-office-gate-application__tabs"> + {tabs.map((item, index) => ( + <div + className={`branch-office-gate-application__tab ${index === activeIndex ? "branch-office-gate-application__tab--active" : ""}`} + key={item} + onClick={() => setActiveIndex(index)} + > + {item} + </div> + ))} + </div> + )} + > + <div className="branch-office-gate-application__content"> + <div className="branch-office-gate-application__table"> + <div className="branch-office-gate-application__row"> + <div>权限范围</div> + <div>申请类型</div> + <div>申请人</div> + <div>审批人</div> + </div> + <div className="branch-office-gate-application__body"> + <SeamlessScroll list={records} step={0.5}> + {records.map((item, index) => ( + <div + className="branch-office-gate-application__row" + key={index} + > + <div>{item.permissionRange}</div> + <div + className={`branch-office-gate-application__type branch-office-gate-application__type--${item.status}`} + > + {item.status === "1" ? "临时" : "长期"} + </div> + <div>{item.applicant}</div> + <div>{item.approver}</div> + </div> + ))} + </SeamlessScroll> + </div> + </div> + </div> + </Panel> + ); +} + +export default GateApplicationPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/MenJin/GateApplicationPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/MenJin/GateApplicationPanel/index.less new file mode 100644 index 0000000..622ee7b --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/MenJin/GateApplicationPanel/index.less @@ -0,0 +1,53 @@ +.branch-office-gate__application { + margin-top: 10px; +} + +.branch-office-gate-application__tabs { + display: flex; +} + +.branch-office-gate-application__tab { + padding: 2px 12px; + color: #fff; + font-size: 12px; + cursor: pointer; + background-color: rgba(36, 115, 239, 0.27); + border: 1px solid #5d718c; + border-radius: 2px; +} + +.branch-office-gate-application__tab--active { + background-color: #2473ef; + border-color: #2473ef; +} + +.branch-office-gate-application__content { + padding: 10px; +} + +.branch-office-gate-application__body { + height: 300px; + overflow-y: hidden; +} + +.branch-office-gate-application__row { + display: grid; + grid-template-columns: repeat(4, 1fr); + background-color: rgba(42, 86, 158, 0.53); +} + +.branch-office-gate-application__body +.branch-office-gate-application__row:nth-child(odd) { + background-color: transparent; +} + +.branch-office-gate-application__row div { + padding: 5px; + color: #fff; + font-size: 12px; + text-align: center; +} + +.branch-office-gate-application__row .branch-office-gate-application__type--1 { + color: #fc6b13; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/MenJin/echarts.js b/src/pages/Container/Map/components/Content/branchOffice/MenJin/echarts.js deleted file mode 100644 index 1d27f68..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/MenJin/echarts.js +++ /dev/null @@ -1,140 +0,0 @@ -import * as echarts from "echarts"; - -export const initEcharts1 = (mainRef, chartInstance, data) => { - chartInstance.current = echarts.init(mainRef.current); - - const inData = data.map(item => Number.parseInt(item.IN)); - const outData = data.map(item => Number.parseInt(item.OUT)); - const names = data.map(item => item.NAME); - - const option = { - tooltip: { - trigger: "axis", - axisPointer: { - type: "none", - }, - }, - legend: { - data: ["进", "出"], - top: "0%", - right: "0%", - textStyle: { - color: "#fff", - fontSize: 14, - }, - itemWidth: 12, - itemHeight: 10, - }, - dataZoom: [ - { - type: "inside", - xAxisIndex: 0, - start: 0, - end: 50, - zoomOnMouseWheel: "shift", - moveOnMouseWheel: true, - moveOnMouseMove: true, - }, - { - type: "slider", - height: 6, - bottom: "7%", - show: true, - start: 0, - end: 50, - handleSize: 5, - handleStyle: { - color: "#DCE2E8", - }, - xAxisIndex: [0], - filterMode: "filter", - showDetail: false, - }, - ], - grid: { - left: "10%", - right: "5%", - top: "10%", - bottom: "20%", - }, - xAxis: [ - { - type: "category", - data: names, - axisTick: { - alignWithLabel: true, - }, - axisLabel: { - color: "#fff", - }, - }, - ], - yAxis: { - axisLine: { - show: false, - }, - axisTick: { - show: false, - }, - axisLabel: { - color: "#fff", - }, - splitLine: { - show: true, - lineStyle: { - type: "dashed", - }, - }, - }, - series: [ - { - name: "进", - type: "bar", - backgroundStyle: { - color: "rgba(216, 229, 247, 0.55)", - borderRadius: [8, 8, 0, 0], - }, - itemStyle: { - borderRadius: [12, 12, 0, 0], - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { - offset: 1, - color: "rgba(121,103,255,0.7)", - }, - { - offset: 0, - color: "#7967FF", - }, - ]), - }, - barWidth: "10", - data: inData, - }, - { - name: "出", - type: "bar", - backgroundStyle: { - color: "rgba(216, 229, 247, 0.55)", - borderRadius: [8, 8, 0, 0], - }, - itemStyle: { - borderRadius: [12, 12, 0, 0], - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { - offset: 1, - color: "rgba(255,198,124,0.7)", - }, - { - offset: 0, - color: "#FFC67C", - }, - ]), - }, - barWidth: "10", - data: outData, - }, - ], - }; - - chartInstance.current.setOption(option); -}; diff --git a/src/pages/Container/Map/components/Content/branchOffice/MenJin/index.js b/src/pages/Container/Map/components/Content/branchOffice/MenJin/index.js index 5287b5d..4b4820d 100644 --- a/src/pages/Container/Map/components/Content/branchOffice/MenJin/index.js +++ b/src/pages/Container/Map/components/Content/branchOffice/MenJin/index.js @@ -1,172 +1,14 @@ -import { useMount } from "ahooks"; -import { useRef, useState } from "react"; -import CountUp from "react-countup"; -import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import icon1 from "~/assets/images/map_bi/content/ico15.png"; -import icon2 from "~/assets/images/map_bi/content/ico16.png"; -import icon3 from "~/assets/images/map_bi/content/ico17.png"; -import icon4 from "~/assets/images/map_bi/content/ico18.png"; -import icon5 from "~/assets/images/map_bi/content/icon32.png"; -import Title from "~/pages/Container/Map/components/Content/branchOffice/Title"; -import { initEcharts1 } from "./echarts"; -import "./index.less"; +import ClosedAreaStatsPanel from "./ClosedAreaStatsPanel"; +import EntryExitTrendPanel from "./EntryExitTrendPanel"; +import GateApplicationPanel from "./GateApplicationPanel"; -const block2OptionsNavList = ["人员", "车辆"]; -const block3OptionsNavList = ["人员", "车辆"]; +/** 分公司门禁模块仅负责按展示顺序组合业务区块。 */ function MenJin() { - const [block1List, setBlock1List] = useState([ - { label: "封闭区域数", count: 123, icon: icon1 }, - { label: "车行通道数", count: 123, icon: icon2 }, - { label: "人行通道数", count: 123, icon: icon3 }, - { label: "封闭区域当前人数", count: 123, icon: icon4 }, - ]); - const [block2Index, setBlock2Index] = useState(0); - const [block3Index, setBlock3Index] = useState(0); - const [block3List, setBlock3List] = useState([ - { permissionRange: "A区", status: "1", applicant: "张三", approver: "李主管" }, - { permissionRange: "B区", status: "1", applicant: "李四", approver: "王经理" }, - { permissionRange: "C区", status: "1", applicant: "王五", approver: "赵主任" }, - { permissionRange: "D区", status: "0", applicant: "赵六", approver: "钱总监" }, - { permissionRange: "E区", status: "0", applicant: "钱七", approver: "孙部长" }, - { permissionRange: "F区", status: "0", applicant: "孙八", approver: "周经理" }, - { permissionRange: "G区", status: "0", applicant: "周九", approver: "吴主任" }, - { permissionRange: "H区", status: "1", applicant: "吴十", approver: "郑主管" }, - { permissionRange: "I区", status: "0", applicant: "郑一", approver: "王总监" }, - { permissionRange: "J区", status: "1", applicant: "王二", approver: "张部长" }, - ]); - - const chartInstance = useRef(null); - const main1Ref = useRef(null); - - useMount(() => { - initEcharts1(main1Ref, chartInstance, [ - { NAME: "零号门", IN: "0", OUT: "0" }, - { NAME: "一号门", IN: "15", OUT: "12" }, - { NAME: "二号门", IN: "8", OUT: "10" }, - { NAME: "三号门", IN: "22", OUT: "18" }, - { NAME: "四号门", IN: "5", OUT: "7" }, - { NAME: "五号门", IN: "30", OUT: "25" }, - { NAME: "六号门", IN: "12", OUT: "14" }, - { NAME: "七号门", IN: "18", OUT: "16" }, - { NAME: "八号门", IN: "9", OUT: "11" }, - { NAME: "九号门", IN: "25", OUT: "20" }, - ]); - - return () => { - if (chartInstance.current) { - chartInstance.current.dispose(); - chartInstance.current = null; - } - }; - }); - - const block2OptionsClick = (index) => { - if (index === block2Index) - return; - setBlock2Index(index); - }; - - const block3OptionsClick = (index) => { - if (index === block3Index) - return; - setBlock3Index(index); - }; - return ( - <div className="branch_office_menjin"> - <div className="block1"> - <Title title="封闭区域统计" /> - <div className="options"> - <div className="items"> - { - block1List.map((item, index) => ( - <div className="item" key={index}> - <div className="img"> - <img src={item.icon} alt="" /> - </div> - <div className="info"> - <div className="label">{item.label}</div> - <div className="count"> - <CountUp end={+item.count}></CountUp> - </div> - </div> - <div className="corner-img top-left-img"> - <img src={icon5} alt="" /> - </div> - <div className="corner-img top-right-img"> - <img src={icon5} alt="" /> - </div> - <div className="corner-img bottom-left-img"> - <img src={icon5} alt="" /> - </div> - <div className="corner-img bottom-right-img"> - <img src={icon5} alt="" /> - </div> - </div> - )) - } - </div> - </div> - </div> - <div className="block2"> - <Title - title="封闭区域进出情况统计" - extra={( - <div className="tabs"> - {block2OptionsNavList.map((item, index) => ( - <div - key={index} - className={`tab ${index === block2Index ? "active" : ""}`} - onClick={() => block2OptionsClick(index)} - > - {item} - </div> - ))} - </div> - )} - /> - <div className="options"> - <div ref={main1Ref} style={{ width: "100%", height: "300px" }} /> - </div> - </div> - <div className="block3"> - <Title - title="口门申请记录" - extra={( - <div className="tabs"> - {block3OptionsNavList.map((item, index) => ( - <div - key={index} - className={`tab ${index === block3Index ? "active" : ""}`} - onClick={() => block3OptionsClick(index)} - > - {item} - </div> - ))} - </div> - )} - /> - <div className="table"> - <div className="tr"> - <div className="td">权限范围</div> - <div className="td">申请类型</div> - <div className="td">申请人</div> - <div className="td">审批人</div> - </div> - <div className="scroll"> - <SeamlessScroll list={block3List} step={0.5}> - {block3List.map((item, index) => ( - <div key={index} className="tr"> - <div className="td">{item.permissionRange}</div> - <div className="td" style={{ color: item.status === "1" ? "#FC6B13" : "#fff" }}>{item.status === "1" ? "临时" : "长期"}</div> - <div className="td">{item.applicant}</div> - <div className="td">{item.approver}</div> - </div> - ))} - </SeamlessScroll> - </div> - </div> - </div> + <div className="branch-office-gate"> + <ClosedAreaStatsPanel /> + <EntryExitTrendPanel /> + <GateApplicationPanel /> </div> ); } diff --git a/src/pages/Container/Map/components/Content/branchOffice/MenJin/index.less b/src/pages/Container/Map/components/Content/branchOffice/MenJin/index.less deleted file mode 100644 index d7a2251..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/MenJin/index.less +++ /dev/null @@ -1,168 +0,0 @@ -.branch_office_menjin { - .tabs-style() { - display: flex; - - .tab { - background-color: rgba(36, 115, 239, 0.27); - padding: 2px 12px; - border-radius: 2px; - color: #fff; - font-size: 12px; - border: 1px solid #5d718c; - cursor: pointer; - - &.active { - background-color: #2473ef; - border: 1px solid #2473ef; - } - } - } - - .block1 { - background-color: rgba(12, 28, 88, 0.4); - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .items { - display: flex; - align-items: center; - flex-wrap: wrap; - justify-content: space-between; - gap: 10px 20px; - - .item { - position: relative; - width: calc(50% - 10px); - display: flex; - align-items: center; - color: #fff; - padding: 9px 0 9px 9px; - background-color: rgba(1, 56, 154, 0.26); - border: 1px solid #2091F3; - - .img { - background-color: #102561; - border: 1px solid #19455B; - border-radius: 4px; - padding: 8px; - margin-right: 10px; - - img { - width: 35px; - height: 30px; - } - } - - .info { - .label { - font-weight: bold; - } - - .count { - } - } - - .corner-img { - position: absolute; - width: 12px; - height: 12px; - - &.top-left-img { - top: -10px; - left: -3px; - } - - &.top-right-img { - transform: rotate(90deg); - top: -3px; - right: -10px; - } - - &.bottom-left-img { - transform: rotate(270deg); - bottom: -3px; - left: -10px; - } - - &.bottom-right-img { - transform: rotate(180deg); - bottom: -10px; - right: -3px; - } - } - } - } - } - } - - .block2 { - background-color: rgba(12, 28, 88, 0.4); - margin-top: 10px; - - .tabs { - .tabs-style(); - } - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - } - } - - .block3 { - background-image: linear-gradient(to bottom, - rgba(0, 0, 0, 0), - rgba(0, 0, 0, 0.8)); - margin-top: 10px; - - .tabs { - .tabs-style(); - } - - .table { - padding: 10px; - - .scroll { - height: 300px; - overflow-y: hidden; - - .tr { - &:nth-child(odd) { - background-color: transparent; - } - } - } - - .tr { - display: grid; - grid-template-columns: repeat(4, 1fr); - background-color: rgba(42, 86, 158, 0.53); - - .td { - text-align: center; - font-size: 12px; - color: #fff; - padding: 5px; - - &.green { - color: #7ccf41; - } - - &.yellow { - color: #ffcb05; - } - } - } - } - } -} diff --git a/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherAlarmPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherAlarmPanel/index.js new file mode 100644 index 0000000..6636209 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherAlarmPanel/index.js @@ -0,0 +1,46 @@ +import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const records = [ + { source: "国家气象局", level: "蓝色", area: "北京市朝阳区" }, + { source: "地方气象台", level: "黄色", area: "上海市浦东新区" }, + { source: "中央气象台", level: "橙色", area: "广州市天河区" }, + { source: "区域气象中心", level: "红色", area: "深圳市南山区" }, + { source: "国家气象局", level: "蓝色", area: "杭州市西湖区" }, + { source: "地方气象台", level: "黄色", area: "成都市锦江区" }, + { source: "中央气象台", level: "橙色", area: "武汉市江汉区" }, + { source: "区域气象中心", level: "红色", area: "西安市雁塔区" }, + { source: "国家气象局", level: "蓝色", area: "南京市鼓楼区" }, + { source: "地方气象台", level: "黄色", area: "重庆市渝中区" }, +]; + +/** 负责展示本地气象告警信息表格。 */ +function WeatherAlarmPanel() { + return ( + <Panel title="气象告警信息" className="branch-office-weather__alarm"> + <div className="branch-office-weather-alarm__content"> + <div className="branch-office-weather-alarm__table"> + <div className="branch-office-weather-alarm__row"> + <div>预警信息来源</div> + <div>预警级别</div> + <div>影响区域</div> + </div> + <div className="branch-office-weather-alarm__body"> + <SeamlessScroll list={records} step={0.5}> + {records.map((item, index) => ( + <div className="branch-office-weather-alarm__row" key={index}> + <div>{item.source}</div> + <div>{item.level}</div> + <div>{item.area}</div> + </div> + ))} + </SeamlessScroll> + </div> + </div> + </div> + </Panel> + ); +} + +export default WeatherAlarmPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherAlarmPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherAlarmPanel/index.less new file mode 100644 index 0000000..07f66ad --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherAlarmPanel/index.less @@ -0,0 +1,30 @@ +.branch-office-weather__alarm { + margin-top: 10px; +} + +.branch-office-weather-alarm__content { + padding: 10px 15px; +} + +.branch-office-weather-alarm__table { + margin-top: 5px; +} + +.branch-office-weather-alarm__body { + height: 300px; + overflow-y: hidden; +} + +.branch-office-weather-alarm__row { + display: grid; + grid-template-columns: repeat(3, 1fr); + margin-top: 5px; + background-color: rgba(17, 51, 112, 0.8); +} + +.branch-office-weather-alarm__row div { + padding: 5px; + color: #fff; + font-size: 12px; + text-align: center; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.js new file mode 100644 index 0000000..825b2e2 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.js @@ -0,0 +1,122 @@ +import { Connect } from "@cqsjjb/jjb-dva-runtime"; +import { Spin } from "antd"; +import { useEffect, useState } from "react"; +import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; +import precipitationIcon from "~/assets/images/public/bigScreen/ico21.png"; +import temperatureIcon from "~/assets/images/public/bigScreen/img10.png"; +import windIcon from "~/assets/images/public/bigScreen/img11.png"; +import { NS_BI_STATISTICS } from "~/enumerate/namespace"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import { getAlertColor, getWeatherIcon } from "~/utils/weather"; +import "./index.less"; + +const records = [ + { department: "技术部", requiredDevices: 150, anchoredDevices: 142 }, + { department: "销售部", requiredDevices: 80, anchoredDevices: 76 }, + { department: "市场部", requiredDevices: 60, anchoredDevices: 58 }, + { department: "运营部", requiredDevices: 120, anchoredDevices: 115 }, + { department: "客服部", requiredDevices: 90, anchoredDevices: 88 }, + { department: "财务部", requiredDevices: 30, anchoredDevices: 29 }, + { department: "人事部", requiredDevices: 25, anchoredDevices: 24 }, + { department: "研发部", requiredDevices: 200, anchoredDevices: 195 }, + { department: "质量部", requiredDevices: 45, anchoredDevices: 42 }, + { department: "采购部", requiredDevices: 35, anchoredDevices: 33 }, +]; + +/** 负责查询并展示天气、预警和部门设备锚定情况。 */ +function WeatherPreventionPanel(props) { + const [weather, setWeather] = useState({}); + const [alerts, setAlerts] = useState([]); + + useEffect(() => { + const loadWeather = async () => { + const { result = {} } = await props.getWeather(); + setWeather(result.now || {}); + setAlerts(Array.isArray(result.alerts) ? result.alerts : []); + }; + loadWeather(); + }, []); + + return ( + <Panel title="天气预防情况"> + <Spin spinning={props.biStatistics.getWeatherLoading}> + <div className="branch-office-weather-data__content"> + <div className="branch-office-weather-data__weather"> + <div className="branch-office-weather-data__condition"> + <img src={getWeatherIcon(weather.text)} alt="" /> + <div>{weather.text || "暂无"}</div> + </div> + <div className="branch-office-weather-data__metrics"> + <WeatherMetric + icon={temperatureIcon} + label="温度:" + value={weather.temp ? `${weather.temp}℃` : "--"} + /> + <WeatherMetric + icon={windIcon} + label="风速:" + value={weather.wind_class || "--"} + /> + <WeatherMetric + icon={precipitationIcon} + label="降水量:" + value={weather.prec_1h ? `${weather.prec_1h}毫米` : "--"} + /> + </div> + </div> + <div className="branch-office-weather-data__alerts"> + <SeamlessScroll + list={alerts} + step={0.5} + limitScrollNum={2} + singleHeight={22} + > + {alerts.map((item, index) => ( + <div + className="branch-office-weather-data__alert" + key={index} + style={{ color: getAlertColor(item.level) }} + > + {item.title} + </div> + ))} + </SeamlessScroll> + </div> + <div className="branch-office-weather-data__table"> + <div className="branch-office-weather-data__row"> + <div>部门名称</div> + <div>需锚定设备数</div> + <div>已锚定设备数</div> + </div> + <div className="branch-office-weather-data__table-body"> + <SeamlessScroll list={records} step={0.5}> + {records.map((item, index) => ( + <div className="branch-office-weather-data__row" key={index}> + <div>{item.department}</div> + <div>{item.requiredDevices}</div> + <div>{item.anchoredDevices}</div> + </div> + ))} + </SeamlessScroll> + </div> + </div> + </div> + </Spin> + </Panel> + ); +} + +/** 天气区块中的单项实时气象指标。 */ +function WeatherMetric({ icon, label, value }) { + return ( + <div className="branch-office-weather-data__metric"> + <img src={icon} alt="" /> + <div> + <div className="branch-office-weather-data__metric-label">{label}</div> + <div className="branch-office-weather-data__metric-value">{value}</div> + </div> + </div> + ); +} + +export default Connect([NS_BI_STATISTICS], true)(WeatherPreventionPanel); diff --git a/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.less new file mode 100644 index 0000000..cecb48e --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.less @@ -0,0 +1,99 @@ +.branch-office-weather-data__content { + padding: 10px 15px; +} + +.branch-office-weather-data__weather { + display: flex; + gap: 40px; +} + +.branch-office-weather-data__condition { + display: flex; + gap: 5px; + padding: 10px; + color: #fff; +} + +.branch-office-weather-data__condition img { + width: 40px; + height: 40px; +} + +.branch-office-weather-data__condition div { + letter-spacing: 5px; + writing-mode: vertical-lr; +} + +.branch-office-weather-data__metrics { + display: flex; + flex: 1; + flex-wrap: wrap; + gap: 20px 10px; + align-items: center; + justify-content: space-between; +} + +.branch-office-weather-data__metric { + display: flex; + width: calc((100% / 2) - 10px); + gap: 10px; + align-items: center; +} + +.branch-office-weather-data__metric img { + width: 42px; + height: 42px; +} + +.branch-office-weather-data__metric-label { + color: #fff; + font-size: 12px; +} + +.branch-office-weather-data__metric-value { + color: #00e7ff; + font-size: 18px; + font-weight: bold; +} + +.branch-office-weather-data__alerts { + height: 20px; + margin-top: 10px; + overflow-y: hidden; +} + +.branch-office-weather-data__alert { + font-size: 14px; + font-weight: bold; +} + +.branch-office-weather-data__table { + margin-top: 5px; +} + +.branch-office-weather-data__table-body { + height: 200px; + overflow-y: hidden; +} + +.branch-office-weather-data__row { + display: grid; + grid-template-columns: repeat(3, 1fr); + margin-top: 5px; + background-image: linear-gradient(to bottom, + rgba(0, 42, 85, 0.38), + rgba(1, 37, 74, 0.47), + rgba(4, 38, 87, 0)); + border: 1px solid; + border-bottom: none; + border-image: linear-gradient(to top, + rgba(8, 41, 87, 0.5), + rgba(64, 152, 255, 0.5)) 1; +} + +.branch-office-weather-data__row div { + padding: 5px; + color: #fff; + font-size: 12px; + text-align: center; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/QiXiang/index.js b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/index.js index c7bd917..0a0b584 100644 --- a/src/pages/Container/Map/components/Content/branchOffice/QiXiang/index.js +++ b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/index.js @@ -1,152 +1,14 @@ -import { Connect } from "@cqsjjb/jjb-dva-runtime"; -import { useEffect, useState } from "react"; -import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import icon3 from "~/assets/images/public/bigScreen/ico21.png"; -import icon1 from "~/assets/images/public/bigScreen/img10.png"; -import icon2 from "~/assets/images/public/bigScreen/img11.png"; -import { NS_BI_STATISTICS } from "~/enumerate/namespace"; -import Title from "~/pages/Container/Map/components/Content/branchOffice/Title"; -import { getAlertColor, getWeatherIcon } from "~/utils/weather"; -import "./index.less"; - -function QiXiang(props) { - const [weatherData, setWeatherData] = useState({}); - const [alert, setAlert] = useState([]); - const [block1List, setBlock1List] = useState([ - { department: "技术部", requiredDevices: 150, anchoredDevices: 142 }, - { department: "销售部", requiredDevices: 80, anchoredDevices: 76 }, - { department: "市场部", requiredDevices: 60, anchoredDevices: 58 }, - { department: "运营部", requiredDevices: 120, anchoredDevices: 115 }, - { department: "客服部", requiredDevices: 90, anchoredDevices: 88 }, - { department: "财务部", requiredDevices: 30, anchoredDevices: 29 }, - { department: "人事部", requiredDevices: 25, anchoredDevices: 24 }, - { department: "研发部", requiredDevices: 200, anchoredDevices: 195 }, - { department: "质量部", requiredDevices: 45, anchoredDevices: 42 }, - { department: "采购部", requiredDevices: 35, anchoredDevices: 33 }, - ]); - const [block2List, setBlock2List] = useState([ - { source: "国家气象局", level: "蓝色", area: "北京市朝阳区" }, - { source: "地方气象台", level: "黄色", area: "上海市浦东新区" }, - { source: "中央气象台", level: "橙色", area: "广州市天河区" }, - { source: "区域气象中心", level: "红色", area: "深圳市南山区" }, - { source: "国家气象局", level: "蓝色", area: "杭州市西湖区" }, - { source: "地方气象台", level: "黄色", area: "成都市锦江区" }, - { source: "中央气象台", level: "橙色", area: "武汉市江汉区" }, - { source: "区域气象中心", level: "红色", area: "西安市雁塔区" }, - { source: "国家气象局", level: "蓝色", area: "南京市鼓楼区" }, - { source: "地方气象台", level: "黄色", area: "重庆市渝中区" }, - ]); - - const getWeatherData = async () => { - const { result = {} } = await props.getWeather(); - setWeatherData(result.now); - setAlert(Array.isArray(result.alerts) ? result.alerts : []); - }; - - useEffect(() => { - getWeatherData(); - }, []); +import WeatherAlarmPanel from "./WeatherAlarmPanel"; +import WeatherPreventionPanel from "./WeatherPreventionPanel"; +/** 分公司气象模块仅负责按展示顺序组合业务区块。 */ +function QiXiang() { return ( - <div className="branch_office_qixiang"> - <div className="block1"> - <Title title="天气预防情况" /> - <div className="options"> - <div className="weather"> - <div className="icon"> - <div className="img"> - <img src={getWeatherIcon(weatherData.text)} alt="" /> - </div> - <div className="text">{weatherData.text || "暂无"}</div> - </div> - <div className="items"> - <div className="item"> - <div className="img"> - <img src={icon1} alt="" /> - </div> - <div className="info"> - <div className="label">温度:</div> - <div className="value">{weatherData.temp ? `${weatherData.temp}℃` : "--"}</div> - </div> - </div> - <div className="item"> - <div className="img"> - <img src={icon2} alt="" /> - </div> - <div className="info"> - <div className="label">风速:</div> - <div className="value">{weatherData.wind_class || "--"}</div> - </div> - </div> - <div className="item"> - <div className="img"> - <img src={icon3} alt="" /> - </div> - <div className="info"> - <div className="label">降水量:</div> - <div className="value">{weatherData.prec_1h ? `${weatherData.prec_1h}毫米` : "--"}</div> - </div> - </div> - </div> - </div> - <div className="alert"> - <div className="scroll"> - <SeamlessScroll list={alert} step={0.5} limitScrollNum={2} singleHeight={22}> - { - alert.map((item, index) => ( - <div className="item" key={index}> - <div className="title" style={{ color: getAlertColor(item.level) }}>{item.title}</div> - </div> - )) - } - </SeamlessScroll> - </div> - </div> - <div className="table"> - <div className="tr"> - <div className="td">部门名称</div> - <div className="td">需锚定设备数</div> - <div className="td">已锚定设备数</div> - </div> - <div className="scroll"> - <SeamlessScroll list={block1List} step={0.5}> - {block1List.map((item, index) => ( - <div key={index} className="tr"> - <div className="td">{item.department}</div> - <div className="td">{item.requiredDevices}</div> - <div className="td">{item.anchoredDevices}</div> - </div> - ))} - </SeamlessScroll> - </div> - </div> - </div> - </div> - <div className="block2"> - <Title title="气象告警信息" /> - <div className="options"> - <div className="table"> - <div className="tr"> - <div className="td">预警信息来源</div> - <div className="td">预警级别</div> - <div className="td">影响区域</div> - </div> - <div className="scroll"> - <SeamlessScroll list={block2List} step={0.5}> - {block2List.map((item, index) => ( - <div key={index} className="tr"> - <div className="td">{item.source}</div> - <div className="td">{item.level}</div> - <div className="td">{item.area}</div> - </div> - ))} - </SeamlessScroll> - </div> - </div> - </div> - </div> + <div className="branch-office-weather"> + <WeatherPreventionPanel /> + <WeatherAlarmPanel /> </div> ); } -export default Connect([NS_BI_STATISTICS], true)(QiXiang); +export default QiXiang; diff --git a/src/pages/Container/Map/components/Content/branchOffice/QiXiang/index.less b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/index.less deleted file mode 100644 index a7c0545..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/QiXiang/index.less +++ /dev/null @@ -1,153 +0,0 @@ -.branch_office_qixiang{ - .block1 { - background-color: rgba(12, 28, 88, 0.4); - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .weather { - display: flex; - gap: 40px; - - .icon { - padding: 10px; - display: flex; - gap: 5px; - - .img { - img { - width: 40px; - height: 40px; - } - } - - .text { - color: #fff; - writing-mode: vertical-lr; - letter-spacing: 5px; - } - } - - .items { - flex: 1; - display: flex; - align-items: center; - justify-content: space-between; - flex-wrap: wrap; - gap: 20px 10px; - - .item { - width: calc((100% / 2) - 10px); - display: flex; - align-items: center; - gap: 10px; - - .img { - img { - width: 42px; - height: 42px; - } - } - - .info { - .label { - font-size: 12px; - color: #fff; - } - - .value { - font-size: 18px; - color: #00e7ff; - font-weight: bold; - } - } - } - } - } - - .alert { - margin-top: 10px; - - .scroll { - height: 20px; - overflow-y: hidden; - - .item { - .title { - font-weight: bold; - font-size: 14px; - } - } - } - } - - .table { - margin-top: 5px; - - .scroll { - height: 200px; - overflow-y: hidden; - } - - .tr { - margin-top: 5px; - display: grid; - grid-template-columns: repeat(3, 1fr); - background-image: linear-gradient(to bottom, rgba(0, 42, 85, 0.38), rgba(1, 37, 74, 0.47), rgba(4, 38, 87, 0)); - border: 1px solid; - border-image: linear-gradient(to top, rgba(8, 41, 87, 0.5), rgba(64, 152, 255, 0.5)) 1; - border-bottom: none; - - .td { - text-align: center; - font-size: 12px; - color: #fff; - padding: 5px; - } - } - } - } - } - - .block2 { - background-color: rgba(12, 28, 88, 0.4); - margin-top: 10px; - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .table { - margin-top: 5px; - - .scroll { - height: 300px; - overflow-y: hidden; - } - - .tr { - margin-top: 5px; - display: grid; - grid-template-columns: repeat(3, 1fr); - background-color: rgba(17, 51, 112, 0.8); - - .td { - text-align: center; - font-size: 12px; - color: #fff; - padding: 5px; - } - } - } - } - } -} diff --git a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmHandlingPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmHandlingPanel/index.js new file mode 100644 index 0000000..d28044d --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmHandlingPanel/index.js @@ -0,0 +1,52 @@ +import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const records = [ + { alarmContent: "SOS紧急告警", personnelInvolved: "张三", dispositionStatus: 1 }, + { alarmContent: "静止/滞留告警", personnelInvolved: "李四", dispositionStatus: 2 }, + { alarmContent: "电子围栏越界", personnelInvolved: "王五", dispositionStatus: 1 }, + { alarmContent: "非授权区域停留", personnelInvolved: "赵六", dispositionStatus: 3 }, + { alarmContent: "设备及信号类告警", personnelInvolved: "钱七", dispositionStatus: 1 }, + { alarmContent: "聚集告警", personnelInvolved: "孙八", dispositionStatus: 2 }, + { alarmContent: "单独作业告警", personnelInvolved: "周九", dispositionStatus: 1 }, + { alarmContent: "超员/缺员告警", personnelInvolved: "吴十", dispositionStatus: 3 }, + { alarmContent: "SOS紧急告警", personnelInvolved: "郑一", dispositionStatus: 1 }, + { alarmContent: "静止/滞留告警", personnelInvolved: "王二", dispositionStatus: 2 }, +]; + +const dispositionText = { 1: "已处置", 2: "未处置", 3: "处置中" }; + +/** 负责展示人员告警处置状态列表。 */ +function AlarmHandlingPanel() { + return ( + <Panel title="告警处置情况" className="branch-office-personnel__alarm-handling"> + <div className="branch-office-alarm-handling__content"> + <div className="branch-office-alarm-handling__table"> + <div className="branch-office-alarm-handling__row"> + <div>报警内容</div> + <div>涉及人员</div> + <div>处置情况</div> + </div> + <div className="branch-office-alarm-handling__body"> + <SeamlessScroll list={records} step={0.5}> + {records.map((item, index) => ( + <div className="branch-office-alarm-handling__row" key={index}> + <div>{item.alarmContent}</div> + <div>{item.personnelInvolved}</div> + <div + className={`branch-office-alarm-handling__status branch-office-alarm-handling__status--${item.dispositionStatus}`} + > + {dispositionText[item.dispositionStatus]} + </div> + </div> + ))} + </SeamlessScroll> + </div> + </div> + </div> + </Panel> + ); +} + +export default AlarmHandlingPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmHandlingPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmHandlingPanel/index.less new file mode 100644 index 0000000..82832b8 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmHandlingPanel/index.less @@ -0,0 +1,42 @@ +.branch-office-personnel__alarm-handling { + margin-top: 10px; +} + +.branch-office-alarm-handling__content { + padding: 10px 15px; +} + +.branch-office-alarm-handling__table { + margin-top: 5px; +} + +.branch-office-alarm-handling__body { + height: 100px; + overflow-y: hidden; +} + +.branch-office-alarm-handling__row { + display: grid; + grid-template-columns: repeat(3, 1fr); + margin-top: 5px; + background-color: rgba(17, 51, 112, 0.8); +} + +.branch-office-alarm-handling__row div { + padding: 5px; + color: #fff; + font-size: 12px; + text-align: center; +} + +.branch-office-alarm-handling__row .branch-office-alarm-handling__status--1 { + color: #1db924; +} + +.branch-office-alarm-handling__row .branch-office-alarm-handling__status--2 { + color: #ff611c; +} + +.branch-office-alarm-handling__row .branch-office-alarm-handling__status--3 { + color: #258ff1; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmTypeChartPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmTypeChartPanel/index.js new file mode 100644 index 0000000..6f59420 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmTypeChartPanel/index.js @@ -0,0 +1,75 @@ +import { useMount } from "ahooks"; +import * as echarts from "echarts"; +import { useRef } from "react"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const records = [ + { name: "SOS紧急告警", value: "120" }, + { name: "静止/滞留告警", value: "10" }, + { name: "电子围栏越界", value: "10" }, + { name: "非授权区域停留", value: "20" }, + { name: "设备及信号类告警", value: "30" }, + { name: "聚集告警", value: "40" }, + { name: "单独作业告警", value: "50" }, + { name: "超员/缺员告警", value: "60" }, + { name: "已处置报警数/报警数", value: "10" }, +]; + +/** 负责展示报警类型占比图,并管理图表实例生命周期。 */ +function AlarmTypeChartPanel() { + const chartInstance = useRef(null); + const chartRef = useRef(null); + + const initEcharts = (mainRef, chartInstance, data) => { + chartInstance.current = echarts.init(mainRef.current); + + const option = { + tooltip: { + trigger: "item", + }, + legend: { + left: "center", + top: "0", + textStyle: { + color: "#fff", + }, + }, + series: [{ + color: ["#4198AF", "#285DF0", "#EA439D", "#F59FBC", "#FCA158", "#03D4B9", "#9FD224", "#13C871", "#FF611C"], + type: "pie", + radius: [0, "50%"], + center: ["50%", "60%"], + label: { + textStyle: { + color: "#fff", + }, + }, + data, + }], + }; + + chartInstance.current.setOption(option); + }; + + useMount(() => { + initEcharts(chartRef, chartInstance, records); + + return () => { + if (chartInstance.current) { + chartInstance.current.dispose(); + chartInstance.current = null; + } + }; + }); + + return ( + <Panel title="报警类型"> + <div className="branch-office-alarm-type-chart__content"> + <div ref={chartRef} className="branch-office-alarm-type-chart__chart" /> + </div> + </Panel> + ); +} + +export default AlarmTypeChartPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmTypeChartPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmTypeChartPanel/index.less new file mode 100644 index 0000000..b7f9b07 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/AlarmTypeChartPanel/index.less @@ -0,0 +1,8 @@ +.branch-office-alarm-type-chart__content { + padding: 10px 15px; +} + +.branch-office-alarm-type-chart__chart { + width: 100%; + height: 250px; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/ClosedAreaPersonnelPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/ClosedAreaPersonnelPanel/index.js new file mode 100644 index 0000000..553b297 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/ClosedAreaPersonnelPanel/index.js @@ -0,0 +1,187 @@ +import { useMount } from "ahooks"; +import * as echarts from "echarts"; +import { useRef } from "react"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const records = [ + { name: "A区", unitPersonnelCount: 120, relatedPartyPersonnelCount: 85 }, + { name: "B区", unitPersonnelCount: 95, relatedPartyPersonnelCount: 70 }, + { name: "C区", unitPersonnelCount: 80, relatedPartyPersonnelCount: 60 }, + { name: "D区", unitPersonnelCount: 150, relatedPartyPersonnelCount: 110 }, + { name: "E区", unitPersonnelCount: 75, relatedPartyPersonnelCount: 55 }, + { name: "F区", unitPersonnelCount: 110, relatedPartyPersonnelCount: 80 }, + { name: "G区", unitPersonnelCount: 65, relatedPartyPersonnelCount: 45 }, + { name: "H区", unitPersonnelCount: 140, relatedPartyPersonnelCount: 100 }, + { name: "I区", unitPersonnelCount: 90, relatedPartyPersonnelCount: 65 }, + { name: "J区", unitPersonnelCount: 125, relatedPartyPersonnelCount: 90 }, +]; + +/** 负责展示封闭区域人员统计图,并管理图表实例生命周期。 */ +function ClosedAreaPersonnelPanel() { + const chartInstance = useRef(null); + const chartRef = useRef(null); + + const initEcharts = (mainRef, chartInstance, data) => { + chartInstance.current = echarts.init(mainRef.current); + + const unitPersonnelData = data.map(item => Number.parseInt(item.unitPersonnelCount)); + const relatedPartyPersonnelData = data.map(item => Number.parseInt(item.relatedPartyPersonnelCount)); + const names = data.map(item => item.name); + + const option = { + tooltip: { + trigger: "axis", + axisPointer: { + type: "none", + }, + }, + legend: { + data: ["单位人员数", "相关方人员数"], + top: "0%", + right: "0%", + textStyle: { + color: "#fff", + fontSize: 14, + }, + itemWidth: 12, + itemHeight: 10, + }, + dataZoom: [ + { + type: "inside", + xAxisIndex: 0, + start: 0, + end: 50, + zoomOnMouseWheel: "shift", + moveOnMouseWheel: true, + moveOnMouseMove: true, + }, + { + type: "slider", + height: 6, + bottom: "7%", + show: true, + start: 0, + end: 50, + handleSize: 5, + handleStyle: { + color: "#DCE2E8", + }, + xAxisIndex: [0], + filterMode: "filter", + showDetail: false, + }, + ], + grid: { + left: "10%", + right: "5%", + top: "15%", + bottom: "25%", + }, + xAxis: [ + { + type: "category", + data: names, + axisTick: { + alignWithLabel: true, + }, + axisLabel: { + color: "#fff", + }, + }, + ], + yAxis: { + axisLine: { + show: false, + }, + axisTick: { + show: false, + }, + axisLabel: { + color: "#fff", + }, + splitLine: { + show: true, + lineStyle: { + type: "dashed", + }, + }, + }, + series: [ + { + name: "单位人员数", + type: "bar", + backgroundStyle: { + color: "rgba(216, 229, 247, 0.55)", + borderRadius: [8, 8, 0, 0], + }, + itemStyle: { + borderRadius: [12, 12, 0, 0], + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 1, + color: "rgba(84,173,147,0.2)", + }, + { + offset: 0, + color: "#54AD93", + }, + ]), + }, + barWidth: "10", + data: unitPersonnelData, + }, + { + name: "相关方人员数", + type: "bar", + backgroundStyle: { + color: "rgba(216, 229, 247, 0.55)", + borderRadius: [8, 8, 0, 0], + }, + itemStyle: { + borderRadius: [12, 12, 0, 0], + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 1, + color: "rgba(72,117,231,0.2)", + }, + { + offset: 0, + color: "#4875E7", + }, + ]), + }, + barWidth: "10", + data: relatedPartyPersonnelData, + }, + ], + }; + + chartInstance.current.setOption(option); + }; + + useMount(() => { + initEcharts(chartRef, chartInstance, records); + + return () => { + if (chartInstance.current) { + chartInstance.current.dispose(); + chartInstance.current = null; + } + }; + }); + + return ( + <Panel + title="封闭区域人员统计情况" + className="branch-office-personnel__area-statistics" + > + <div className="branch-office-area-personnel__content"> + <div ref={chartRef} className="branch-office-area-personnel__chart" /> + </div> + </Panel> + ); +} + +export default ClosedAreaPersonnelPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/ClosedAreaPersonnelPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/ClosedAreaPersonnelPanel/index.less new file mode 100644 index 0000000..d3109c2 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/ClosedAreaPersonnelPanel/index.less @@ -0,0 +1,12 @@ +.branch-office-personnel__area-statistics { + margin-top: 10px; +} + +.branch-office-area-personnel__content { + padding: 10px 15px; +} + +.branch-office-area-personnel__chart { + width: 100%; + height: 200px; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/PersonnelLocationPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/PersonnelLocationPanel/index.js new file mode 100644 index 0000000..8cbacc5 --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/PersonnelLocationPanel/index.js @@ -0,0 +1,50 @@ +import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; +import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; +import "./index.less"; + +const records = [ + { name: "张三", area: "A区", onlineStatus: 1 }, + { name: "李四", area: "B区", onlineStatus: 1 }, + { name: "王五", area: "C区", onlineStatus: 2 }, + { name: "赵六", area: "D区", onlineStatus: 1 }, + { name: "钱七", area: "E区", onlineStatus: 1 }, + { name: "孙八", area: "F区", onlineStatus: 2 }, + { name: "周九", area: "G区", onlineStatus: 1 }, + { name: "吴十", area: "H区", onlineStatus: 1 }, + { name: "郑一", area: "I区", onlineStatus: 2 }, + { name: "王二", area: "J区", onlineStatus: 1 }, +]; + +/** 负责展示人员定位与在线状态列表。 */ +function PersonnelLocationPanel() { + return ( + <Panel title="人员定位列表" className="branch-office-personnel__location"> + <div className="branch-office-personnel-location__content"> + <div className="branch-office-personnel-location__table"> + <div className="branch-office-personnel-location__row"> + <div>人员姓名</div> + <div>所属区域</div> + <div>在线状态</div> + </div> + <div className="branch-office-personnel-location__body"> + <SeamlessScroll list={records} step={0.5}> + {records.map((item, index) => ( + <div className="branch-office-personnel-location__row" key={index}> + <div>{item.name}</div> + <div>{item.area}</div> + <div + className={`branch-office-personnel-location__status branch-office-personnel-location__status--${item.onlineStatus}`} + > + {item.onlineStatus === 1 ? "在线" : "离线"} + </div> + </div> + ))} + </SeamlessScroll> + </div> + </div> + </div> + </Panel> + ); +} + +export default PersonnelLocationPanel; diff --git a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/PersonnelLocationPanel/index.less b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/PersonnelLocationPanel/index.less new file mode 100644 index 0000000..36d55ea --- /dev/null +++ b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/PersonnelLocationPanel/index.less @@ -0,0 +1,34 @@ +.branch-office-personnel__location { + margin-top: 10px; +} + +.branch-office-personnel-location__content { + padding: 10px 15px; +} + +.branch-office-personnel-location__table { + margin-top: 5px; +} + +.branch-office-personnel-location__body { + height: 100px; + overflow-y: hidden; +} + +.branch-office-personnel-location__row { + display: grid; + grid-template-columns: repeat(3, 1fr); + margin-top: 5px; + background-color: rgba(17, 51, 112, 0.8); +} + +.branch-office-personnel-location__row div { + padding: 5px; + color: #fff; + font-size: 12px; + text-align: center; +} + +.branch-office-personnel-location__row .branch-office-personnel-location__status--1 { + color: #1db924; +} diff --git a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/echarts.js b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/echarts.js deleted file mode 100644 index 4dd5f05..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/echarts.js +++ /dev/null @@ -1,171 +0,0 @@ -import * as echarts from "echarts"; - -export const initEcharts1 = (mainRef, chartInstance, data) => { - chartInstance.current = echarts.init(mainRef.current); - - const option = { - tooltip: { - trigger: "item", - }, - legend: { - left: "center", - top: "0", - textStyle: { - color: "#fff", - }, - }, - series: [{ - color: ["#4198AF", "#285DF0", "#EA439D", "#F59FBC", "#FCA158", "#03D4B9", "#9FD224", "#13C871", "#FF611C"], - type: "pie", - radius: [0, "50%"], - center: ["50%", "60%"], - label: { - textStyle: { - color: "#fff", - }, - }, - data, - }], - }; - - chartInstance.current.setOption(option); -}; - -export const initEcharts2 = (mainRef, chartInstance, data) => { - chartInstance.current = echarts.init(mainRef.current); - - const unitPersonnelData = data.map(item => Number.parseInt(item.unitPersonnelCount)); - const relatedPartyPersonnelData = data.map(item => Number.parseInt(item.relatedPartyPersonnelCount)); - const names = data.map(item => item.name); - - const option = { - tooltip: { - trigger: "axis", - axisPointer: { - type: "none", - }, - }, - legend: { - data: ["单位人员数", "相关方人员数"], - top: "0%", - right: "0%", - textStyle: { - color: "#fff", - fontSize: 14, - }, - itemWidth: 12, - itemHeight: 10, - }, - dataZoom: [ - { - type: "inside", - xAxisIndex: 0, - start: 0, - end: 50, - zoomOnMouseWheel: "shift", - moveOnMouseWheel: true, - moveOnMouseMove: true, - }, - { - type: "slider", - height: 6, - bottom: "7%", - show: true, - start: 0, - end: 50, - handleSize: 5, - handleStyle: { - color: "#DCE2E8", - }, - xAxisIndex: [0], - filterMode: "filter", - showDetail: false, - }, - ], - grid: { - left: "10%", - right: "5%", - top: "15%", - bottom: "25%", - }, - xAxis: [ - { - type: "category", - data: names, - axisTick: { - alignWithLabel: true, - }, - axisLabel: { - color: "#fff", - }, - }, - ], - yAxis: { - axisLine: { - show: false, - }, - axisTick: { - show: false, - }, - axisLabel: { - color: "#fff", - }, - splitLine: { - show: true, - lineStyle: { - type: "dashed", - }, - }, - }, - series: [ - { - name: "单位人员数", - type: "bar", - backgroundStyle: { - color: "rgba(216, 229, 247, 0.55)", - borderRadius: [8, 8, 0, 0], - }, - itemStyle: { - borderRadius: [12, 12, 0, 0], - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { - offset: 1, - color: "rgba(84,173,147,0.2)", - }, - { - offset: 0, - color: "#54AD93", - }, - ]), - }, - barWidth: "10", - data: unitPersonnelData, - }, - { - name: "相关方人员数", - type: "bar", - backgroundStyle: { - color: "rgba(216, 229, 247, 0.55)", - borderRadius: [8, 8, 0, 0], - }, - itemStyle: { - borderRadius: [12, 12, 0, 0], - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { - offset: 1, - color: "rgba(72,117,231,0.2)", - }, - { - offset: 0, - color: "#4875E7", - }, - ]), - }, - barWidth: "10", - data: relatedPartyPersonnelData, - }, - ], - }; - - chartInstance.current.setOption(option); -}; diff --git a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/index.js b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/index.js index 120ed3f..77ed55f 100644 --- a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/index.js +++ b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/index.js @@ -1,140 +1,16 @@ -import { useMount } from "ahooks"; -import { useRef, useState } from "react"; -import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import Title from "~/pages/Container/Map/components/Content/branchOffice/Title"; -import { initEcharts1, initEcharts2 } from "./echarts"; -import "./index.less"; +import AlarmHandlingPanel from "./AlarmHandlingPanel"; +import AlarmTypeChartPanel from "./AlarmTypeChartPanel"; +import ClosedAreaPersonnelPanel from "./ClosedAreaPersonnelPanel"; +import PersonnelLocationPanel from "./PersonnelLocationPanel"; +/** 分公司人员模块仅负责按展示顺序组合业务区块。 */ function RenYuan() { - const chart1Instance = useRef(null); - const main1Ref = useRef(null); - const chart2Instance = useRef(null); - const main2Ref = useRef(null); - - const [block3List, setBlock3List] = useState([ - { name: "张三", area: "A区", onlineStatus: 1 }, - { name: "李四", area: "B区", onlineStatus: 1 }, - { name: "王五", area: "C区", onlineStatus: 2 }, - { name: "赵六", area: "D区", onlineStatus: 1 }, - { name: "钱七", area: "E区", onlineStatus: 1 }, - { name: "孙八", area: "F区", onlineStatus: 2 }, - { name: "周九", area: "G区", onlineStatus: 1 }, - { name: "吴十", area: "H区", onlineStatus: 1 }, - { name: "郑一", area: "I区", onlineStatus: 2 }, - { name: "王二", area: "J区", onlineStatus: 1 }, - ]); - const [block4List, setBlock4List] = useState([ - { alarmContent: "SOS紧急告警", personnelInvolved: "张三", dispositionStatus: 1 }, - { alarmContent: "静止/滞留告警", personnelInvolved: "李四", dispositionStatus: 2 }, - { alarmContent: "电子围栏越界", personnelInvolved: "王五", dispositionStatus: 1 }, - { alarmContent: "非授权区域停留", personnelInvolved: "赵六", dispositionStatus: 3 }, - { alarmContent: "设备及信号类告警", personnelInvolved: "钱七", dispositionStatus: 1 }, - { alarmContent: "聚集告警", personnelInvolved: "孙八", dispositionStatus: 2 }, - { alarmContent: "单独作业告警", personnelInvolved: "周九", dispositionStatus: 1 }, - { alarmContent: "超员/缺员告警", personnelInvolved: "吴十", dispositionStatus: 3 }, - { alarmContent: "SOS紧急告警", personnelInvolved: "郑一", dispositionStatus: 1 }, - { alarmContent: "静止/滞留告警", personnelInvolved: "王二", dispositionStatus: 2 }, - ]); - - useMount(() => { - initEcharts1(main1Ref, chart1Instance, [ - { name: "SOS紧急告警", value: "120" }, - { name: "静止/滞留告警", value: "10" }, - { name: "电子围栏越界", value: "10" }, - { name: "非授权区域停留", value: "20" }, - { name: "设备及信号类告警", value: "30" }, - { name: "聚集告警", value: "40" }, - { name: "单独作业告警", value: "50" }, - { name: "超员/缺员告警", value: "60" }, - { name: "已处置报警数/报警数", value: "10" }, - ]); - initEcharts2(main2Ref, chart2Instance, [ - { name: "A区", unitPersonnelCount: 120, relatedPartyPersonnelCount: 85 }, - { name: "B区", unitPersonnelCount: 95, relatedPartyPersonnelCount: 70 }, - { name: "C区", unitPersonnelCount: 80, relatedPartyPersonnelCount: 60 }, - { name: "D区", unitPersonnelCount: 150, relatedPartyPersonnelCount: 110 }, - { name: "E区", unitPersonnelCount: 75, relatedPartyPersonnelCount: 55 }, - { name: "F区", unitPersonnelCount: 110, relatedPartyPersonnelCount: 80 }, - { name: "G区", unitPersonnelCount: 65, relatedPartyPersonnelCount: 45 }, - { name: "H区", unitPersonnelCount: 140, relatedPartyPersonnelCount: 100 }, - { name: "I区", unitPersonnelCount: 90, relatedPartyPersonnelCount: 65 }, - { name: "J区", unitPersonnelCount: 125, relatedPartyPersonnelCount: 90 }, - ]); - - return () => { - if (chart1Instance.current) { - chart1Instance.current.dispose(); - chart1Instance.current = null; - } - if (chart2Instance.current) { - chart2Instance.current.dispose(); - chart2Instance.current = null; - } - }; - }); - return ( - <div className="branch_office_renyuan"> - <div className="block1"> - <Title title="报警类型" /> - <div className="options"> - <div ref={main1Ref} style={{ width: "100%", height: "250px" }} /> - </div> - </div> - <div className="block2"> - <Title title="封闭区域人员统计情况" /> - <div className="options"> - <div ref={main2Ref} style={{ width: "100%", height: "200px" }} /> - </div> - </div> - <div className="block3"> - <Title title="人员定位列表" /> - <div className="options"> - <div className="table"> - <div className="tr"> - <div className="td">人员姓名</div> - <div className="td">所属区域</div> - <div className="td">在线状态</div> - </div> - <div className="scroll"> - <SeamlessScroll list={block3List} step={0.5}> - {block3List.map((item, index) => ( - <div key={index} className="tr"> - <div className="td">{item.name}</div> - <div className="td">{item.area}</div> - <div className="td" style={{ color: item.onlineStatus === 1 ? "#1DB924" : "#fff" }}>{item.onlineStatus === 1 ? "在线" : "离线"}</div> - </div> - ))} - </SeamlessScroll> - </div> - </div> - </div> - </div> - <div className="block4"> - <Title title="告警处置情况" /> - <div className="options"> - <div className="table"> - <div className="tr"> - <div className="td">报警内容</div> - <div className="td">涉及人员</div> - <div className="td">处置情况</div> - </div> - <div className="scroll"> - <SeamlessScroll list={block4List} step={0.5}> - {block4List.map((item, index) => ( - <div key={index} className="tr"> - <div className="td">{item.alarmContent}</div> - <div className="td">{item.personnelInvolved}</div> - <div className="td" style={{ color: item.dispositionStatus === 1 ? "#1DB924" : item.dispositionStatus === 2 ? "#FF611C" : "#258FF1" }}> - {item.dispositionStatus === 1 ? "已处置" : item.dispositionStatus === 2 ? "未处置" : "处置中"} - </div> - </div> - ))} - </SeamlessScroll> - </div> - </div> - </div> - </div> + <div className="branch-office-personnel"> + <AlarmTypeChartPanel /> + <ClosedAreaPersonnelPanel /> + <PersonnelLocationPanel /> + <AlarmHandlingPanel /> </div> ); } diff --git a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/index.less b/src/pages/Container/Map/components/Content/branchOffice/RenYuan/index.less deleted file mode 100644 index ce20015..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/RenYuan/index.less +++ /dev/null @@ -1,106 +0,0 @@ -.branch_office_renyuan { - .block1 { - background-color: rgba(12, 28, 88, 0.4); - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - } - } - - .block2 { - background-color: rgba(12, 28, 88, 0.4); - margin-top: 10px; - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - } - } - - .block3 { - background-image: linear-gradient(to bottom, - rgba(0, 0, 0, 0), - rgba(0, 0, 0, 0.8)); - margin-top: 10px; - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .table { - margin-top: 5px; - - .scroll { - height: 100px; - overflow-y: hidden; - } - - .tr { - margin-top: 5px; - display: grid; - grid-template-columns: repeat(3, 1fr); - background-color: rgba(17, 51, 112, 0.8); - - .td { - text-align: center; - font-size: 12px; - color: #fff; - padding: 5px; - } - } - } - } - } - - .block4 { - background-image: linear-gradient(to bottom, - rgba(0, 0, 0, 0), - rgba(0, 0, 0, 0.8)); - margin-top: 10px; - - .options { - padding: 10px 15px; - border: 1px solid; - border-image: linear-gradient(to bottom, - rgba(58, 122, 149, 0), - rgba(58, 122, 149, 1)) 1; - border-top: none; - - .table { - margin-top: 5px; - - .scroll { - height: 100px; - overflow-y: hidden; - } - - .tr { - margin-top: 5px; - display: grid; - grid-template-columns: repeat(3, 1fr); - background-color: rgba(17, 51, 112, 0.8); - - .td { - text-align: center; - font-size: 12px; - color: #fff; - padding: 5px; - } - } - } - } - } -} diff --git a/src/pages/Container/Map/components/Content/branchOffice/Title/index.js b/src/pages/Container/Map/components/Content/branchOffice/Title/index.js deleted file mode 100644 index e1cca96..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/Title/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import titleBgImg from "~/assets/images/map_bi/content/title_bg2.png"; -import "./index.less"; - -function Title(props) { - return ( - <div className="map_content_branch_office_title_container" style={{ backgroundImage: `url(${titleBgImg})` }}> - <div className="basic"> - <div className="label">{props.title}</div> - </div> - <div className="extra">{props.extra}</div> - </div> - ); -} - -export default Title; diff --git a/src/pages/Container/Map/components/Content/branchOffice/Title/index.less b/src/pages/Container/Map/components/Content/branchOffice/Title/index.less deleted file mode 100644 index 5ef6d72..0000000 --- a/src/pages/Container/Map/components/Content/branchOffice/Title/index.less +++ /dev/null @@ -1,25 +0,0 @@ -.map_content_branch_office_title_container { - width: 100%; - height: 41px; - background-size: 100% 100%; - background-repeat: no-repeat; - display: flex; - align-items: center; - justify-content: space-between; - - .basic { - display: flex; - align-items: center; - - .label { - font-size: 14px; - font-weight: bold; - color: #fff; - padding-left: 25px; - } - } - - .extra { - margin-right: 5px; - } -}