map.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. <template>
  2. <div id="devicePosition" style="width: 100vw; height: 91vh;">
  3. <el-container v-if="onOff" style="height: 91vh;" v-loading="isLoging">
  4. <el-aside width="400px" style="background-color: #ffffff">
  5. <DeviceTree ref="deviceTree" :clickEvent="clickEvent" :contextMenuEvent="contextmenuEventHandler" ></DeviceTree>
  6. </el-aside>
  7. <el-main style="height: 91vh; padding: 0">
  8. <MapComponent ref="map"></MapComponent>
  9. </el-main>
  10. </el-container>
  11. <div v-if="!onOff" style="width: 100%; height:100%; text-align: center; line-height: 5rem">
  12. <p>地图功能已关闭</p>
  13. </div>
  14. <div ref="infobox" v-if="channel != null " >
  15. <div v-if="channel != null" class="infobox-content">
  16. <el-descriptions class="margin-top" :title="channel.name" :column="1" :colon="true" size="mini" :labelStyle="labelStyle" >
  17. <el-descriptions-item label="编号" >{{channel.channelId}}</el-descriptions-item>
  18. <el-descriptions-item label="型号">{{channel.model}}</el-descriptions-item>
  19. <el-descriptions-item label="经度" >{{channel[longitudeStr]}}</el-descriptions-item>
  20. <el-descriptions-item label="纬度" >{{channel[latitudeStr]}}</el-descriptions-item>
  21. <el-descriptions-item label="生产厂商">{{channel.manufacture}}</el-descriptions-item>
  22. <el-descriptions-item label="行政区域" >{{channel.civilCode}}</el-descriptions-item>
  23. <el-descriptions-item label="设备归属" >{{channel.owner}}</el-descriptions-item>
  24. <el-descriptions-item label="安装地址" >{{channel.address == null?'未知': channel.address}}</el-descriptions-item>
  25. <el-descriptions-item label="云台类型" >{{channel.PTZTypeText}}</el-descriptions-item>
  26. <el-descriptions-item label="通道状态">
  27. <el-tag size="small" v-if="channel.status === true">在线</el-tag>
  28. <el-tag size="small" type="info" v-if="channel.status === false">离线</el-tag>
  29. </el-descriptions-item>
  30. </el-descriptions>
  31. <div style="padding-top: 10px">
  32. <el-button v-bind:disabled="device == null || device.online === 0" type="primary" size="small" title="播放" icon="el-icon-video-play" @click="play(channel)"></el-button>
  33. <el-button type="primary" size="small" title="编辑位置" icon="el-icon-edit" @click="edit(channel)"></el-button>
  34. <el-button type="primary" size="small" title="轨迹查询" icon="el-icon-map-location" @click="getTrace(channel)"></el-button>
  35. </div>
  36. <span class="infobox-close el-icon-close" @click="closeInfoBox()"></span>
  37. </div>
  38. </div>
  39. <devicePlayer ref="devicePlayer" ></devicePlayer>
  40. <queryTrace ref="queryTrace" ></queryTrace>
  41. </div>
  42. </template>
  43. <script>
  44. import MapComponent from "./common/MapComponent.vue";
  45. import DeviceService from "./service/DeviceService";
  46. import DeviceTree from "./common/DeviceTree";
  47. import channelMapInfobox from "./dialog/channelMapInfobox";
  48. import devicePlayer from './dialog/devicePlayer.vue'
  49. import queryTrace from './dialog/queryTrace.vue'
  50. export default {
  51. name: "map",
  52. components: {
  53. MapComponent,
  54. DeviceTree,
  55. channelMapInfobox,
  56. devicePlayer,
  57. queryTrace,
  58. },
  59. data() {
  60. return {
  61. onOff: typeof window.mapParam !== "undefined" && window.mapParam.enable,
  62. deviceService: new DeviceService(),
  63. layer: null,
  64. lineLayer: null,
  65. channel: null,
  66. device: null,
  67. infoBoxId: null,
  68. labelStyle: {
  69. width: "56px"
  70. },
  71. isLoging: false,
  72. longitudeStr: "longitude",
  73. latitudeStr: "latitude",
  74. };
  75. },
  76. created() {
  77. if (this.$route.query.deviceId) {
  78. console.log(this.$route.query.deviceId)
  79. // this.$refs.deviceTree.openByDeivceId(this.$route.query.deivceId)
  80. setTimeout(()=>{ // 延迟以等待地图加载完成 TODO 后续修改为通过是实际这;状态加回调完成
  81. this.deviceService.getAllChannel(false, false, this.$route.query.deviceId, this.channelsHandler)
  82. }, 1000)
  83. }
  84. if (window.mapParam.coordinateSystem == "GCJ-02") {
  85. this.longitudeStr = "longitudeGcj02";
  86. this.latitudeStr = "latitudeGcj02";
  87. }else if (window.mapParam.coordinateSystem == "WGS84") {
  88. this.longitudeStr = "longitudeWgs84";
  89. this.latitudeStr = "latitudeWgs84";
  90. }else {
  91. this.longitudeStr = "longitude";
  92. this.latitudeStr = "latitude";
  93. }
  94. },
  95. destroyed() {
  96. },
  97. methods: {
  98. clickEvent: function (channelId) {
  99. this.$axios({
  100. method: 'get',
  101. url: `/api/common/channel/one`,
  102. params: {
  103. id: channelId,
  104. }
  105. }).then((res) => {
  106. if (res.data.code === 0) {
  107. console.log(res.data.data)
  108. console.log(res.data.data.gbLongitude)
  109. console.log(res.data.data.gbLatitude)
  110. if (!res.data.data.gbLongitude || !res.data.data.gbLatitude) {
  111. this.$message.error({
  112. showClose: true,
  113. message: "位置信息不存在"
  114. })
  115. }else {
  116. if (this.layer != null) {
  117. this.$refs.map.removeLayer(this.layer);
  118. }
  119. this.closeInfoBox()
  120. this.layer = this.$refs.map.addLayer([{
  121. position: [res.data.data.gbLongitude, res.data.data.gbLatitude],
  122. image: {
  123. src: this.getImageByChannel(res.data.data),
  124. anchor: [0.5, 1]
  125. },
  126. data: res.data.data
  127. }], this.featureClickEvent)
  128. this.$refs.map.panTo([res.data.data.gbLongitude, res.data.data.gbLatitude], mapParam.maxZoom)
  129. }
  130. }
  131. }).catch(function (error) {
  132. console.log(error);
  133. });
  134. },
  135. contextmenuEventHandler: function (device, event, data, isCatalog) {
  136. console.log(device)
  137. console.log(device.online)
  138. this.device = device;
  139. if (data.channelId && !isCatalog) {
  140. // 点击通道
  141. this.$contextmenu({
  142. items: [
  143. {
  144. label: "播放",
  145. icon: "el-icon-video-play",
  146. disabled: device.online === 0,
  147. onClick: () => {
  148. this.play(data);
  149. }
  150. },
  151. {
  152. label: "编辑位置",
  153. icon: "el-icon-edit",
  154. disabled: false,
  155. onClick: () => {
  156. this.edit(data)
  157. }
  158. },
  159. {
  160. label: "轨迹查询",
  161. icon: "el-icon-map-location",
  162. disabled: false,
  163. onClick: () => {
  164. this.getTrace(data)
  165. }
  166. }
  167. ],
  168. event, // 鼠标事件信息
  169. customClass: "custom-class", // 自定义菜单 class
  170. zIndex: 3000, // 菜单样式 z-index
  171. });
  172. } else {
  173. if (typeof data.channelId === "undefined") {
  174. this.deviceOrSubChannelMenu(event, data)
  175. }else {
  176. // TODO 子目录暂时不支持查询他下面所有设备, 支持支持查询直属于这个目录的设备
  177. this.deviceOrSubChannelMenu(event, data)
  178. }
  179. }
  180. },
  181. deviceOrSubChannelMenu: function (event, data) {
  182. // 点击设备
  183. this.$contextmenu({
  184. items: [
  185. {
  186. label: "定位",
  187. icon: "el-icon-s-promotion",
  188. disabled: false,
  189. onClick: () => {
  190. if (!data.channelId) {
  191. this.deviceService.getAllChannel(false, false, data.deviceId, this.channelsHandler)
  192. }
  193. if (data.channelId && data.subCount > 0) {
  194. // 点击子目录
  195. this.deviceService.getAllSubChannel(false, data.deviceId, data.channelId, this.channelsHandler)
  196. }
  197. }
  198. },
  199. {
  200. label: "查询轨迹",
  201. icon: "el-icon-map-location",
  202. disabled: false,
  203. onClick: () => {
  204. this.getTrace(data)
  205. }
  206. }
  207. ],
  208. event, // 鼠标事件信息
  209. customClass: "custom-class", // 自定义菜单 class
  210. zIndex: 3000, // 菜单样式 z-index
  211. });
  212. },
  213. channelsHandler: function (channels) {
  214. console.log(2)
  215. if (channels.length > 0) {
  216. this.clean()
  217. this.closeInfoBox()
  218. let params = [];
  219. for (let i = 0; i < channels.length; i++) {
  220. let longitude = channels[i][this.longitudeStr];
  221. let latitude = channels[i][this.latitudeStr];
  222. if (longitude * latitude === 0) {
  223. continue;
  224. }
  225. let item = {
  226. position: [longitude, latitude],
  227. image: {
  228. src: this.getImageByChannel(channels[i]),
  229. anchor: [0.5, 1]
  230. },
  231. data: channels[i]
  232. }
  233. params.push(item);
  234. }
  235. console.log(3)
  236. this.layer = this.$refs.map.addLayer(params, this.featureClickEvent)
  237. console.log(4)
  238. if (params.length === 1) {
  239. this.$refs.map.panTo([channels[0][this.longitudeStr], channels[0][this.latitudeStr]], mapParam.maxZoom)
  240. } else if (params.length > 1) {
  241. this.$refs.map.fit(this.layer)
  242. } else {
  243. this.$message.error({
  244. showClose: true,
  245. message: "未获取到位置信息"
  246. })
  247. }
  248. } else {
  249. this.$message.error({
  250. showClose: true,
  251. message: "未获取到位置信息"
  252. })
  253. }
  254. },
  255. getImageByChannel: function (channel) {
  256. let src = "static/images/gis/camera.png"
  257. switch (channel.gbPtzType) {
  258. case 1:
  259. if (channel.gbStatus === "ON") {
  260. src = "static/images/gis/camera1.png"
  261. } else {
  262. src = "static/images/gis/camera1-offline.png"
  263. }
  264. break;
  265. case 2:
  266. if (channel.gbStatus === "ON") {
  267. src = "static/images/gis/camera2.png"
  268. } else {
  269. src = "static/images/gis/camera2-offline.png"
  270. }
  271. break;
  272. case 3:
  273. if (channel.gbStatus === "ON") {
  274. src = "static/images/gis/camera3.png"
  275. } else {
  276. src = "static/images/gis/camera3-offline.png"
  277. }
  278. break;
  279. default:
  280. if (channel.gbStatus === "ON") {
  281. src = "static/images/gis/camera.png"
  282. } else {
  283. src = "static/images/gis/camera-offline.png"
  284. }
  285. }
  286. return src;
  287. },
  288. featureClickEvent: function (channels) {
  289. this.closeInfoBox()
  290. if (channels.length > 0) {
  291. this.channel = channels[0]
  292. }
  293. this.$nextTick(() => {
  294. let position = [this.channel[this.longitudeStr], this.channel[this.latitudeStr]];
  295. this.infoBoxId = this.$refs.map.openInfoBox(position, this.$refs.infobox, [0, -50])
  296. })
  297. },
  298. closeInfoBox: function () {
  299. if (this.infoBoxId != null) {
  300. this.$refs.map.closeInfoBox(this.infoBoxId)
  301. }
  302. },
  303. play: function (channel) {
  304. let deviceId = channel.deviceId;
  305. this.isLoging = true;
  306. let channelId = channel.channelId;
  307. console.log("通知设备推流1:" + deviceId + " : " + channelId);
  308. let that = this;
  309. this.$axios({
  310. method: 'get',
  311. url: '/api/play/start/' + deviceId + '/' + channelId
  312. }).then(function (res) {
  313. that.isLoging = false;
  314. if (res.data.code === 0) {
  315. that.$refs.devicePlayer.openDialog("media", deviceId, channelId, {
  316. streamInfo: res.data.data,
  317. hasAudio: channel.hasAudio
  318. });
  319. } else {
  320. that.$message.error(res.data.msg);
  321. }
  322. }).catch(function (e) {
  323. });
  324. },
  325. edit: function (data) {
  326. this.$message.warning({
  327. showClose: true,
  328. message: "暂不支持"
  329. })
  330. },
  331. getTrace: function (data) {
  332. // this.$message.warning('暂不支持');
  333. this.clean()
  334. this.$refs.queryTrace.openDialog(data, (channelPositions) => {
  335. console.log("getTrace")
  336. console.log(channelPositions)
  337. if (channelPositions.length === 0) {
  338. this.$message.info({
  339. showClose: true,
  340. message: "未查询到轨迹信息"
  341. })
  342. } else {
  343. let positions = [];
  344. for (let i = 0; i < channelPositions.length; i++) {
  345. if (channelPositions[i][this.longitudeStr] * channelPositions[i][this.latitudeStr] > 0) {
  346. positions.push([channelPositions[i][this.longitudeStr], channelPositions[i][this.latitudeStr]])
  347. }
  348. }
  349. if (positions.length === 0) {
  350. this.$message.info({
  351. showClose: true,
  352. message: "未查询到轨迹信息"
  353. })
  354. return;
  355. }
  356. this.lineLayer = this.$refs.map.addLineLayer(positions)
  357. this.$refs.map.fit(this.lineLayer)
  358. }
  359. })
  360. },
  361. clean: function (){
  362. if (this.lineLayer != null) {
  363. this.$refs.map.removeLayer(this.lineLayer)
  364. }
  365. if (this.infoBoxId != null) {
  366. this.$refs.map.closeInfoBox(this.infoBoxId)
  367. }
  368. if (this.layer != null) {
  369. this.$refs.map.removeLayer(this.layer)
  370. }
  371. }
  372. },
  373. };
  374. </script>
  375. <style>
  376. .infobox-content{
  377. width: 260px;
  378. background-color: #FFFFFF;
  379. padding: 10px;
  380. border-radius: 10px;
  381. border: 1px solid #e2e2e2;
  382. }
  383. .infobox-content::after {
  384. position: absolute;
  385. bottom: -11px;
  386. left: 130px;
  387. display: block;
  388. content: "";
  389. width: 16px;
  390. height: 16px;
  391. background: url('~@static/images/arrow.png') no-repeat center;
  392. }
  393. .infobox-close {
  394. position: absolute;
  395. right: 1rem;
  396. top: 1rem;
  397. color: #000000;
  398. cursor:pointer
  399. }
  400. .el-descriptions__title {
  401. font-size: 1rem;
  402. font-weight: 700;
  403. padding: 20px 20px 0px 23px;
  404. text-align: center;
  405. width: 100%;
  406. }
  407. </style>