index.html 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <!-- 引入 ECharts 文件 -->
  6. <script type="text/javascript" src="static/jquery-3.3.1.min.js"></script>
  7. <script src="static/echarts.min.js"></script>
  8. <script src="static/plug-in.min.js"></script>
  9. <script src="static/jquery.liMarquee.js"></script>
  10. <link rel="stylesheet" href="static/index.css" media="all" />
  11. </head>
  12. <body>
  13. <div class="positon">
  14. <!-- <button class="back-btn" onclick="goBack()">&larr; 返回</button> -->
  15. 底图:<select id="mapName">
  16. </select>
  17. </div>
  18. <div class="title-name">设备运行状态一览图</div>
  19. <div class="title-icon">
  20. <label class="legend-item legend-normal"><input type="checkbox" id="filterNormal" checked> 正常设备</label>
  21. <label class="legend-item legend-yellow"><input type="checkbox" id="filterPlanned" checked> 问题设备</label>
  22. <!-- <label class="legend-item legend-red"><input type="checkbox" id="filterUnplanned" checked> 非计划性维修设备</label>-->
  23. </div>
  24. <div style="display:flex;width:1920px;justify-content:space-between;align-items:center;">
  25. <div id="echart" class="echart"></div>
  26. <div class="data">
  27. <div>
  28. <span id="monthNum">暂无</span>
  29. </div>
  30. <div>
  31. <span id="waitNum">暂无</span>
  32. <span id="rate">暂无</span>
  33. </div>
  34. <div style="height: 80%;">
  35. <div id="weekJob" class="item title">
  36. <span>设备编号</span>
  37. <span>保养任务</span>
  38. <span>保养计划时间</span>
  39. </div>
  40. <div class="scrollDiv str_wrap str_vertical"></div>
  41. <!-- <div class="item">
  42. <span>设备名称</span>
  43. <span>正常</span>
  44. <span>2021/03/21</span>
  45. </div>-->
  46. </div>
  47. </div>
  48. </div>
  49. </body>
  50. <style>
  51. /*Plugin CSS*/
  52. .str_wrap {
  53. overflow: hidden;
  54. height: 56%;
  55. width: 100%;
  56. position: relative;
  57. -moz-user-select: none;
  58. -khtml-user-select: none;
  59. user-select: none;
  60. white-space: nowrap;
  61. }
  62. .positon {
  63. position: absolute;
  64. top: 20px;
  65. left: 30%;
  66. z-index: 1000;
  67. }
  68. #mapName {
  69. width: 120px;
  70. }
  71. .str_move {
  72. white-space: nowrap;
  73. position: absolute;
  74. top: 0;
  75. left: 0;
  76. cursor: move;
  77. }
  78. .str_move_clone {
  79. display: inline-block;
  80. vertical-align: top;
  81. position: absolute;
  82. left: 100%;
  83. top: 0;
  84. }
  85. .str_vertical .str_move_clone {
  86. left: 0;
  87. top: 100%;
  88. }
  89. .str_down .str_move_clone {
  90. left: 0;
  91. bottom: 100%;
  92. }
  93. .title-name {
  94. position: absolute;
  95. top: 87px;
  96. left: 26%;
  97. font-size: 34px;
  98. color: black;
  99. z-index: 10000;
  100. }
  101. .title-icon {
  102. position: absolute;
  103. top: 130px;
  104. left: 30%;
  105. z-index: 10000;
  106. }
  107. .title-icon label.legend-item {
  108. display: inline-flex;
  109. align-items: center;
  110. margin-right: 24px;
  111. font-size: 14px;
  112. color: #333;
  113. cursor: pointer;
  114. user-select: none;
  115. }
  116. .title-icon label.legend-item::before {
  117. content: '';
  118. display: inline-block;
  119. width: 12px;
  120. height: 12px;
  121. border-radius: 50%;
  122. margin-right: 8px;
  123. }
  124. .title-icon label.legend-normal::before {
  125. background-color: #174FBF;
  126. box-shadow: 0 0 6px #174FBF;
  127. }
  128. .title-icon label.legend-yellow::before {
  129. background-color: yellow;
  130. box-shadow: 0 0 6px yellow;
  131. }
  132. .title-icon label.legend-red::before {
  133. background-color: red;
  134. box-shadow: 0 0 6px red;
  135. }
  136. .title-icon input[type="checkbox"] {
  137. margin-right: 5px;
  138. cursor: pointer;
  139. transform: scale(1.2);
  140. }
  141. </style>
  142. <script>
  143. function getBase64(image) {
  144. var canvas = document.createElement("canvas");
  145. canvas.width = image.width;
  146. canvas.height = image.height;
  147. var context = canvas.getContext("2d");
  148. context.drawImage(image, 0, 0, image.width, image.height);
  149. // 将canvas的内容转换为base64编码的字符串
  150. var base64 = canvas.toDataURL("image/png");// 可以根据需要更改为其他格式,如'image/jpeg'等
  151. return base64;
  152. }
  153. const ACCESS_TOKEN = 'Access-Token'
  154. const VUE_STORE_BASE = 'pro__'
  155. let mapName, mapList, dataList
  156. var tooltip_message = ''
  157. // 基于准备好的dom,初始化echarts实例
  158. var myChart = echarts.init(document.getElementById('echart'))
  159. var img = new Image()
  160. var canvas = document.createElement('canvas')
  161. var ctx = canvas.getContext('2d')
  162. canvas.width = myChart.getWidth() * window.devicePixelRatio
  163. canvas.height = myChart.getHeight() * window.devicePixelRatio
  164. var fullImage = new Image()
  165. img.onload = function () {
  166. ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
  167. fullImage.src = canvas.toDataURL()
  168. setTimeout(function () {
  169. myChart.resize()
  170. }, 100)
  171. }
  172. img.src = './static/03_04.png'
  173. var option = {
  174. // 背景
  175. backgroundColor: {
  176. repeat: 'no-repeat',
  177. image: fullImage
  178. },
  179. // 地图
  180. geo: {
  181. map: 'world',
  182. silent: true,
  183. top: 0,
  184. left: 0,
  185. right: 0,
  186. bottom: 0,
  187. itemStyle: {
  188. normal: {
  189. opacity: 0.8,
  190. borderWidth: 0,
  191. color: '#a53d13'
  192. }
  193. }
  194. },
  195. title: {
  196. text: '',
  197. },
  198. // 开启工具栏
  199. tooltip: {
  200. "trigger": "item",
  201. "confine": true,
  202. "formatter": (params, ticket, callback) => {
  203. // params.data [20, 204, "L-012"],水平位置,垂直位置,设备编号
  204. let sbNo = params.data[2];
  205. getSbDetail(sbNo);
  206. return tooltip_message;
  207. }
  208. },
  209. series: [{
  210. name: '正常点',
  211. type: 'effectScatter',
  212. coordinateSystem: 'geo',
  213. data: [
  214. ],
  215. showEffectOn: 'render',
  216. rippleEffect: {
  217. brushType: 'stroke'
  218. },
  219. hoverAnimation: true,
  220. label: {
  221. normal: {
  222. formatter: '{@value}',
  223. position: 'left',
  224. show: false
  225. }
  226. },
  227. itemStyle: {
  228. normal: {
  229. color: '#174FBF',
  230. shadowBlur: 200,
  231. shadowColor: '#174FBF'
  232. }
  233. },
  234. zlevel: 1
  235. }, {
  236. name: '正常点编号',
  237. type: 'scatter',
  238. coordinateSystem: 'geo',
  239. symbol: 'pin', // 气泡
  240. symbolSize: 50,
  241. label: {
  242. normal: {
  243. show: true,
  244. formatter: '{@value}',
  245. textStyle: {
  246. color: '#000',
  247. fontSize: 11
  248. }
  249. }
  250. },
  251. itemStyle: {
  252. normal: {
  253. color: '#174FBF'
  254. }
  255. },
  256. zlevel: 6,
  257. data: [
  258. ]
  259. }, {
  260. name: '红色非计划性报修异常点',
  261. type: 'effectScatter',
  262. coordinateSystem: 'geo',
  263. data: [
  264. ],
  265. showEffectOn: 'render',
  266. rippleEffect: {
  267. brushType: 'stroke'
  268. },
  269. hoverAnimation: true,
  270. label: {
  271. normal: {
  272. formatter: '{@value}',
  273. position: 'left',
  274. show: false
  275. }
  276. },
  277. itemStyle: {
  278. normal: {
  279. color: 'red',
  280. shadowBlur: 200,
  281. shadowColor: 'red'
  282. }
  283. },
  284. zlevel: 1
  285. }, {
  286. name: '红色非计划性报修异常点编号',
  287. type: 'scatter',
  288. coordinateSystem: 'geo',
  289. symbol: 'pin', // 气泡
  290. symbolSize: 50,
  291. label: {
  292. normal: {
  293. show: true,
  294. formatter: '{@value}',
  295. textStyle: {
  296. color: '#000',
  297. fontSize: 11
  298. }
  299. }
  300. },
  301. itemStyle: {
  302. normal: {
  303. color: 'red'
  304. }
  305. },
  306. zlevel: 6,
  307. data: [
  308. ]
  309. }, {
  310. name: '黄色计划性报修异常点',
  311. type: 'effectScatter',
  312. coordinateSystem: 'geo',
  313. data: [
  314. ],
  315. showEffectOn: 'render',
  316. rippleEffect: {
  317. brushType: 'stroke'
  318. },
  319. hoverAnimation: true,
  320. label: {
  321. normal: {
  322. formatter: '{@value}',
  323. position: 'left',
  324. show: false
  325. }
  326. },
  327. itemStyle: {
  328. normal: {
  329. color: 'yellow',
  330. shadowBlur: 200,
  331. shadowColor: 'yellow'
  332. }
  333. },
  334. zlevel: 1
  335. }, {
  336. name: '黄色计划性报修异常点编号',
  337. type: 'scatter',
  338. coordinateSystem: 'geo',
  339. symbol: 'pin', // 气泡
  340. symbolSize: 50,
  341. label: {
  342. normal: {
  343. show: true,
  344. formatter: '{@value}',
  345. textStyle: {
  346. color: '#000',
  347. fontSize: 11
  348. }
  349. }
  350. },
  351. itemStyle: {
  352. normal: {
  353. color: 'yellow'
  354. }
  355. },
  356. zlevel: 6,
  357. data: [
  358. ]
  359. }]
  360. }
  361. function getMapList() {
  362. $('#mapName').on('change', function (e) {
  363. mapName = e.delegateTarget.value
  364. img.src = mapList.find(item => item.targetId == mapName).url
  365. init()
  366. });
  367. $.ajax({
  368. type: "GET",
  369. url: "/api/upms/files?type=56",
  370. headers: getHeader(),
  371. success: function (res) {
  372. var data = res.data
  373. mapList = data
  374. for (var i = 0; i < data.length; i++) {
  375. var option = '<option value="' + data[i].targetId + '">' + data[i].targetId + '</option>'
  376. $('#mapName').append(option)
  377. }
  378. mapName = mapList[0].targetId
  379. img.src = mapList.find(item => item.targetId == mapName).url
  380. $('#mapName').val(mapName);
  381. }
  382. })
  383. }
  384. getMapList()
  385. //初始化加载数据,定时器只能在定时到了才发生
  386. getData();
  387. getScreenJobData();
  388. // 使用刚指定的配置项和数据显示图表
  389. myChart.setOption(option);
  390. // 设备详情
  391. function getSbDetail(sbNo) {
  392. $.ajax({
  393. type: "get",
  394. url: "/api/sb/infos/no/" + sbNo,
  395. headers: getHeader(),
  396. success: function (response) {
  397. tooltip_message = '<div>设备编号:' + response.data.no +
  398. '<br/>设备名称:' + response.data.name +
  399. '<br/>设备型号:' + response.data.model +
  400. '<br/>使用位置:' + response.data.cph +
  401. '<br/>设备维修员:' + response.data.repairUserName + '</div>';
  402. },
  403. error: function (xhr, textStatus, errorThrown) {
  404. error(xhr, textStatus, errorThrown);
  405. }
  406. });
  407. }
  408. // 任务数据
  409. function getScreenJobData() {
  410. console.log(21);
  411. $.ajax({
  412. type: "get",
  413. url: "/api/check/jobs/screen",
  414. headers: getHeader(),
  415. success: function (response) {
  416. $('#monthNum').text(response.data.finishNum + "/" + response.data.monthNum);
  417. $('#waitNum').text("待完成:" + response.data.waitNum);
  418. $('#rate').text("完成率:" + response.data.rate + "%");
  419. let weekJob = '';
  420. response.data.weekList.forEach(item => {
  421. weekJob = weekJob + ' <div class=\'item\'>\n' +
  422. ' <span>' + item.sbNo + '</span>\n' +
  423. ' <span>' + (item.requirement || '') + '</span>\n' +
  424. ' <span>' + item.startTime + '</span>\n' +
  425. ' </div>'
  426. })
  427. $('.scrollDiv').empty().append(weekJob);
  428. $('.scrollDiv').liMarquee({
  429. direction: 'up',//身上滚动
  430. runshort: false,//内容不足时不滚动
  431. scrollamount: 20//速度
  432. });
  433. },
  434. error: function (xhr, textStatus, errorThrown) {
  435. error(xhr, textStatus, errorThrown);
  436. }
  437. });
  438. }
  439. // setInterval(mockData(), 10000);
  440. setInterval(function () { getData() }, 60000);
  441. // setInterval(function(){ getScreenJobData() }, 60000);
  442. function mockData() {
  443. var data = [
  444. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '正常'],
  445. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '正常'],
  446. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '正常'],
  447. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '正常'],
  448. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '正常'],
  449. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '正常']
  450. ];
  451. var data2 = [
  452. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '不知道', '不知道'],
  453. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '不知道', '不知道'],
  454. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '不知道', '不知道'],
  455. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '不知道', '不知道'],
  456. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '不知道', '不知道'],
  457. [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), '不知道', '不知道']
  458. ];
  459. option.series[0].data = data;// 正常数据
  460. option.series[1].data = data2;// 异常的红色内容名称
  461. option.series[2].data = data2;// 异常黄点
  462. console.log(data);
  463. console.log(data2);
  464. myChart.setOption(option, true)
  465. }
  466. function getData() {
  467. $.ajax({
  468. type: "get",
  469. url: "/api/sb/infos/screen",
  470. headers: getHeader(),
  471. success: function (response) {
  472. dataList = response.data;
  473. init()
  474. },
  475. error: function (xhr, textStatus, errorThrown) {
  476. error(xhr, textStatus, errorThrown);
  477. }
  478. });
  479. }
  480. function init() {
  481. data = dataList.find(item => item.mapName === mapName)
  482. const normalSbInfoList = [];
  483. const repairSbInfoListRed = [];
  484. const repairSbInfoListYellow = [];
  485. if (data.normalSbInfoList != null) {
  486. for (var i = 0; i < data.normalSbInfoList.length; i++) {
  487. normalSbInfoList.push([data.normalSbInfoList[i].zjm, data.normalSbInfoList[i].jbdh,
  488. data.normalSbInfoList[i].no])
  489. }
  490. }
  491. if (data.repairSbInfoListRed != null) {
  492. for (var i = 0; i < data.repairSbInfoListRed.length; i++) {
  493. repairSbInfoListRed.push([data.repairSbInfoListRed[i].zjm, data.repairSbInfoListRed[i].jbdh,
  494. data.repairSbInfoListRed[i].no])
  495. }
  496. }
  497. if (data.repairSbInfoListYellow != null) {
  498. for (var i = 0; i < data.repairSbInfoListYellow.length; i++) {
  499. repairSbInfoListYellow.push([data.repairSbInfoListYellow[i].zjm, data.repairSbInfoListYellow[i].jbdh,
  500. data.repairSbInfoListYellow[i].no])
  501. }
  502. }
  503. // 保存原始数据副本
  504. option._originalData = {
  505. normal: normalSbInfoList.slice(),
  506. red: repairSbInfoListRed.slice(),
  507. yellow: repairSbInfoListYellow.slice()
  508. };
  509. option.series[0].data = normalSbInfoList;// 正常数据:
  510. option.series[1].data = normalSbInfoList;// 正常数据的编号
  511. option.series[2].data = repairSbInfoListRed;// 非计划性报修 红色编号
  512. option.series[3].data = repairSbInfoListRed;// 非计划性报修 红色
  513. option.series[4].data = repairSbInfoListYellow;// 计划性报修 黃色编号
  514. option.series[5].data = repairSbInfoListYellow;// 计划性报修 黃色
  515. applyFilter();
  516. }
  517. function applyFilter() {
  518. const showNormal = $('#filterNormal').is(':checked');
  519. const showPlanned = $('#filterPlanned').is(':checked');
  520. const showUnplanned = $('#filterUnplanned').is(':checked');
  521. const orig = option._originalData || { normal: [], red: [], yellow: [] };
  522. option.series[0].data = showNormal ? orig.normal : [];
  523. option.series[1].data = showNormal ? orig.normal : [];
  524. option.series[2].data = showUnplanned ? orig.red : [];
  525. option.series[3].data = showUnplanned ? orig.red : [];
  526. option.series[4].data = showPlanned ? orig.yellow : [];
  527. option.series[5].data = showPlanned ? orig.yellow : [];
  528. myChart.setOption(option, true);
  529. }
  530. $('#filterNormal, #filterPlanned, #filterUnplanned').on('change', function () {
  531. applyFilter();
  532. });
  533. function getHeader() {
  534. return {
  535. Authorization: getToken(),
  536. "Cache-Control": "no-cache",
  537. accept: "application/json; charset=utf-8",
  538. 'Content-Type': 'application/json;charset=UTF-8'
  539. // accept: "application/json; charset=utf-8",
  540. // "Access-Control-Allow-Origin": "http://localhost:5000",
  541. // "Access-Control-Allow-Headers":"Origin, X-Requested-With, Content-Type, Accept"
  542. }
  543. }
  544. function getToken() {
  545. const storeTokenJson = JSON.parse(localStorage.getItem(VUE_STORE_BASE + ACCESS_TOKEN))
  546. return 'Bearer ' + storeTokenJson.value
  547. }
  548. function goBack() {
  549. // 优先尝试让父级 Vue 路由跳转到首页
  550. try {
  551. if (window.parent && window.parent !== window) {
  552. // 通知父级 Vue 跳转首页
  553. window.parent.postMessage({ action: 'navigateHome' }, '*')
  554. // 作为兜底:让父级回退
  555. setTimeout(function () {
  556. if (window.parent.history.length > 1) {
  557. window.parent.history.back()
  558. }
  559. }, 100)
  560. } else {
  561. if (window.history.length > 1) {
  562. window.history.back()
  563. }
  564. }
  565. } catch (e) {
  566. if (window.history.length > 1) {
  567. window.history.back()
  568. }
  569. }
  570. }
  571. (function () {
  572. console.log("滚动")
  573. $('.scrollDiv').liMarquee({
  574. direction: 'up',//身上滚动
  575. runshort: false,//内容不足时不滚动
  576. scrollamount: 20//速度
  577. });
  578. });
  579. </script>
  580. </html>