map.html 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>天府机坪图</title>
  6. <link rel="stylesheet" href="/static/css/bootstrap.min.css">
  7. <link rel="stylesheet" href="/static/css/moveitem.css">
  8. <style>
  9. .one {
  10. width: 400px;
  11. height: 400px;
  12. border: 1px solid #000;
  13. }
  14. .two {
  15. border: 1px solid #000;
  16. position: absolute;
  17. width: 100%;
  18. height: 100%;
  19. top: 0;
  20. left: 0;
  21. z-index: 1;
  22. }
  23. .map-tooltip {
  24. list-style: none;
  25. padding: 0;
  26. margin: 0;
  27. font-size: 14px;
  28. }
  29. .map-tooltip .title {
  30. display: flex;
  31. align-items: center;
  32. margin-bottom: 8px;
  33. font-weight: bold;
  34. }
  35. .map-tooltip .circle {
  36. width: 8px;
  37. height: 8px;
  38. border-radius: 50%;
  39. background: #ff4444;
  40. margin-right: 8px;
  41. }
  42. .map-tooltip li {
  43. margin-bottom: 6px;
  44. line-height: 1.4;
  45. }
  46. </style>
  47. <script src="/static/js/jquery.min.js"></script>
  48. <script src="/static/js/echarts.min.js"></script>
  49. <script src="/static/js/bootstrap.min.js"></script>
  50. <script src="/static/js/moveitem.js"></script>
  51. </head>
  52. <body>
  53. <div class="two">
  54. <div id="main" style="width: 100vw; height: 100vh;"></div>
  55. </div>
  56. <div id="moveitem" class="myMoveItem" style="top: 50px; left: 10px; width: 384px; height: 612px; z-index: 23; display: none;">
  57. <div class="moveItem_header">
  58. <p id="moveitemtittle" class="moveItem_title"></p>
  59. <div class="moveItem_oper">
  60. <button type="button" class="moveItem_fullScreen"><></button>
  61. <button type="button" class="moveItem_normalScreen">><</button>
  62. <button type="button" class="moveItem_close">X</button>
  63. </div>
  64. </div>
  65. <div id="moveItem_body" class="moveItem_body" style="background-color: #FFF; height: calc(100% - 30px); overflow-y: auto;">
  66. <p>测试1</p>
  67. </div>
  68. <span class="moveItem_resize"></span>
  69. </div>
  70. <div id="moveitemDigital" class="myMoveItem" style="top: 800px; left: 10px; width: 1500px; height: 480px; z-index: 33; display: none;">
  71. <div class="moveItem_header">
  72. <p id="moveitemDigitaltittle" class="moveItem_title"></p>
  73. <div class="moveItem_oper">
  74. <button type="button" class="moveItem_fullScreen"><></button>
  75. <button type="button" class="moveItem_normalScreen">><</button>
  76. <button type="button" class="moveItem_close">X</button>
  77. </div>
  78. </div>
  79. <div id="moveItemDigital_body" class="moveItem_body" style="background-color: #FFF; height: calc(100% - 30px); overflow-y: auto;">
  80. <p>测试1</p>
  81. </div>
  82. <span class="moveItem_resize"></span>
  83. </div>
  84. <script type="text/javascript">
  85. // 全局变量
  86. let myChart = null;
  87. const REFRESH_INTERVAL = 60000;
  88. let refreshTimer = null;
  89. // DOM加载完成初始化
  90. $(document).ready(function() {
  91. initEcharts();
  92. startRequest(); // 传入token启动
  93. initRefreshTimer(); // 传入token初始化刷新
  94. initPopupEvent();
  95. });
  96. // 初始化ECharts
  97. function initEcharts() {
  98. const chartDom = document.getElementById('main');
  99. if (!chartDom) {
  100. console.error('ECharts容器不存在');
  101. return;
  102. }
  103. myChart = echarts.init(chartDom);
  104. window.addEventListener('resize', () => myChart.resize());
  105. }
  106. // 首次加载数据(带token参数)
  107. function startRequest( ) {
  108. const requestHeaders = {
  109. 'Content-Type': 'application/json',
  110. 'Authorization': `{{token}}`
  111. };
  112. // 加载SVG地图
  113. $.get('/static/svg/maptest.svg')
  114. .done(function(svg) {
  115. echarts.registerMap('map-tf', { svg: svg });
  116. // 加载机坪数据(POST请求)
  117. const requestData = {
  118. selectedtime: '{{selectedtime}}'
  119. };
  120. $.ajax({
  121. url: '/static/mapDispaly',
  122. type: 'POST',
  123. headers: requestHeaders,
  124. data: JSON.stringify(requestData),
  125. dataType: 'json',
  126. success: function(data) {
  127. renderEcharts(data);
  128. },
  129. error: function(xhr) {
  130. console.error('机位数据加载失败:', xhr.responseText);
  131. alert(`加载失败(${xhr.status}):${xhr.responseJSON?.error || '未知错误'}`);
  132. }
  133. });
  134. })
  135. .fail(function(err) {
  136. console.error('SVG地图加载失败:', err);
  137. alert('地图资源加载异常');
  138. });
  139. }
  140. // 渲染图表
  141. function renderEcharts(data) {
  142. if (!myChart) return;
  143. const option = {
  144. tooltip: {
  145. backgroundColor: 'white',
  146. padding: 10,
  147. extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); border-radius: 24px; opacity: 0.8; border: 2px solid white; width: 274px; height: 270px;',
  148. textStyle: {
  149. color: 'black',
  150. fontSize: 14
  151. },
  152. formatter: function(params) {
  153. const spotData = data[params.name] || {};
  154. if (spotData.机号 !== "") {
  155. return `
  156. <ul class="map-tooltip">
  157. <li class="title">
  158. <p class="circle"></p>
  159. <p class="province">机位:${params.name || '未知'} ${spotData.航班类型 || ''}</p>
  160. </li>
  161. <li><p class="name">机号: ${spotData.机号 || '无'}</p></li>
  162. <li><p class="name">机型:${spotData.机型 || '无'} ${spotData.发动机 || '无'}</p></li>
  163. <li>
  164. <p class="name">航班号:${spotData.航班号 || ''}<br>
  165. ${spotData.进港机场 || ''}-天府-${spotData.出港机场 || ''}</p>
  166. </li>
  167. <li><p class="name">放行人员: ${spotData.放行 || '无'}</p></li>
  168. <li><p class="name">维修人员: ${spotData.维修人员 || '无'}</p></li>
  169. <li><p class="name">二送人员: ${spotData.二送人员 || '无'}</p></li>
  170. </ul>
  171. `;
  172. }
  173. return '';
  174. }
  175. },
  176. geo: {
  177. map: 'map-tf',
  178. roam: true,
  179. selectedMode: 'multiple',
  180. itemStyle: {
  181. color: 'rgba(0, 0, 0, 0)',
  182. borderColor: 'rgba(0, 0, 0, 0)'
  183. },
  184. emphasis: {
  185. itemStyle: {
  186. color: 'rgba(0, 0, 0, 0)',
  187. borderColor: 'rgba(0, 0, 0, 0)'
  188. },
  189. label: { show: false }
  190. },
  191. select: {
  192. itemStyle: {
  193. color: 'rgba(0, 0, 0, 0)',
  194. borderColor: 'rgba(0, 0, 0, 0)'
  195. },
  196. label: { show: false }
  197. },
  198. regions: makeRegions(data)
  199. },
  200. series: [{
  201. type: 'map',
  202. mapType: 'map-tf',
  203. geoIndex: 0
  204. }]
  205. };
  206. myChart.setOption(option);
  207. bindEchartsEvent();
  208. }
  209. // 生成区域配置
  210. function makeRegions(data) {
  211. const regions = [];
  212. if (typeof data !== 'object' || data === null) return regions;
  213. for (const key in data) {
  214. const spotData = data[key];
  215. const region = {
  216. name: key,
  217. silent: true,
  218. itemStyle: {
  219. color: spotData.color || 'rgba(0, 0, 0, 0)',
  220. borderWidth: 1
  221. },
  222. emphasis: {
  223. itemStyle: {
  224. color: spotData.color || 'rgba(0, 0, 0, 0)',
  225. borderWidth: 1
  226. },
  227. label: { show: false }
  228. },
  229. select: {
  230. itemStyle: {
  231. color: spotData.color || 'rgba(0, 0, 0, 0)',
  232. borderWidth: 1
  233. }
  234. }
  235. };
  236. if (spotData.南航 === '1') {
  237. region.itemStyle.borderColor = 'red';
  238. region.emphasis.itemStyle.borderColor = 'red';
  239. region.select.itemStyle.borderColor = 'red';
  240. } else {
  241. region.itemStyle.borderColor = spotData.color || 'rgba(0, 0, 0, 0)';
  242. region.emphasis.itemStyle.borderColor = spotData.color || 'rgba(0, 0, 0, 0)';
  243. region.select.itemStyle.borderColor = spotData.color || 'rgba(0, 0, 0, 0)';
  244. }
  245. regions.push(region);
  246. }
  247. return regions;
  248. }
  249. // 绑定ECharts事件
  250. function bindEchartsEvent() {
  251. if (!myChart) return;
  252. myChart.on('click', function(params) {
  253. console.log('点击机位:', params.name);
  254. sendSelect(params.name);
  255. });
  256. myChart.on('georoam', function(params) {
  257. if (params.dy || params.dx) return;
  258. const option = myChart.getOption();
  259. const zoom = option.geo[0].zoom || 1;
  260. const fontSize = 15 / (1 + Math.exp(-zoom + 2));
  261. option.geo[0].label.textStyle.fontSize = fontSize;
  262. myChart.setOption(option);
  263. });
  264. myChart.on('geoselectchanged', function(params) {
  265. console.log('选中机位:', params.name);
  266. });
  267. myChart.on('mousemove', function(params) {
  268. console.log('鼠标悬停:', params.name);
  269. });
  270. myChart.on('mouseup', function(params) {
  271. console.log('鼠标抬起:', params.name);
  272. });
  273. }
  274. // 初始化刷新定时器
  275. function initRefreshTimer() {
  276. if (refreshTimer) clearInterval(refreshTimer);
  277. refreshTimer = setInterval(() => RefreshRequest(), REFRESH_INTERVAL);
  278. window.addEventListener('beforeunload', () => {
  279. clearInterval(refreshTimer);
  280. });
  281. }
  282. // 刷新数据请求(带token参数)
  283. function RefreshRequest() {
  284. const requestHeaders = {
  285. 'Content-Type': 'application/json',
  286. 'Authorization': `{{token}}`
  287. };
  288. const requestData = {
  289. selectedtime: '{{selectedtime}}'
  290. };
  291. $.ajax({
  292. url: '/static/mapDispaly',
  293. type: 'POST',
  294. headers: requestHeaders,
  295. data: JSON.stringify(requestData),
  296. dataType: 'json',
  297. success: function(data) {
  298. console.log('数据刷新成功');
  299. const option = {
  300. tooltip: {
  301. formatter: function(params) {
  302. const spotData = data[params.name] || {};
  303. if (spotData.机号 !== "") {
  304. return `
  305. <ul class="map-tooltip">
  306. <li class="title">
  307. <p class="circle"></p>
  308. <p class="province">机位:${params.name || '未知'} ${spotData.航班类型 || ''}</p>
  309. </li>
  310. <li><p class="name">机号: ${spotData.机号 || '无'}</p></li>
  311. <li><p class="name">机型:${spotData.机型 || '无'} ${spotData.发动机 || '无'}</p></li>
  312. <li>
  313. <p class="name">航班号:${spotData.航班号 || ''}<br>
  314. ${spotData.进港机场 || ''}-天府-${spotData.出港机场 || ''}</p>
  315. </li>
  316. <li><p class="name">放行人员: ${spotData.放行 || '无'}</p></li>
  317. <li><p class="name">维修人员: ${spotData.维修人员 || '无'}</p></li>
  318. <li><p class="name">二送人员: ${spotData.二送人员 || '无'}</p></li>
  319. </ul>
  320. `;
  321. }
  322. return '';
  323. }
  324. },
  325. geo: {
  326. regions: makeRegions(data)
  327. }
  328. };
  329. myChart.setOption(option);
  330. },
  331. error: function(xhr) {
  332. console.error('刷新失败:', xhr.responseText);
  333. setTimeout(() => RefreshRequest(), 5000);
  334. }
  335. });
  336. }
  337. // 获取字体大小
  338. function getFontSize() {
  339. try {
  340. const option = myChart.getOption();
  341. const zoom = option.geo[0].zoom || 1;
  342. return 15 / (1 + Math.exp(-zoom + 2));
  343. } catch (err) {
  344. console.error('获取字体大小失败:', err);
  345. return 10;
  346. }
  347. }
  348. // 发送选中机位请求
  349. function sendSelect(bay) {
  350. // 构造请求头,包含token认证信息
  351. const requestHeaders = {
  352. 'Content-Type': 'application/json' ,
  353. 'Authorization': `{{token}}`
  354. };
  355. const requestData = {
  356. selectedtime: '{{selectedtime}}',
  357. bay: bay
  358. };
  359. // 使用$.ajax而非$.post,以便添加headers配置
  360. $.ajax({
  361. url: `/map/getSelectInf`,
  362. type: 'POST',
  363. headers: requestHeaders, // 添加token头信息
  364. data: JSON.stringify(requestData), // 保持原有机位参数
  365. //dataType: 'json',// 预期返回HTML内容
  366. dataType: 'html', // 预期返回HTML内容
  367. success: function(data) {
  368. $("#moveitemtittle").text(bay);
  369. $("#moveItem_body").html(data);
  370. $("#moveitem").show();
  371. },
  372. });
  373. }
  374. // 发送通知
  375. function sendMsg() {
  376. const bay = $("#moveitemtittle").text();
  377. if (!bay) {
  378. alert('未选中任何机位');
  379. return;
  380. }
  381. $.post("/sendMsgBay", { 'bay': bay })
  382. .done(function(data) {
  383. console.log("发送通知成功:", data);
  384. alert('通知发送成功');
  385. })
  386. .fail(function(err) {
  387. console.error('发送通知失败:', err);
  388. alert('通知发送失败,请重试');
  389. });
  390. }
  391. // 获取数字详情
  392. function getDigital() {
  393. $.get('/getDigital')
  394. .done(function(data) {
  395. $("#moveitemDigitaltittle").text('详细数据');
  396. $("#moveItemDigital_body").html(data);
  397. $("#moveitemDigital").show();
  398. })
  399. .fail(function(err) {
  400. console.error('获取数字详情失败:', err);
  401. alert('无法获取数字详情,请重试');
  402. });
  403. }
  404. // 初始化弹窗事件
  405. function initPopupEvent() {
  406. $('.moveItem_close').on('click', function() {
  407. $(this).closest('.myMoveItem').hide();
  408. });
  409. $('.moveItem_fullScreen').on('click', function() {
  410. const popup = $(this).closest('.myMoveItem');
  411. popup.css({
  412. 'top': '0',
  413. 'left': '0',
  414. 'width': '100vw',
  415. 'height': '100vh',
  416. 'z-index': '9999'
  417. });
  418. });
  419. $('.moveItem_normalScreen').on('click', function() {
  420. const popup = $(this).closest('.myMoveItem');
  421. if (popup.attr('id') === 'moveitem') {
  422. popup.css({
  423. 'top': '50px',
  424. 'left': '10px',
  425. 'width': '384px',
  426. 'height': '612px',
  427. 'z-index': '23'
  428. });
  429. } else if (popup.attr('id') === 'moveitemDigital') {
  430. popup.css({
  431. 'top': '800px',
  432. 'left': '10px',
  433. 'width': '1500px',
  434. 'height': '480px',
  435. 'z-index': '33'
  436. });
  437. }
  438. });
  439. }
  440. </script>
  441. </body>
  442. </html>