From f02bb4f8cf6061851ec7c2c411629a8ae36e06b4 Mon Sep 17 00:00:00 2001
From: vincentlu <t1341870251@gmail.com>
Date: 星期三, 17 十二月 2025 10:57:09 +0800
Subject: [PATCH] #
---
zy-acs-flow/src/map/header/AreaFab.jsx | 22 +++++
zy-acs-flow/src/map/header/RouteFab.jsx | 3
zy-acs-flow/src/map/insight/area/index.jsx | 182 +++++++++++++++++++++++++++++++++++++++++++++
zy-acs-flow/src/i18n/zh.js | 8 +-
zy-acs-flow/src/map/MapPage.jsx | 11 ++
5 files changed, 222 insertions(+), 4 deletions(-)
diff --git a/zy-acs-flow/src/i18n/zh.js b/zy-acs-flow/src/i18n/zh.js
index 28cb7bc..7706c13 100644
--- a/zy-acs-flow/src/i18n/zh.js
+++ b/zy-acs-flow/src/i18n/zh.js
@@ -659,11 +659,11 @@
monitor: '鏃ュ織鐩戞帶',
save: '淇濆瓨鍦板浘',
clear: '娓呯┖鍦板浘',
- adapt: 'ADAPT',
- rotate: 'ROTATE',
+ adapt: '閫傞厤',
+ rotate: '鏃嬭浆',
flip: 'FLIP',
- fake: 'FAKE',
- route: 'ROUTE',
+ fake: '妯℃嫙',
+ route: '璺嚎',
disable: '绂佺敤',
enable: '鍚敤',
reset: '閲嶇疆',
diff --git a/zy-acs-flow/src/map/MapPage.jsx b/zy-acs-flow/src/map/MapPage.jsx
index 9251563..1c4b71c 100644
--- a/zy-acs-flow/src/map/MapPage.jsx
+++ b/zy-acs-flow/src/map/MapPage.jsx
@@ -26,6 +26,7 @@
import PulseSignal from "../page/components/PulseSignal";
import FakeFab from "./header/FakeFab";
import RouteFab from "./header/RouteFab";
+import AreaFab from "./header/AreaFab";
import MoreOperate from "./header/MoreOperate";
let player;
@@ -465,6 +466,16 @@
gap: 2
}}
>
+ {mode !== MAP_MODE.AREA_MODE && (
+ <>
+ <AreaFab
+ curZone={curZone}
+ showRoutes={showRoutes}
+ setShowRoutes={setShowRoutes}
+ notify={notify}
+ />
+ </>
+ )}
{mode !== MAP_MODE.MOVABLE_MODE && (
<>
<RouteFab
diff --git a/zy-acs-flow/src/map/header/AreaFab.jsx b/zy-acs-flow/src/map/header/AreaFab.jsx
new file mode 100644
index 0000000..76d9708
--- /dev/null
+++ b/zy-acs-flow/src/map/header/AreaFab.jsx
@@ -0,0 +1,22 @@
+import React from "react";
+import { useTranslate } from "react-admin";
+import { Fab } from '@mui/material';
+import CropFreeIcon from '@mui/icons-material/CropFree';
+
+const AreaFab = ({ active = false, onToggle }) => {
+ const translate = useTranslate();
+
+ return (
+ <Fab
+ variant="extended"
+ color={active ? 'primary' : 'default'}
+ size="small"
+ onClick={onToggle}
+ >
+ <CropFreeIcon />
+ {translate('page.map.devices.area')}
+ </Fab>
+ );
+};
+
+export default AreaFab;
diff --git a/zy-acs-flow/src/map/header/RouteFab.jsx b/zy-acs-flow/src/map/header/RouteFab.jsx
index e961aaf..c390a07 100644
--- a/zy-acs-flow/src/map/header/RouteFab.jsx
+++ b/zy-acs-flow/src/map/header/RouteFab.jsx
@@ -33,6 +33,9 @@
size="small"
disabled={loading}
onClick={handleClick}
+ sx={{
+ minWidth: 100
+ }}
>
<AltRouteIcon />
{translate('page.map.action.route')}
diff --git a/zy-acs-flow/src/map/insight/area/index.jsx b/zy-acs-flow/src/map/insight/area/index.jsx
new file mode 100644
index 0000000..cc961f5
--- /dev/null
+++ b/zy-acs-flow/src/map/insight/area/index.jsx
@@ -0,0 +1,182 @@
+import React, { useState, useEffect, useMemo } from 'react';
+import { useTranslate } from "react-admin";
+import {
+ Box,
+ Tabs,
+ Tab,
+ Divider,
+ List,
+ ListItemButton,
+ ListItemText,
+ Typography,
+ Stack,
+} from '@mui/material';
+import JsonShow from '../../JsonShow';
+import { fetchAreaList } from '../../http';
+
+const AreaInsight = ({ curZone, setTitle }) => {
+ const translate = useTranslate();
+ const [areas, setAreas] = useState([]);
+ const [selectedId, setSelectedId] = useState(null);
+ const [activeTab, setActiveTab] = useState(0);
+ const formatCoord = (value) => {
+ if (value === null || value === undefined) {
+ return '-';
+ }
+ if (typeof value === 'number') {
+ return value.toFixed(2);
+ }
+ const num = Number(value);
+ if (!Number.isNaN(num)) {
+ return num.toFixed(2);
+ }
+ return String(value);
+ };
+
+ useEffect(() => {
+ let mounted = true;
+ if (!curZone) {
+ setAreas([]);
+ setSelectedId(null);
+ return;
+ }
+ fetchAreaList(curZone).then((list) => {
+ if (!mounted) {
+ return;
+ }
+ const nextList = Array.isArray(list) ? list : [];
+ setAreas(nextList);
+ setSelectedId((prev) => {
+ if (prev) {
+ const exists = nextList.some((item) => item?.id === prev);
+ if (exists) {
+ return prev;
+ }
+ }
+ return nextList[0]?.id ?? null;
+ });
+ });
+ return () => {
+ mounted = false;
+ };
+ }, [curZone]);
+
+ const selectedArea = useMemo(
+ () => areas.find((area) => area?.id === selectedId) || null,
+ [areas, selectedId]
+ );
+
+ useEffect(() => {
+ if (setTitle) {
+ const label = selectedArea
+ ? `${translate('page.map.devices.area')} - ${selectedArea.name || selectedArea.id}`
+ : translate('page.map.devices.area');
+ setTitle(label);
+ }
+ }, [selectedArea, setTitle, translate]);
+
+ useEffect(() => {
+ return () => {
+ if (setTitle) {
+ setTitle(null);
+ }
+ };
+ }, [setTitle]);
+
+ const handleTabChange = (_event, newValue) => {
+ setActiveTab(newValue);
+ };
+
+ const detailPairs = [
+ { label: translate('page.map.area.form.name'), value: selectedArea?.name },
+ { label: translate('page.map.area.form.code'), value: selectedArea?.code },
+ { label: translate('page.map.area.form.maxQty'), value: selectedArea?.maxCount },
+ { label: translate('page.map.area.form.speedLimit'), value: selectedArea?.speedLimit },
+ { label: translate('page.map.area.form.priority'), value: selectedArea?.priority },
+ ];
+
+ return (
+ <Box sx={{ height: '100%', display: 'flex', gap: 2 }}>
+ <Box width={220} sx={{ borderRight: 1, borderColor: 'divider', overflowY: 'auto' }}>
+ <List dense disablePadding>
+ {areas.map((area) => (
+ <ListItemButton
+ key={area.id}
+ selected={area.id === selectedId}
+ onClick={() => setSelectedId(area.id)}
+ >
+ <ListItemText
+ primary={area.name || `#${area.id}`}
+ secondary={area.code}
+ />
+ </ListItemButton>
+ ))}
+ {areas.length === 0 && (
+ <Typography variant="body2" color="text.secondary" p={2}>
+ {translate('page.map.area.form.codesEmpty')}
+ </Typography>
+ )}
+ </List>
+ </Box>
+
+ <Box sx={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
+ <Tabs
+ value={activeTab}
+ onChange={handleTabChange}
+ sx={{ mb: 0 }}
+ >
+ <Tab label={translate('page.map.insight.title')} />
+ <Tab label="JSON" />
+ </Tabs>
+
+ <Divider />
+
+ <Box flex={1} pt={2}>
+ {activeTab === 0 && (
+ <Stack spacing={1}>
+ {detailPairs.map(({ label, value }) => (
+ <Stack key={label} direction="row" spacing={1}>
+ <Typography variant="body2" color="text.secondary" minWidth={120}>
+ {label}:
+ </Typography>
+ <Typography variant="body2">
+ {value != null && value !== '' ? value : '-'}
+ </Typography>
+ </Stack>
+ ))}
+ {selectedArea?.start && selectedArea?.end && (
+ <>
+ <Typography variant="body2" color="text.secondary">
+ {translate('page.map.area.form.startX')}/{translate('page.map.area.form.startY')}:
+ </Typography>
+ <Typography variant="body2">
+ {`${formatCoord(selectedArea.start.x)} , ${formatCoord(selectedArea.start.y)}`}
+ </Typography>
+ <Typography variant="body2" color="text.secondary">
+ {translate('page.map.area.form.endX')}/{translate('page.map.area.form.endY')}:
+ </Typography>
+ <Typography variant="body2">
+ {`${formatCoord(selectedArea.end.x)} , ${formatCoord(selectedArea.end.y)}`}
+ </Typography>
+ </>
+ )}
+ {(selectedArea?.codeList?.length ?? 0) > 0 && (
+ <Typography variant="body2">
+ {translate('page.map.area.form.codes', { count: selectedArea.codeList.length })}
+ </Typography>
+ )}
+ </Stack>
+ )}
+ {activeTab === 1 && (
+ <JsonShow
+ data={selectedArea || {}}
+ height={550}
+ />
+ )}
+ </Box>
+ </Box>
+ </Box>
+ );
+};
+
+export default AreaInsight;
--
Gitblit v1.9.1