map.vue 13 KB

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