map.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  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="auto" 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 === 1">在线</el-tag>
  28. <el-tag size="small" type="info" v-if="channel.status === 0">离线</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 (device, data, isCatalog) {
  99. this.device = device;
  100. if (data.channelId && !isCatalog) {
  101. // 点击通道
  102. if (data[this.longitudeStr] * data[this.latitudeStr] === 0) {
  103. this.$message.error('未获取到位置信息');
  104. } else {
  105. if (this.layer != null) {
  106. this.$refs.map.removeLayer(this.layer);
  107. }
  108. this.closeInfoBox()
  109. this.layer = this.$refs.map.addLayer([{
  110. position: [data[this.longitudeStr], data[this.latitudeStr]],
  111. image: {
  112. src: this.getImageByChannel(data),
  113. anchor: [0.5, 1]
  114. },
  115. data: data
  116. }], this.featureClickEvent)
  117. this.$refs.map.panTo([data[this.longitudeStr], data[this.latitudeStr]], mapParam.maxZoom)
  118. }
  119. }
  120. },
  121. contextmenuEventHandler: function (device, event, data, isCatalog) {
  122. console.log(device)
  123. console.log(device.online)
  124. this.device = device;
  125. if (data.channelId && !isCatalog) {
  126. // 点击通道
  127. this.$contextmenu({
  128. items: [
  129. {
  130. label: "播放",
  131. icon: "el-icon-video-play",
  132. disabled: device.online === 0,
  133. onClick: () => {
  134. this.play(data);
  135. }
  136. },
  137. {
  138. label: "编辑位置",
  139. icon: "el-icon-edit",
  140. disabled: false,
  141. onClick: () => {
  142. this.edit(data)
  143. }
  144. },
  145. {
  146. label: "轨迹查询",
  147. icon: "el-icon-map-location",
  148. disabled: false,
  149. onClick: () => {
  150. this.getTrace(data)
  151. }
  152. }
  153. ],
  154. event, // 鼠标事件信息
  155. customClass: "custom-class", // 自定义菜单 class
  156. zIndex: 3000, // 菜单样式 z-index
  157. });
  158. } else {
  159. if (typeof data.channelId === "undefined") {
  160. this.deviceOrSubChannelMenu(event, data)
  161. }else {
  162. // TODO 子目录暂时不支持查询他下面所有设备, 支持支持查询直属于这个目录的设备
  163. this.deviceOrSubChannelMenu(event, data)
  164. }
  165. }
  166. },
  167. deviceOrSubChannelMenu: function (event, data) {
  168. // 点击设备
  169. this.$contextmenu({
  170. items: [
  171. {
  172. label: "定位",
  173. icon: "el-icon-s-promotion",
  174. disabled: false,
  175. onClick: () => {
  176. if (!data.channelId) {
  177. this.deviceService.getAllChannel(false, false, data.deviceId, this.channelsHandler)
  178. }
  179. if (data.channelId && data.subCount > 0) {
  180. // 点击子目录
  181. this.deviceService.getAllSubChannel(false, data.deviceId, data.channelId, this.channelsHandler)
  182. }
  183. }
  184. }
  185. ],
  186. event, // 鼠标事件信息
  187. customClass: "custom-class", // 自定义菜单 class
  188. zIndex: 3000, // 菜单样式 z-index
  189. });
  190. },
  191. channelsHandler: function (channels) {
  192. console.log(2)
  193. if (channels.length > 0) {
  194. this.clean()
  195. this.closeInfoBox()
  196. let params = [];
  197. for (let i = 0; i < channels.length; i++) {
  198. let longitude = channels[i][this.longitudeStr];
  199. let latitude = channels[i][this.latitudeStr];
  200. if (longitude * latitude === 0) {
  201. continue;
  202. }
  203. let item = {
  204. position: [longitude, latitude],
  205. image: {
  206. src: this.getImageByChannel(channels[i]),
  207. anchor: [0.5, 1]
  208. },
  209. data: channels[i]
  210. }
  211. params.push(item);
  212. }
  213. console.log(3)
  214. this.layer = this.$refs.map.addLayer(params, this.featureClickEvent)
  215. console.log(4)
  216. if (params.length === 1) {
  217. this.$refs.map.panTo([channels[0][this.longitudeStr], channels[0][this.latitudeStr]], mapParam.maxZoom)
  218. } else if (params.length > 1) {
  219. this.$refs.map.fit(this.layer)
  220. } else {
  221. this.$message.error('未获取到位置信息');
  222. }
  223. } else {
  224. this.$message.error('未获取到位置信息');
  225. }
  226. },
  227. getImageByChannel: function (channel) {
  228. let src = "static/images/gis/camera.png"
  229. switch (channel.ptztype) {
  230. case 1:
  231. if (channel.status === 1) {
  232. src = "static/images/gis/camera1.png"
  233. } else {
  234. src = "static/images/gis/camera1-offline.png"
  235. }
  236. break;
  237. case 2:
  238. if (channel.status === 1) {
  239. src = "static/images/gis/camera2.png"
  240. } else {
  241. src = "static/images/gis/camera2-offline.png"
  242. }
  243. break;
  244. case 3:
  245. if (channel.status === 1) {
  246. src = "static/images/gis/camera3.png"
  247. } else {
  248. src = "static/images/gis/camera3-offline.png"
  249. }
  250. break;
  251. default:
  252. if (channel.status === 1) {
  253. src = "static/images/gis/camera.png"
  254. } else {
  255. src = "static/images/gis/camera-offline.png"
  256. }
  257. }
  258. return src;
  259. },
  260. featureClickEvent: function (channels) {
  261. this.closeInfoBox()
  262. if (channels.length > 0) {
  263. this.channel = channels[0]
  264. }
  265. this.$nextTick(() => {
  266. let position = [this.channel[this.longitudeStr], this.channel[this.latitudeStr]];
  267. this.infoBoxId = this.$refs.map.openInfoBox(position, this.$refs.infobox, [0, -50])
  268. })
  269. },
  270. closeInfoBox: function () {
  271. if (this.infoBoxId != null) {
  272. this.$refs.map.closeInfoBox(this.infoBoxId)
  273. }
  274. },
  275. play: function (channel) {
  276. let deviceId = channel.deviceId;
  277. this.isLoging = true;
  278. let channelId = channel.channelId;
  279. console.log("通知设备推流1:" + deviceId + " : " + channelId);
  280. let that = this;
  281. this.$axios({
  282. method: 'get',
  283. url: '/api/play/start/' + deviceId + '/' + channelId
  284. }).then(function (res) {
  285. that.isLoging = false;
  286. if (res.data.code === 0) {
  287. that.$refs.devicePlayer.openDialog("media", deviceId, channelId, {
  288. streamInfo: res.data.data,
  289. hasAudio: channel.hasAudio
  290. });
  291. } else {
  292. that.$message.error(res.data.msg);
  293. }
  294. }).catch(function (e) {
  295. });
  296. },
  297. edit: function (data) {
  298. this.$message.warning('暂不支持');
  299. },
  300. getTrace: function (data) {
  301. // this.$message.warning('暂不支持');
  302. this.clean()
  303. this.$refs.queryTrace.openDialog(data, (channelPositions) => {
  304. console.log("getTrace")
  305. console.log(channelPositions)
  306. if (channelPositions.length === 0) {
  307. this.$message.success('未查询到轨迹信息');
  308. } else {
  309. let positions = [];
  310. for (let i = 0; i < channelPositions.length; i++) {
  311. if (channelPositions[i].cnLng * channelPositions[i].cnLat > 0) {
  312. positions.push([channelPositions[i].cnLng, channelPositions[i].cnLat])
  313. }
  314. }
  315. if (positions.length === 0) {
  316. this.$message.success('未查询到轨迹信息');
  317. return;
  318. }
  319. this.lineLayer = this.$refs.map.addLineLayer(positions)
  320. this.$refs.map.fit(this.lineLayer)
  321. }
  322. })
  323. },
  324. clean: function (){
  325. if (this.lineLayer != null) {
  326. this.$refs.map.removeLayer(this.lineLayer)
  327. }
  328. if (this.infoBoxId != null) {
  329. this.$refs.map.closeInfoBox(this.infoBoxId)
  330. }
  331. if (this.layer != null) {
  332. this.$refs.map.removeLayer(this.layer)
  333. }
  334. }
  335. },
  336. };
  337. </script>
  338. <style>
  339. .infobox-content{
  340. width: 260px;
  341. background-color: #FFFFFF;
  342. padding: 10px;
  343. border-radius: 10px;
  344. border: 1px solid #e2e2e2;
  345. }
  346. .infobox-content::after {
  347. position: absolute;
  348. bottom: -11px;
  349. left: 130px;
  350. display: block;
  351. content: "";
  352. width: 16px;
  353. height: 16px;
  354. background: url('~@static/images/arrow.png') no-repeat center;
  355. }
  356. .infobox-close {
  357. position: absolute;
  358. right: 1rem;
  359. top: 1rem;
  360. color: #000000;
  361. cursor:pointer
  362. }
  363. .el-descriptions__title {
  364. font-size: 1rem;
  365. font-weight: 700;
  366. padding: 20px 20px 0px 23px;
  367. text-align: center;
  368. width: 100%;
  369. }
  370. </style>