monitorTree.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. <template>
  2. <div class="widget-monitorTree" ref="widget-monitorTree">
  3. <div class="content">
  4. <div class="header">
  5. <!-- <div class="title">设备监测树</div> -->
  6. <el-input placeholder="设备名称搜索" v-model="filterText" size="mini" clearable>
  7. <el-button slot="append" icon="el-icon-search"></el-button>
  8. </el-input>
  9. </div>
  10. <div class="statistic">
  11. <div class="deviceTypeGroup">
  12. <el-tree
  13. class="filter-tree"
  14. show-checkbox
  15. :data="treeData"
  16. :props="defaultProps"
  17. default-expand-all
  18. node-key="id"
  19. :default-checked-keys="defaultCheckedKeys"
  20. :filter-node-method="filterNode"
  21. @check="getCheckList()"
  22. @node-click="handleTreeNodeClick"
  23. ref="tree"
  24. >
  25. <span slot-scope="{ node }">
  26. {{ node.label }}
  27. <img v-if="setNodeImg(node)" :src="`${setNodeImg(node)}`" style="width: 16px; height: 16px" />
  28. </span>
  29. </el-tree>
  30. </div>
  31. </div>
  32. </div>
  33. </div>
  34. </template>
  35. <script lang="ts">
  36. import { Vue, Component, Prop, Watch } from 'vue-property-decorator'
  37. import { getLatAndLon } from '@/views/groupPage/util'
  38. import { getZhgdCameraData, getZhgdEquipmentData, getZhgdHelmetData, getZhgdGPSData } from '@/views/groupPage/apis'
  39. import deviceInfo from '@/views/groupPage/districtPageModules/customTools/infoComponents/deviceInfo.vue'
  40. const Cesium = (window as any).Cesium
  41. let customDataSource = new Cesium.CustomDataSource('monitorTree')
  42. let timeInterval = null
  43. let handler = null
  44. //设备监测树
  45. @Component({ name: 'monitorTree' })
  46. export default class monitorTree extends Vue {
  47. @Watch('filterText')
  48. onChangeMethod(val) {
  49. let target = this.$refs.tree as any
  50. target.filter(val)
  51. }
  52. @Watch('deviceCheckList', { deep: true })
  53. onChangeCheckListMethod(val) {
  54. this.showMapPoint()
  55. this.$store.state.bigScreen.monitorTreeCheckHistory = []
  56. val.forEach((item) => {
  57. this.$store.state.bigScreen.monitorTreeCheckHistory.push(item.id)
  58. })
  59. }
  60. @Watch('treeData', { deep: true })
  61. onChangeTreeDataMethod() {
  62. this.setCheckInfo()
  63. }
  64. @Watch('currentDeviceShow')
  65. onChangeCurrentDeviceShow(n) {
  66. if (n) this.initInfoPop(n, Cesium.Cartesian3.fromDegrees(n.position[0], n.position[1]))
  67. }
  68. get historyInfo() {
  69. return this.$store.state.bigScreen.monitorTreeCheckHistory
  70. }
  71. get currentDeviceShow() {
  72. return this.$store.state.bigScreen.currentDeviceShow
  73. }
  74. viewer
  75. //参数
  76. deviceCheckList = []
  77. filterText = ''
  78. treeData = []
  79. defaultProps = { children: 'children', label: 'name' }
  80. defaultCheckedKeys = []
  81. mounted() {
  82. this.initCom()
  83. //定时刷新
  84. if (timeInterval) {
  85. clearInterval(timeInterval)
  86. timeInterval = null
  87. }
  88. timeInterval = setInterval(() => {
  89. this.initCom()
  90. }, 10000) //600000
  91. }
  92. initCom() {
  93. if (!this.$store.state.bigScreen.isInitViewer) return
  94. let init = (window as any).viewer
  95. if (init) {
  96. this.viewer = init
  97. this.clearDataSource()
  98. this.viewer.dataSources.add(customDataSource)
  99. this.getData()
  100. } else {
  101. console.error('viewer未初始化')
  102. }
  103. }
  104. //获取监测设备数据
  105. async getData() {
  106. //摄像头
  107. const cameraRes = await getZhgdCameraData({})
  108. let cameras = cameraRes.result.map((item) => {
  109. return {
  110. ...item,
  111. status: item.status == 0 ? '离线' : '在线',
  112. type: item.type == 1 ? '球机' : '枪机',
  113. deviceType: '摄像头'
  114. }
  115. })
  116. //监控设备
  117. const monitorRes = await getZhgdEquipmentData({})
  118. let monitors = monitorRes.result.map((item) => {
  119. item.zhgdEquipment.status = item.zhgdEquipment.status == 0 ? '离线' : '在线'
  120. item.zhgdEquipment.deviceType = '监控设备'
  121. item.zhgdEquipment.name = item.zhgdEquipment.equipment_name
  122. item.zhgdEquipment.latitude = item.zhgdEquipment.lat
  123. item.zhgdEquipment.longitude = item.zhgdEquipment.lon
  124. return item.zhgdEquipment
  125. })
  126. //安全帽
  127. const helmetRes = await getZhgdHelmetData({})
  128. let helmets = helmetRes.result.map((item) => {
  129. return {
  130. ...item,
  131. deviceType: '安全帽',
  132. latitude: item.lat,
  133. longitude: item.lon,
  134. name: item.person_id,
  135. status: item.status == 1 ? '未激活' : item.status == 2 ? '离线' : item.status == 3 ? '在线' : '无'
  136. }
  137. })
  138. //GPS设备
  139. // const gpsRes = await getZhgdGPSData({})
  140. this.treeData = [
  141. { name: '智慧工地摄像头', children: cameras },
  142. { name: '智慧工地监控设备', children: monitors },
  143. // { name: '智慧工地GPS', children: [] },
  144. { name: '智慧工地安全帽', children: helmets }
  145. ]
  146. }
  147. setStatusIconColor(type) {
  148. let color = type === '在线' ? '#2BA7FF' : type === '离线' ? 'grey' : type === '正常' ? '#2BA7FF' : 'red'
  149. return 'background-color:' + color
  150. }
  151. //节点过滤
  152. filterNode(value, data) {
  153. if (!value) return true
  154. return data.name.indexOf(value) !== -1
  155. }
  156. setNodeImg(node) {
  157. let iconSrc
  158. return iconSrc
  159. }
  160. handleTreeNodeClick(data) {
  161. if (!data.longitude || !data.latitude) {
  162. this.$message.info('暂无位置信息')
  163. return
  164. }
  165. let entity = customDataSource.entities.getById(data.name)
  166. let item = {
  167. name: data.name,
  168. id: data.id,
  169. position: [Number(data.longitude), Number(data.latitude)],
  170. info: data
  171. }
  172. this.viewer.flyTo(entity).then(() => {
  173. this.initInfoPop(item, Cesium.Cartesian3.fromDegrees(item.position[0], item.position[1]))
  174. })
  175. }
  176. getCheckList() {
  177. this.deviceCheckList = (this.$refs.tree as any).getCheckedNodes().filter((item) => !item.children)
  178. }
  179. setCheckInfo() {
  180. let checkedList = []
  181. let tempCheckList = this.treeData
  182. .map((item, index) => {
  183. if (item.children) {
  184. item.children.forEach((child) => {
  185. if (this.historyInfo != null) {
  186. if (child.id && this.historyInfo.some((id) => id == child.id)) {
  187. checkedList.push(child.id)
  188. }
  189. } else {
  190. checkedList.push(child.id)
  191. }
  192. })
  193. }
  194. return item.children
  195. })
  196. .flat()
  197. let temp = []
  198. tempCheckList.forEach((item) => {
  199. if (checkedList.some((id) => id == item.id)) {
  200. temp.push(item)
  201. }
  202. })
  203. this.deviceCheckList = temp
  204. this.$nextTick(() => {
  205. this.defaultCheckedKeys = checkedList
  206. this.$store.state.bigScreen.monitorTreeCheckHistory = this.defaultCheckedKeys
  207. })
  208. }
  209. showMapPoint() {
  210. if (Cesium.defined(customDataSource)) {
  211. customDataSource.entities.removeAll()
  212. }
  213. for (let index = 0; index < this.deviceCheckList.length; index++) {
  214. const item = this.deviceCheckList[index]
  215. if (!item.longitude || !item.latitude) continue
  216. const infoData = {
  217. id: item.id,
  218. name: item.name,
  219. position: [Number(item.longitude), Number(item.latitude)],
  220. info: item
  221. }
  222. this.addEntity(infoData)
  223. }
  224. this.activeMapEvent()
  225. }
  226. addEntity(options) {
  227. let image = this.iconSelect(options.info.deviceType)
  228. const position = Cesium.Cartesian3.fromDegrees(options.position[0], options.position[1], 0)
  229. var bottomPosition = Cesium.Cartographic.fromCartesian(position)
  230. var entity = new Cesium.Entity({
  231. id: options.name,
  232. type: 'deviceInfo',
  233. name: options.name,
  234. show: true,
  235. position: Cesium.Cartographic.toCartesian(bottomPosition),
  236. billboard: {
  237. sizeInMeters: false,
  238. image: image,
  239. width: 40,
  240. height: 65,
  241. // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  242. verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
  243. horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
  244. disableDepthTestDistance: 9999999
  245. },
  246. info: options
  247. })
  248. customDataSource.entities.add(entity)
  249. }
  250. iconSelect(type) {
  251. let icon = ''
  252. switch (type) {
  253. case '摄像头':
  254. icon = require('@/views/groupPage/images/设备/摄像头.png')
  255. break
  256. case '监控设备':
  257. icon = require('@/views/groupPage/images/设备/监控设备.png')
  258. break
  259. case '安全帽':
  260. icon = require('@/views/groupPage/images/设备/安全帽.png')
  261. break
  262. }
  263. return icon
  264. }
  265. activeMapEvent() {
  266. const that = this
  267. handler = new Cesium.ScreenSpaceEventHandler(this.viewer.canvas)
  268. handler.setInputAction(
  269. function (movement) {
  270. var pickedObject = this.viewer.scene.pick(movement.position)
  271. const coordinate = getLatAndLon(this.viewer, movement)
  272. const position = Cesium.Cartesian3.fromDegrees(coordinate.longitude, coordinate.latitude, coordinate.altitude)
  273. // 使用时,最好利用pickPositionSupported判断一下浏览器是否支持模型高度拾取
  274. if (this.viewer.scene.pickPositionSupported && Cesium.defined(pickedObject)) {
  275. if (pickedObject.id.type === 'deviceInfo') {
  276. that.initInfoPop(pickedObject.id.info, position)
  277. }
  278. } else {
  279. }
  280. }.bind(this),
  281. Cesium.ScreenSpaceEventType.LEFT_CLICK
  282. )
  283. }
  284. initInfoPop(data, position) {
  285. let deviceInfoConstructor = Vue.extend(deviceInfo)
  286. const deviceInfoModule = new deviceInfoConstructor({
  287. data: {
  288. info: data.info,
  289. id: data.id.toString(),
  290. position
  291. },
  292. store: this.$store
  293. }).$mount()
  294. }
  295. clearDataSource() {
  296. if (Cesium.defined(customDataSource)) {
  297. customDataSource.entities.removeAll()
  298. this.viewer.dataSources.remove(customDataSource)
  299. }
  300. if (timeInterval) {
  301. clearInterval(timeInterval)
  302. timeInterval = null
  303. }
  304. }
  305. beforeDestroy() {}
  306. }
  307. </script>
  308. <style lang="scss" scoped>
  309. .animate__flipInX,
  310. .animate__flipOutX {
  311. animation-duration: 3s; //动画持续时间
  312. animation-delay: 0s; //动画延迟时间
  313. }
  314. .widget-monitorTree {
  315. width: 100%;
  316. height: 100%;
  317. color: #eee;
  318. //font
  319. font-family: Source Han Sans CN;
  320. & ::placeholder {
  321. color: rgba(255, 255, 255, 0.3);
  322. }
  323. .content {
  324. width: 2.1875rem /* 420/192 */;
  325. height: 1.979167rem /* 380/192 */;
  326. // background: linear-gradient(0deg, rgba(2, 20, 37, 0.56) 0%, #072643 100%);
  327. .header {
  328. height: 0.208333rem /* 40/192 */;
  329. margin: 0 0.052083rem 0 0.052083rem;
  330. padding: 0.026042rem /* 5/192 */;
  331. // border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  332. display: flex;
  333. align-items: center;
  334. justify-content: space-between;
  335. .title {
  336. font-size: 0.083333rem /* 16/192 */;
  337. font-weight: 500;
  338. color: #2ba7ff;
  339. white-space: nowrap;
  340. }
  341. .close {
  342. display: flex;
  343. align-items: center;
  344. height: 100%;
  345. color: rgba(138, 211, 253, 1);
  346. font-size: 0.09375rem /* 18/192 */;
  347. cursor: pointer;
  348. }
  349. /deep/ .el-input__inner {
  350. background-color: rgb(9, 48, 84);
  351. border: none;
  352. color: #eee;
  353. height: 0.177083rem /* 34/192 */;
  354. font-size: 0.072917rem /* 14/192 */;
  355. }
  356. /deep/ .el-input-group__append {
  357. background-color: rgb(9, 48, 84);
  358. border: none;
  359. }
  360. /deep/ .el-icon-search:before {
  361. color: rgba(138, 211, 253, 0.3);
  362. font-size: 0.09375rem /* 18/192 */;
  363. }
  364. }
  365. .statistic {
  366. display: flex;
  367. flex-flow: column;
  368. width: 100%;
  369. height: 100%;
  370. margin-top: 0.052083rem /* 10/192 */;
  371. .deviceTypeGroup {
  372. width: 100%;
  373. height: calc(100% - 0.104167rem /* 20/192 */);
  374. overflow: auto;
  375. /deep/ .el-tree {
  376. background: transparent;
  377. color: #8eb2ce;
  378. font-size: 0.083333rem /* 16/192 */;
  379. .el-tree-node__content {
  380. background-color: transparent;
  381. }
  382. .el-tree-node__content:hover {
  383. background-color: rgb(62, 70, 112);
  384. }
  385. // div[role='group'] > .el-tree-node {
  386. // width: 50%;
  387. // float: left;
  388. // }
  389. .el-tree-node.is-current > .el-tree-node__content {
  390. background: rgba(22, 119, 255, 0.1);
  391. border-right: 3px solid #1677ff;
  392. color: #4b95fe;
  393. /deep/ .el-tree-node__expand-icon {
  394. color: rgb(0, 112, 255);
  395. }
  396. /deep/ .is-leaf {
  397. color: rgba(0, 0, 0, 0);
  398. }
  399. }
  400. }
  401. .el-checkbox {
  402. color: #fff;
  403. margin: 0.052083rem /* 10/192 */ 0;
  404. }
  405. /deep/ .el-checkbox__inner {
  406. background: #0a1525;
  407. border-color: rgba(3, 109, 190, 1);
  408. }
  409. /deep/ .el-checkbox__inner::after {
  410. border: 2px solid rgba(17, 156, 255, 1);
  411. border-left: 0;
  412. border-top: 0;
  413. }
  414. /deep/ .el-checkbox__input.is-checked .el-checkbox__inner {
  415. background: #0a1525;
  416. border-color: rgba(3, 109, 190, 1);
  417. }
  418. /deep/ .el-checkbox__input.is-checked + .el-checkbox__label {
  419. color: #fff;
  420. }
  421. }
  422. }
  423. }
  424. }
  425. </style>