NodeMap.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. <template>
  2. <div class="nodeMap-contenter">
  3. <!-- 地图 -->
  4. <div class="map-cont">
  5. <a-row class="map-title" :getter="24">
  6. <span>智享亿家生活垃圾分类网点地图</span>
  7. <a-icon @click="isFullscreenMap" :style="{fontSize:'30px',padding: '7px',float: 'right', cursor: 'pointer'}" :type="isMapFull ?'fullscreen-exit':'fullscreen'" />
  8. </a-row>
  9. <!-- 地图容器 -->
  10. <div id="container" :style="{height: mapHeight}">
  11. </div>
  12. <div class="map-footer">
  13. <a-row :getter="[24,24]">
  14. <a-col :xs="24" :sm="12" :md="8" :lg="6">
  15. <div class="map-data">
  16. <a-icon type="team" :style="{color:'#83ace1',fontSize:'20px',padding:'5px',backgroundColor:'#FFF',borderRadius:'8px'}" />
  17. <span>&nbsp;&nbsp;用户总数 {{ userTotal }} 个</span>
  18. </div>
  19. </a-col>
  20. <a-col :xs="24" :sm="12" :md="8" :lg="6">
  21. <div class="map-data">
  22. <a-icon type="rest" :style="{color:'#83ace1',fontSize:'20px',padding:'5px',backgroundColor:'#FFF',borderRadius:'8px'}" />
  23. <span>&nbsp;&nbsp;智能收集箱 {{ deviceTotal }} 台</span>
  24. </div>
  25. </a-col>
  26. <a-col :xs="24" :sm="12" :md="8" :lg="6">
  27. <div class="map-data">
  28. <a-icon type="shop" :style="{color:'#83ace1',fontSize:'20px',padding:'5px',backgroundColor:'#FFF',borderRadius:'8px'}" />
  29. <span>&nbsp;&nbsp;智能兑换亭 {{ sellerTotal }} 台</span>
  30. </div>
  31. </a-col>
  32. <a-col :xs="24" :sm="12" :md="8" :lg="6">
  33. <div class="map-data">
  34. <a-icon type="hdd" :style="{color:'#83ace1',fontSize:'20px',padding:'5px',backgroundColor:'#FFF',borderRadius:'8px'}" />
  35. <span>&nbsp;&nbsp;智能兑换机 {{ machineTotal }} 台</span>
  36. </div>
  37. </a-col>
  38. </a-row>
  39. </div>
  40. </div>
  41. <div v-show="!isMapFull" class="data-cont">
  42. <!-- 实时轮播数据 -->
  43. <a-row :gutter="[24,24]">
  44. <a-col
  45. :xs="24"
  46. :sm="24"
  47. :md="24"
  48. :lg="24"
  49. :xl="24"
  50. :xxl="16">
  51. <div class="switper-cont">
  52. <div class="data-title">实时投递数据</div>
  53. <div class="chart-box data-form">
  54. <div class="swipter-title">
  55. <a-row :gutter="0">
  56. <a-col style="padding: 0px 2px;text-align: center;" :span="6">投递时间</a-col>
  57. <a-col style="padding: 0px 2px;text-align: center;" :span="4">投递用户</a-col>
  58. <a-col style="padding: 0px 2px;text-align: center;" :span="6">投递网点</a-col>
  59. <a-col style="padding: 0px 2px;text-align: center;" :span="5">投递分类</a-col>
  60. <a-col style="padding: 0px 2px;text-align: center;" :span="3">投递重量(g)</a-col>
  61. </a-row>
  62. </div>
  63. <vue-seamless-scroll :data="deliveryLogData" :class-option="classOption" class="seamless-warp">
  64. <a-spin class="loading" :spinning="deliverySpinning" tip="Loading..." />
  65. <div class="swipter-data" v-for="item in deliveryLogData" :key="item.id">
  66. <a-row :gutter="0">
  67. <a-col style="padding: 0px 2px;text-align: center;" :span="6">{{ item.deliveryTime }}</a-col>
  68. <a-col style="padding: 0px 2px;text-align: center;" :span="4">{{ item.customerMobile }}</a-col>
  69. <a-col
  70. :title="item.stationName"
  71. style="padding: 0px 2px;text-align: center;white-space: nowrap;
  72. text-overflow: ellipsis;
  73. overflow: hidden;
  74. word-break: break-all;"
  75. :span="6">
  76. {{ item.stationName }}
  77. </a-col>
  78. <a-col style="padding: 0px 2px;text-align: center;" :span="5">{{ item.rubbishTypeDictValue }}</a-col>
  79. <a-col style="padding: 0px 2px;text-align: center;" :span="3">{{ item.rubbishWeight }}</a-col>
  80. </a-row>
  81. </div>
  82. </vue-seamless-scroll>
  83. </div>
  84. </div>
  85. </a-col>
  86. <a-col
  87. :xs="24"
  88. :sm="24"
  89. :md="24"
  90. :lg="24"
  91. :xl="24"
  92. :xxl="8">
  93. <div class="switper-cont">
  94. <div class="data-title">实时新增用户</div>
  95. <div class="chart-box data-form">
  96. <div class="swipter-title">
  97. <a-row :gutter="0">
  98. <a-col style="padding: 0px 2px;text-align: center;" :span="16">注册时间</a-col>
  99. <a-col style="padding: 0px 2px;text-align: center;" :span="8">用户手机</a-col>
  100. </a-row>
  101. </div>
  102. <vue-seamless-scroll :data="userLogData" :class-option="classOption" class="seamless-warp">
  103. <a-spin class="loading" :spinning="userLogSpinning" tip="Loading..." />
  104. <div class="swipter-data" v-for="item in userLogData" :key="item.id">
  105. <a-row :gutter="0">
  106. <a-col style="padding: 0px 2px;text-align: center;" :span="16">{{ item.registerTime }}</a-col>
  107. <a-col style="padding: 0px 2px;text-align: center;" :span="8">{{ item.mobile }}</a-col>
  108. </a-row>
  109. </div>
  110. </vue-seamless-scroll>
  111. </div>
  112. </vue-seamless-scroll>
  113. </div>
  114. </a-col>
  115. </a-row>
  116. <!-- 图表数据 -->
  117. <a-row :gutter="[24,24]" type="flex" justify="space-between">
  118. <a-col
  119. :xs="24"
  120. :sm="20"
  121. :md="12"
  122. :lg="12"
  123. :xl="12"
  124. :xxl="8">
  125. <div class="chart-cont">
  126. <div class="data-title">设备分布统计图</div>
  127. <chart-pie
  128. ref="chartPie"
  129. class="chart-box"
  130. :value="equipmentPieData"
  131. title=""
  132. unit="%"
  133. :color="equipmentPieColor"
  134. :xAxisRotate="resize"
  135. text="" />
  136. </div>
  137. </a-col>
  138. <a-col
  139. xs="24"
  140. :sm="20"
  141. :md="12"
  142. :lg="12"
  143. :xl="12"
  144. :xxl="8">
  145. <div class="chart-cont">
  146. <div class="data-title">设备数量统计对比图</div>
  147. <chart-bar
  148. ref="ChartBar"
  149. class="chart-box"
  150. color="#0000fe"
  151. :yMax="1000"
  152. yUnit="台"
  153. :value="equipmentBarData"
  154. :seriesData="equipmentBarSeriesData"
  155. text="" />
  156. </div>
  157. </a-col>
  158. <a-col
  159. xs="24"
  160. :sm="20"
  161. :md="16"
  162. :lg="12"
  163. :xl="12"
  164. :xxl="8">
  165. <div class="chart-cont">
  166. <div class="data-title">项目介绍</div>
  167. <div class="chart-box textCount">
  168. 全市共有党政机关、企事业单位827个,职工人数16655人。其中,市级单位334个,秦都区297个,渭城区196个;居民小区1010个(两区居民小区994个,保障房小区16个),
  169. 物业管理小区770个(两区754个,保障房小区16个)。其中,市本级保障房小区12个,物业管理小区12个。秦都区654个(含保障房小区3个),物业管理小区584个。渭城区
  170. 344个(含保障房小区1个),物业管理174个(含保障房小区1个);学校(中小学、幼儿园)200所,其中,秦都区139所(中小学53所、幼儿园86所)。渭城区61所(中小
  171. 学27所、幼儿园34所)。截止目前,城区已建成试点单位67个, 设置智能分类箱
  172. <span class="blue">{{ deviceTotal }}</span>组, 积分兑换亭<span class="blue">{{ sellerTotal }}</span>个, 无人兑换机<span
  173. class="blue">{{ machineTotal }}</span>个, 覆盖28处办公场所的177个机关事业单位,居民小区29个,企业2个,学校8个。 软件注册人数<span class="blue">{{ userTotal }}</span>人,
  174. 周回收有害垃圾5.646公斤、 可回收物9.26吨, 日处理厨余垃圾106吨。
  175. </div>
  176. </div>
  177. </a-col>
  178. </a-row>
  179. </div>
  180. </div>
  181. </div>
  182. </template>
  183. <script>
  184. import {
  185. ChartPie,
  186. ChartBar
  187. } from '@/components/Echarts'
  188. import vueSeamlessScroll from 'vue-seamless-scroll'
  189. import {
  190. getUserTotal,
  191. getDeviceTotal,
  192. getOfficialPartnerTotal,
  193. getDeliveryCarousel,
  194. getCustomerCarousel,
  195. getExchangeMachineTotal,
  196. getMapStation
  197. } from '@/api/nodeMap.js'
  198. export default {
  199. name: 'NodeMap',
  200. components: {
  201. ChartPie,
  202. ChartBar,
  203. vueSeamlessScroll
  204. },
  205. data () {
  206. return {
  207. map: null, // 地图对象
  208. markerLayer: null, // 地图点标记对象
  209. infoWindow: null, // 地图信息窗口对象
  210. isMapFull: false, // 地图是否全屏
  211. userTotal: 0, // 用户总数
  212. deviceTotal: 0, // 设备总数
  213. sellerTotal: 0, // 兑换亭总数
  214. machineTotal: 0, // 兑换机总数
  215. deliverySpinning: true, // 实时投递数据loading
  216. userLogSpinning: true, // 实时用户数据loading
  217. deliveryLogData: [], // 实时投递数据
  218. userLogData: [], // 实时用户数据
  219. screenWidth: document.body.clientWidth, // 这里是给到了一个默认值
  220. timer: false,
  221. // 设备分布统计图
  222. equipmentPieData: [{
  223. name: '学校',
  224. value: '14'
  225. },
  226. {
  227. name: '居民小区',
  228. value: '49'
  229. },
  230. {
  231. name: '公共机构',
  232. value: '32'
  233. },
  234. {
  235. name: '企业',
  236. value: '5'
  237. }
  238. ],
  239. equipmentPieColor: ['#ffaa00', '#00ff00', '#00aaff', '#ff55ff', '#1dc5d4', '#8465c7', '#00ffff'],
  240. // 设备数量统计对比图数据
  241. equipmentBarData: [{
  242. name: '智能分类箱',
  243. value: 0
  244. },
  245. {
  246. name: '积分兑换亭',
  247. value: 0
  248. },
  249. {
  250. name: '无人兑换机',
  251. value: 0
  252. }
  253. ],
  254. equipmentBarSeriesData: ['0', '0', '0']
  255. }
  256. },
  257. computed: {
  258. resize () {
  259. console.log(this.screenWidth, 'this.screenWidth')
  260. if (this.screenWidth < 1640) {
  261. return 40
  262. } else {
  263. return 0
  264. }
  265. },
  266. mapHeight () {
  267. return this.isMapFull ? '80vh' : '45vh'
  268. },
  269. classOption () {
  270. return {
  271. step: 0.4, // 数值越大速度滚动越快
  272. limitMoveNum: 2, // 开始无缝滚动的数据量 this.dataList.length
  273. hoverStop: true, // 是否开启鼠标悬停stop
  274. direction: 1, // 0向下 1向上 2向左 3向右
  275. openWatch: true, // 开启数据实时监控刷新dom
  276. singleHeight: 0, // 单步运动停止的高度(默认值0是无缝不停止的滚动) direction => 0/1
  277. singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3
  278. waitTime: 1000 // 单步运动停止的时间(默认值1000ms)
  279. }
  280. }
  281. },
  282. watch: {
  283. screenWidth (val) {
  284. // 为了避免频繁触发resize函数,使用定时器
  285. if (!this.timer) {
  286. this.screenWidth = val
  287. this.timer = true
  288. const that = this
  289. setTimeout(function () {
  290. // that.screenWidth = that.$store.state.canvasWidth
  291. console.log(that.screenWidth)
  292. that.timer = false
  293. }, 400)
  294. }
  295. }
  296. },
  297. methods: {
  298. // 初始化地图
  299. initMap () {
  300. // 定义地图中心点坐标 默认定位在陕西省咸阳市
  301. var center = new TMap.LatLng(34.329828, 108.708344)
  302. // 定义map变量,调用 TMap.Map() 构造函数创建地图
  303. this.map = new TMap.Map(document.getElementById('container'), {
  304. center: center, // 设置地图中心点坐标
  305. zoom: 17.2, // 设置地图缩放级别
  306. pitch: 43.5, // 设置俯仰角
  307. rotation: 45 // 设置地图旋转角度
  308. })
  309. // 创建并初始化MultiMarker 点标记
  310. this.markerLayer = new TMap.MultiMarker({
  311. map: this.map, // 指定地图容器
  312. // 样式定义
  313. styles: {
  314. // 创建一个styleId为"myStyle"的样式(styles的子属性名即为styleId)
  315. 'myStyle': new TMap.MarkerStyle({
  316. 'width': 25, // 点标记样式宽度(像素)
  317. 'height': 25, // 点标记样式高度(像素)
  318. 'src': '../mark.png', // 图片路径
  319. // 焦点在图片中的像素位置,一般大头针类似形式的图片以针尖位置做为焦点,圆形点以圆心位置为焦点
  320. 'anchor': {
  321. x: 16,
  322. y: 32
  323. }
  324. })
  325. },
  326. // 点标记数据数组
  327. geometries: []
  328. })
  329. // 初始化infoWindow
  330. this.infoWindow = new TMap.InfoWindow({
  331. map: this.map,
  332. position: new TMap.LatLng(39.984104, 116.307503),
  333. offset: {
  334. x: 0,
  335. y: -32
  336. } // 设置信息窗相对position偏移像素
  337. })
  338. this.infoWindow.close() // 初始关闭信息窗关闭
  339. this.markerLayer.on('mousemove', this.showMarkTitle)
  340. // 监听地图加载完成
  341. this.map.on('tilesloaded', this.getMapMark)
  342. },
  343. // 获取地图点坐标
  344. getMapMark () {
  345. getMapStation({}).then(res => {
  346. const list = res.data || []
  347. const data = []
  348. list.map((item, index) => {
  349. if (item.lat <= 85.051128 && item.lat >= -85.051128) {
  350. const p = {
  351. 'id': item.id, // 点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
  352. 'styleId': 'myStyle', // 指定样式id
  353. 'position': new TMap.LatLng(Number(item.lat), Number(item.lng)), // 点标记坐标位置
  354. 'properties': { // 自定义属性
  355. 'title': item.name
  356. }
  357. }
  358. data.push(p)
  359. }
  360. })
  361. // 增加点标记
  362. this.markerLayer.add(data)
  363. })
  364. },
  365. // 显示地图标记点名称
  366. showMarkTitle (evt) {
  367. this.infoWindow.open() // 打开信息窗
  368. this.infoWindow.setPosition(evt.geometry.position) // 设置信息窗位置
  369. this.infoWindow.setContent(evt.geometry.properties.title) // 设置信息窗内容
  370. },
  371. // 获取用户总数
  372. getUser () {
  373. getUserTotal({}).then(res => {
  374. this.userTotal = res.data || 0
  375. })
  376. },
  377. // 获取设备总数
  378. getDevice () {
  379. getDeviceTotal({}).then(res => {
  380. this.deviceTotal = res.data || 0
  381. this.equipmentBarData[0].value = this.deviceTotal
  382. console.log(this.equipmentBarData, 'this.equipmentBarData')
  383. })
  384. },
  385. // 获取兑换亭总数
  386. getSeller () {
  387. getOfficialPartnerTotal({}).then(res => {
  388. this.sellerTotal = res.data || 0
  389. this.equipmentBarData[1].value = this.sellerTotal
  390. })
  391. },
  392. // 获取兑换机总数
  393. getMachine () {
  394. getExchangeMachineTotal({}).then(res => {
  395. this.machineTotal = res.data || 0
  396. this.equipmentBarData[2].value = this.machineTotal
  397. })
  398. },
  399. // 获取实时投递数据
  400. getDeliveryLog () {
  401. getDeliveryCarousel({}).then(res => {
  402. this.deliveryLogData = res.data.list || []
  403. })
  404. },
  405. // 获取实时用户数据
  406. getUserLog () {
  407. getCustomerCarousel({}).then(res => {
  408. this.userLogData = res.data.list || []
  409. })
  410. },
  411. // 地图是否全屏展示
  412. isFullscreenMap () {
  413. this.isMapFull = !this.isMapFull
  414. }
  415. },
  416. mounted () {
  417. this.initMap()
  418. const that = this
  419. window.onresize = () => {
  420. return (() => {
  421. window.screenWidth = document.body.clientWidth
  422. that.screenWidth = window.screenWidth
  423. })()
  424. }
  425. },
  426. beforeRouteEnter (to, from, next) {
  427. next(vm => {
  428. vm.deliverySpinning = true
  429. vm.userLogSpinning = true
  430. setTimeout(() => {
  431. vm.deliverySpinning = false
  432. vm.userLogSpinning = false
  433. }, 2000)
  434. vm.getUser()
  435. vm.getDevice()
  436. vm.getSeller()
  437. vm.getMachine()
  438. vm.getDeliveryLog()
  439. vm.getUserLog()
  440. // 5分钟轮播
  441. setInterval(() => vm.getDeliveryLog(), 1000 * 60 * 5)
  442. setInterval(() => vm.getUserLog(), 1000 * 60 * 5)
  443. })
  444. },
  445. beforeRouterLeave (to, from, next) {
  446. next(vm => {
  447. // 销毁地图
  448. vm.map.destroy()
  449. })
  450. }
  451. }
  452. </script>
  453. <style lang="less" scoped="scoped">
  454. .nodeMap-contenter {
  455. width: 100%;
  456. .map-cont {
  457. width: 100%;
  458. margin-bottom: 20px;
  459. .map-title {
  460. background-color: #5c9eff;
  461. height: 45px;
  462. line-height: 45px;
  463. text-align: center;
  464. color: #fff;
  465. font-size: 20px;
  466. }
  467. .map-footer {
  468. background-color: #5c9eff;
  469. padding: 5px;
  470. color: #fff;
  471. font-size: 16px;
  472. .map-data {
  473. padding: 3px;
  474. }
  475. }
  476. }
  477. .data-cont {
  478. width: 100%;
  479. }
  480. .data-form {
  481. text-align: center;
  482. width: 100%;
  483. border: 1px solid #d6d6d6;
  484. border-radius: 10px;
  485. padding: 10px;
  486. overflow: hidden;
  487. display: flex;
  488. flex-direction: column;
  489. .seamless-warp {
  490. flex: 1;
  491. overflow: hidden;
  492. }
  493. .swipter-data {
  494. height: 40px;
  495. line-height: 40px;
  496. }
  497. .loading {
  498. margin-top: 50px;
  499. }
  500. .network-name {
  501. white-space: nowrap;
  502. text-overflow: ellipsis;
  503. overflow: hidden;
  504. word-break: break-all;
  505. }
  506. }
  507. .switper-cont {
  508. height: 300px;
  509. padding: 20px;
  510. background-color: #fff;
  511. border-radius: 10px;
  512. box-shadow: 5px 5px 8px #888888;
  513. display: flex;
  514. flex-direction: column;
  515. }
  516. .data-title {
  517. font-size: 16px;
  518. }
  519. .data-title:before {
  520. content: "";
  521. border: 2px solid #5c9eff;
  522. margin-right: 20px;
  523. }
  524. .chart-box {
  525. flex: 1;
  526. width: 100%;
  527. margin-top: 10px;
  528. }
  529. .chart-cont {
  530. padding: 20px;
  531. height: 400px;
  532. background-color: #fff;
  533. border-radius: 20px;
  534. box-shadow: 5px 5px 8px #888888;
  535. display: flex;
  536. flex-direction: column;
  537. .textCount {
  538. text-indent: 2em;
  539. .blue {
  540. color: #5c9eff;
  541. padding: 0px 2px;
  542. }
  543. }
  544. }
  545. }
  546. </style>