RTMMGis.tsx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. 'use client'
  2. import React, {useEffect, useRef, useState} from 'react';
  3. import deom1 from "../assets/deom1.png";
  4. import deom2 from "../assets/deom2.png";
  5. import Uptow from "../assets/up-two.png";
  6. import markerData from "../assets/markerData.json";
  7. import fameng from "../assets/fameng.png";
  8. import shuifa from "../assets/shuifa.png";
  9. import dataJson from "../assets/index.json";
  10. import "../assets/BaiduMapPage.css";
  11. import {Badge, Card, Col, Modal, Row,} from "antd"
  12. // 添加图标导入
  13. import {CheckCircleOutlined, CloseCircleOutlined, ExclamationCircleOutlined, WifiOutlined} from '@ant-design/icons';
  14. import RTMMPicEcharts from "@/components/realTimeMontorModule/RTMMPicEcharts";
  15. import RTMMBarChart from "@/components/realTimeMontorModule/RTMMBarchart";
  16. import Image from "next/image";
  17. // ... 其余代码保持不变 ...
  18. // 仅在浏览器环境中声明 window 类型
  19. declare global {
  20. interface Window {
  21. BMapGL: any;
  22. initMap: () => void;
  23. }
  24. }
  25. function GisMapBaidu(props: { height: string }) {
  26. const mapRef = useRef<HTMLDivElement>(null);
  27. const [map, setMap] = useState<any>(null);
  28. const [isClient, setIsClient] = useState(false);
  29. // 设备列表状态
  30. const [popupFrameVisible, setPopupFrameVisible] = useState(false);
  31. const [manholeCoverData, setManholeCoverData] = useState<any>(null);
  32. // 标记是否在浏览器环境
  33. useEffect(() => {
  34. setIsClient(true);
  35. }, []);
  36. // 初始化地图
  37. useEffect(() => {
  38. if (!isClient || !mapRef.current) return;
  39. // 检查是否已加载百度地图 GL JS API
  40. if (!window.BMapGL) {
  41. // 动态加载百度地图 GL JS API
  42. const script = document.createElement('script');
  43. script.src = `https://api.map.baidu.com/api?v=2.0&ak=jWDCUpsk33htQsF6IEwk4ctkERTOFFH0&type=webgl`;
  44. script.async = true;
  45. script.onload = () => {
  46. initializeMap();
  47. };
  48. document.head.appendChild(script);
  49. } else {
  50. initializeMap();
  51. }
  52. return () => {
  53. // 清理工作
  54. if (map && map.destroy) {
  55. map.destroy();
  56. }
  57. };
  58. }, [isClient]);
  59. const initializeMap = () => {
  60. if (!window.BMapGL || !mapRef.current) return;
  61. // 创建地图实例
  62. const mapInstance = new window.BMapGL.Map(mapRef.current);
  63. // 设置中心点坐标
  64. const point = new window.BMapGL.Point(110.39573035064166, 28.44628067667752);
  65. mapInstance.centerAndZoom(point, 16);
  66. // 启用滚轮放大缩小
  67. mapInstance.enableScrollWheelZoom(true);
  68. // 设置初始倾斜角度为30度(范围:0-45度)
  69. mapInstance.setTilt(45);
  70. // 添加控件
  71. mapInstance.setMinZoom(16);
  72. mapInstance.setMaxZoom(19);
  73. mapInstance.setMapStyleV2({
  74. styleId: '62ce43ad2a1362c23e612b783d7406e7'
  75. });
  76. // 添加标记点
  77. if (markerData?.list?.dataOne) {
  78. markerData.list.dataOne.forEach((item: any) => {
  79. const icon = new window.BMapGL.Icon(fameng.src, new window.BMapGL.Size(23, 25), {
  80. anchor: new window.BMapGL.Size(10, 25),
  81. });
  82. const markers = new window.BMapGL.Point(item.lng, item.lat);
  83. const marker = new window.BMapGL.Marker(markers, {icon});
  84. mapInstance.addOverlay(marker);
  85. // 创建信息窗口
  86. const opts = {
  87. width: 300,
  88. title: item.name
  89. };
  90. const infoWindow = new window.BMapGL.InfoWindow(
  91. "倾斜角度:188deg<br/>" +
  92. "状态:正常运行<br/>" +
  93. "信噪比:16db<br/>" +
  94. "负责人:张三<br/>" +
  95. "联系方式:121234564", opts
  96. );
  97. marker.addEventListener('click', () => {
  98. mapInstance.openInfoWindow(infoWindow, markers);
  99. console.log('点击了标记点', item);
  100. // 添加显示弹窗的逻辑
  101. setManholeCoverData(item);
  102. setPopupFrameVisible(true);
  103. });
  104. });
  105. }
  106. if (markerData?.list?.dataTwo) {
  107. markerData.list.dataTwo.forEach((item: any) => {
  108. const icon = new window.BMapGL.Icon(shuifa.src, new window.BMapGL.Size(23, 25), {
  109. anchor: new window.BMapGL.Size(20, 0),
  110. });
  111. const markers = new window.BMapGL.Point(item.lng, item.lat);
  112. const marker = new window.BMapGL.Marker(markers, {icon});
  113. mapInstance.addOverlay(marker);
  114. // 创建信息窗口
  115. const opts = {
  116. width: 300,
  117. title: item.name
  118. };
  119. // const infoWindow = new window.BMapGL.InfoWindow(
  120. // "倾斜角度:188deg<br/>" +
  121. // "状态:正常运行<br/>" +
  122. // "信噪比:16db<br/>" +
  123. // "负责人:张三<br/>" +
  124. // "联系方式:121234564", opts
  125. // );
  126. marker.addEventListener('click', () => {
  127. // mapInstance.openInfoWindow(infoWindow, markers);
  128. setManholeCoverData({
  129. ...item,
  130. neme:item.name,
  131. itemCode:'12312442222',
  132. type: '供水井盖',
  133. state:'1',
  134. worker:'东三社区',
  135. miaoshu:'检测到倾斜角度异常',
  136. time:'2023-08-01 10:00:00',
  137. temperature:'25',//温度
  138. humidity:'50',//湿度
  139. flow:'100',//流量
  140. flowSpeed:'2',//流速
  141. waterLevel:'100',//水位
  142. waterQuality:'100',//水质
  143. PHValue:'7.0',//PH值
  144. rongjieyang:'100',//融结氧
  145. ammoniaNitrogen:'100',//氨氮
  146. });
  147. // 添加显示弹窗的逻辑
  148. setPopupFrameVisible(true);
  149. });
  150. });
  151. }
  152. // 添加线路图层
  153. if (window.BMapGL.LineLayer && dataJson) {
  154. const lineLayer = new window.BMapGL.LineLayer({
  155. enablePicked: true,
  156. autoSelect: true,
  157. pickWidth: 30,
  158. pickHeight: 30,
  159. opacity: 1,
  160. selectedColor: 'blue',
  161. style: {
  162. sequence: false,
  163. marginLength: 18,
  164. borderMask: true,
  165. strokeWeight: 10,
  166. strokeLineJoin: 'round',
  167. strokeLineCap: 'round',
  168. strokeTextureUrl: ['match', ['get', 'name'], 'demo1', deom1.src, 'demo2', deom2.src, Uptow.src],
  169. }
  170. });
  171. lineLayer.setData(dataJson);
  172. mapInstance.addNormalLayer(lineLayer);
  173. }
  174. setMap(mapInstance);
  175. // 添加点击地图事件
  176. mapInstance.addEventListener('click', (e: any) => {
  177. console.log('点击位置经纬度:', e.latlng?.lng, e.latlng?.lat);
  178. });
  179. };
  180. if (!isClient) {
  181. return (
  182. <div className="flex items-center justify-center h-full bg-gray-100">
  183. <div className="text-gray-500">地图加载中...</div>
  184. </div>
  185. );
  186. }
  187. // 合并return语句,修复Modal无法显示的问题
  188. return (
  189. <>
  190. <div className="flex flex-col font-sans" style={{ height: props.height + "vh" }}>
  191. <div className="flex flex-1 overflow-hidden statisticsBox">
  192. {/* 添加左侧三个板块区域 */}
  193. <div style={{ width: '380px', overflow: 'auto' }}>
  194. {/* 板块1 */}
  195. <Card title="状态监测统计(15天)" style={{ marginBottom: '10px',
  196. backgroundColor: '#b9d8ff85'}}>
  197. <div style={{ textAlign: 'center',height: '200px' }}>
  198. <RTMMPicEcharts
  199. // title="告警类型统计"
  200. data={[
  201. {value: 335, name: '位移告警'},
  202. {value: 310, name: '倾斜告警'},
  203. {value: 234, name: '水位告警'},
  204. {value: 135, name: '电压告警'},
  205. {value: 1548, name: '温度告警'}
  206. ]}
  207. style={{height: '100%', width: '100%'}}
  208. />
  209. </div>
  210. </Card>
  211. {/* 板块2 */}
  212. <Card title="设备状态" style={{ marginBottom: '16px', backgroundColor: '#b9d8ff85' }}>
  213. <div>
  214. {/* 修改为2行2列的四宫格布局 */}
  215. <Row gutter={[16, 16]}>
  216. {/* 正常运行状态卡片 */}
  217. <Col span={12}>
  218. <div style={{
  219. display: 'flex',
  220. alignItems: 'center',
  221. padding: '12px',
  222. backgroundColor: 'rgba(255, 255, 255, 0.7)',
  223. borderRadius: '8px'
  224. }}>
  225. {/* 左侧图标 */}
  226. <div style={{
  227. width: '40px',
  228. height: '40px',
  229. backgroundColor: '#f0f9eb',
  230. borderRadius: '50%',
  231. display: 'flex',
  232. alignItems: 'center',
  233. justifyContent: 'center',
  234. marginRight: '12px'
  235. }}>
  236. <CheckCircleOutlined style={{ fontSize: '24px', color: '#52c41a' }} />
  237. </div>
  238. {/* 右侧文字和数值 */}
  239. <div>
  240. <div style={{ color: '#333' }}>正常运行</div>
  241. <div style={{ fontSize: '20px', fontWeight: 'bold', color: '#52c41a' }}>12</div>
  242. </div>
  243. </div>
  244. </Col>
  245. {/* 需要注意状态卡片 */}
  246. <Col span={12}>
  247. <div style={{
  248. display: 'flex',
  249. alignItems: 'center',
  250. padding: '12px',
  251. backgroundColor: 'rgba(255, 255, 255, 0.7)',
  252. borderRadius: '8px'
  253. }}>
  254. <div style={{
  255. width: '40px',
  256. height: '40px',
  257. backgroundColor: '#fffbe6',
  258. borderRadius: '50%',
  259. display: 'flex',
  260. alignItems: 'center',
  261. justifyContent: 'center',
  262. marginRight: '12px'
  263. }}>
  264. <ExclamationCircleOutlined style={{ fontSize: '24px', color: '#faad14' }} />
  265. </div>
  266. <div>
  267. <div style={{ color: '#333' }}>需要注意</div>
  268. <div style={{ fontSize: '20px', fontWeight: 'bold', color: '#faad14' }}>3</div>
  269. </div>
  270. </div>
  271. </Col>
  272. {/* 异常状态卡片 */}
  273. <Col span={12}>
  274. <div style={{
  275. display: 'flex',
  276. alignItems: 'center',
  277. padding: '12px',
  278. backgroundColor: 'rgba(255, 255, 255, 0.7)',
  279. borderRadius: '8px'
  280. }}>
  281. <div style={{
  282. width: '40px',
  283. height: '40px',
  284. backgroundColor: '#fff2f0',
  285. borderRadius: '50%',
  286. display: 'flex',
  287. alignItems: 'center',
  288. justifyContent: 'center',
  289. marginRight: '12px'
  290. }}>
  291. <CloseCircleOutlined style={{ fontSize: '24px', color: '#f5222d' }} />
  292. </div>
  293. <div>
  294. <div style={{ color: '#333' }}>异常状态</div>
  295. <div style={{ fontSize: '20px', fontWeight: 'bold', color: '#f5222d' }}>1</div>
  296. </div>
  297. </div>
  298. </Col>
  299. {/* 离线类型卡片 */}
  300. <Col span={12}>
  301. <div style={{
  302. display: 'flex',
  303. alignItems: 'center',
  304. padding: '12px',
  305. backgroundColor: 'rgba(255, 255, 255, 0.7)',
  306. borderRadius: '8px'
  307. }}>
  308. <div style={{
  309. width: '40px',
  310. height: '40px',
  311. backgroundColor: '#f5f5f5',
  312. borderRadius: '50%',
  313. display: 'flex',
  314. alignItems: 'center',
  315. justifyContent: 'center',
  316. marginRight: '12px'
  317. }}>
  318. <WifiOutlined style={{ fontSize: '24px', color: '#999999' }} />
  319. </div>
  320. <div>
  321. <div style={{ color: '#333' }}>离线类型</div>
  322. <div style={{ fontSize: '20px', fontWeight: 'bold', color: '#999999' }}>2</div>
  323. </div>
  324. </div>
  325. </Col>
  326. </Row>
  327. </div>
  328. </Card>
  329. {/* 板块3 */}
  330. <Card title="分类监测态势" style={{ marginBottom: '16px', backgroundColor: '#b9d8ff85' }}>
  331. <div style={{ height:'170px'}}>
  332. <RTMMBarChart
  333. data={[
  334. {value: 120, name: '电用井盖'},
  335. {value: 85, name: '供水井盖'},
  336. {value: 60, name: '通信井盖'},
  337. {value: 45, name: '燃气井盖'},
  338. {value: 30, name: '污水井盖'},
  339. {value: 30, name: '雨水井盖'},
  340. {value: 30, name: '路灯井盖'},
  341. {value: 30, name: '网络井盖'},
  342. {value: 30, name: '电缆井盖'},
  343. {value: 30, name: '园林井盖'},
  344. {value: 30, name: '化粪池井盖'}
  345. ]}
  346. style={{height: '100%', width: '100%'}}
  347. />
  348. </div>
  349. </Card>
  350. </div>
  351. {/* 地图容器区域 */}
  352. <div ref={mapRef} className="flex-1 h-full"></div>
  353. </div>
  354. </div>
  355. <Modal
  356. title="GIS设备详情"
  357. open={popupFrameVisible}
  358. onCancel={() => setPopupFrameVisible(false)}
  359. width={800}
  360. className="custom-modal"
  361. // 添加mask={false}移除遮罩层
  362. mask={false}
  363. // 定位样式保持不变
  364. style={{
  365. position: 'absolute',
  366. right: 0,
  367. top: 150,
  368. textAlign: 'center',
  369. }}
  370. >
  371. {manholeCoverData && (
  372. <div className="space-y-4" style={{ display: 'flex', justifyContent: 'space-between' }}>
  373. <div style={{ width: '25%' }}>
  374. <div className="space-y-4" style={{ textAlign: 'left' }}>
  375. <Row gutter={16}>
  376. <Col span={24}>
  377. <div>
  378. <strong style={{ color: '#B0E0E6' }}>设备名称:</strong> {manholeCoverData.name}
  379. </div>
  380. </Col>
  381. </Row>
  382. <Row gutter={16}>
  383. <Col span={24}>
  384. <div>
  385. <strong style={{ color: '#B0E0E6' }}>设备编号:</strong> {manholeCoverData.itemCode}
  386. </div>
  387. </Col>
  388. </Row>
  389. <Row gutter={16}>
  390. <Col span={24}>
  391. <div>
  392. <strong style={{ color: '#B0E0E6' }}>设备类型:</strong> {manholeCoverData.type}
  393. </div>
  394. </Col>
  395. </Row>
  396. <Row gutter={16}>
  397. <Col span={24}>
  398. <div>
  399. <strong style={{ color: '#B0E0E6' }}>状态:</strong>
  400. <Badge
  401. style={{
  402. marginLeft: '10px'
  403. }}
  404. status={manholeCoverData.state === "1" ? "success" : "error"}
  405. text={
  406. <span style={{
  407. color: manholeCoverData.state === "1" ? "#52c41a" : "#f5222d"
  408. }}>
  409. {manholeCoverData.state === "1" ? "正常" : "异常"}
  410. </span>
  411. }
  412. />
  413. </div>
  414. </Col>
  415. </Row>
  416. <Row gutter={16}>
  417. <Col span={24}>
  418. <div>
  419. <strong style={{ color: '#B0E0E6' }}>权属单位:</strong> {manholeCoverData.worker}
  420. </div>
  421. </Col>
  422. </Row>
  423. <Row gutter={16}>
  424. <Col span={24}>
  425. <div>
  426. <strong style={{ color: '#B0E0E6' }}>监测描述:</strong> {manholeCoverData.miaoshu}
  427. </div>
  428. </Col>
  429. </Row>
  430. <div style={{ color: '#B0E0E6', fontWeight:'bold',fontSize:'20px'}}>窨井下监测信息</div>
  431. <Row gutter={16}>
  432. <Col span={12}>
  433. <div>
  434. <strong style={{ color: '#B0E0E6' }}>温度:</strong> {manholeCoverData.temperature}
  435. </div>
  436. </Col>
  437. <Col span={12}>
  438. <div>
  439. <strong style={{ color: '#B0E0E6' }}>湿度:</strong> {manholeCoverData.humidity}
  440. </div>
  441. </Col>
  442. </Row>
  443. <Row gutter={16}>
  444. <Col span={12}>
  445. <div>
  446. <strong style={{ color: '#B0E0E6' }}>流量:</strong> {manholeCoverData.flow}
  447. </div>
  448. </Col>
  449. <Col span={12}>
  450. <div>
  451. <strong style={{ color: '#B0E0E6' }}>水位:</strong> {manholeCoverData.waterLevel}
  452. </div>
  453. </Col>
  454. </Row>
  455. <Row gutter={16}>
  456. <Col span={12}>
  457. <div>
  458. <strong style={{ color: '#B0E0E6' }}>流速:</strong> {manholeCoverData.flowSpeed}
  459. </div>
  460. </Col>
  461. <Col span={12}>
  462. <div>
  463. <strong style={{ color: '#B0E0E6' }}>水质:</strong> {manholeCoverData.waterQuality}
  464. </div>
  465. </Col>
  466. </Row>
  467. <Row gutter={16}>
  468. <Col span={12}>
  469. <div>
  470. <strong style={{ color: '#B0E0E6' }}>PH值:</strong> {manholeCoverData.PHValue}
  471. </div>
  472. </Col>
  473. <Col span={12}>
  474. <div>
  475. <strong style={{ color: '#B0E0E6' }}>氨氮:</strong> {manholeCoverData.ammoniaNitrogen}
  476. </div>
  477. </Col>
  478. </Row>
  479. </div>
  480. </div>
  481. <div style={{width: '75%'}}>
  482. <Image src={'/test1.png'} alt={'测试图片'} width={800} height={800} />
  483. </div>
  484. </div>
  485. )}
  486. </Modal>
  487. {/* 添加全局样式覆盖表头背景色 */}
  488. <style jsx global>{`
  489. /* 修改Modal背景色和添加渐变边框 */
  490. .custom-modal .ant-modal-content {
  491. background-color: rgba(58, 56, 153, 1) !important; /* 主背景色 */
  492. color: white; /* 文字颜色 */
  493. /* 添加由外向内的径向渐变边框 */
  494. border: 2px solid transparent;
  495. border-radius: 8px;
  496. background-clip: padding-box, border-box;
  497. background-origin: padding-box, border-box;
  498. background-image:
  499. linear-gradient( rgba(58, 56, 153, 1), rgba(58, 56, 153, 1)), /* 背景色 */
  500. radial-gradient(circle at center, #ff6b6b, #4ecdc4); /* 由外向内的径向渐变边框 */
  501. }
  502. /* 可选:修改Modal标题栏样式 */
  503. .custom-modal .ant-modal-header {
  504. background-color: rgba(58, 56, 153, 1) !important;
  505. border-bottom: none; /* 移除底部边框 */
  506. }
  507. .custom-modal .ant-modal-title{
  508. color:white !important;
  509. border-bottom:1px solid white;
  510. }
  511. .statisticsBox .ant-card .ant-card-head{
  512. background-color: rgba(58, 56, 153, 1) !important;
  513. min-height: 40px !important;
  514. color:white;
  515. padding:0px 12px !important;
  516. }
  517. .statisticsBox .ant-card .ant-card-body {
  518. padding:12px !important;
  519. }
  520. `}</style>
  521. </>
  522. );
  523. }
  524. export default GisMapBaidu;