videoMap.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. <template>
  2. <div class="app-container">
  3. <div style="background-color: #ffffff;height:100%;">
  4. <div id="container"></div>
  5. </div>
  6. </div>
  7. </template>
  8. <script setup lang="ts">
  9. import AMapLoader from '@amap/amap-jsapi-loader';
  10. /*在Vue3中使用时,需要引入Vue3中的shallowRef方法(使用shallowRef进行非深度监听,
  11. 因为在Vue3中所使用的Proxy拦截操作会改变JSAPI原生对象,所以此处需要区别Vue2使用方式对地图对象进行非深度监听,
  12. 否则会出现问题,建议JSAPI相关对象采用非响应式的普通对象来存储)*/
  13. import { shallowRef } from '@vue/reactivity';
  14. import { ref } from "vue";
  15. import { getDicts } from '@/api/system/dict/data'
  16. import { getGpsData } from '@/api/gpsData/index'
  17. import { getCameraPlayerConfig } from '@/api/gpsData';
  18. import carImage from "@/assets/images/icon/小货车.png";
  19. import add from '@/views/car/carInfo/add.vue';
  20. import EZUIKit from 'ezuikit-js';
  21. const props = defineProps({
  22. showMarket: {
  23. type: Boolean,
  24. default: true
  25. },
  26. darkType: {
  27. type: Boolean,
  28. default: false
  29. },
  30. })
  31. var content = [
  32. "<div class='info' style='display:flex;flex-direction:column'><b>摄像头</b>",
  33. " <div id='video-container' style='border-radius: 10px;width:30vw'></div>",
  34. "</div>",
  35. ];
  36. // const map = shallowRef(null);
  37. const path = ref([]);
  38. const playerObj = ref(null)
  39. const current_position = ref([]);
  40. let mapObj = ref()
  41. const allInfoWindow = shallowRef(null);
  42. let singleMarker = ref(null);
  43. const lineObj = ref(null);
  44. const getDictsData = (param) => {
  45. getDicts(param).then(res => {
  46. for (var index in res.data) {
  47. if (res.data[index].cssClass == '2') {
  48. console.log(res.data[index])
  49. var obj = res.data[index].dictValue.split(",");
  50. // console.log(obj.toString())
  51. var location = formatGCJ(Number(obj[0]), Number(obj[1]));
  52. // console.log(location.toString())
  53. addMarker(location, res.data[index], '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png', 'kdlocation')
  54. } else if (res.data[index].cssClass == '3') {
  55. console.log(res.data[index])
  56. var obj = res.data[index].dictValue.split(",");
  57. // console.log(obj.toString())
  58. var location = formatGCJ(Number(obj[0]), Number(obj[1]));
  59. // console.log(location.toString())
  60. addMarker(location, res.data[index], '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png', 'zzlocation')
  61. }
  62. }
  63. })
  64. }
  65. const closeVideo = () => {
  66. console.log('关闭摄像头')
  67. playerObj.value.stop()
  68. playerObj.value.destroy()
  69. document.getElementById('video-container').innerHTML = "";
  70. }
  71. const getGpsDataList = (param) => {
  72. getGpsData(param).then(res => {
  73. addLine(res.rows);
  74. if (singleMarker.value != null) {
  75. mapObj.value.remove(singleMarker.value);
  76. }
  77. var car = res.rows[res.rows.length - 1]
  78. if (car) {
  79. var location = formatGCJ(car.lng, car.lat);
  80. // addMarker(location, car, "../src/assets/images/icon/小货车.png", 'car')
  81. addMarker(location, car, carImage, 'car')
  82. }
  83. })
  84. }
  85. const setDataList = (data) => {
  86. tableData.value = data
  87. }
  88. const initMap = () => {
  89. window._AMapSecurityConfig = {
  90. securityJsCode: 'b16e0ff6d065a3b8a87e6c448e982ca4',
  91. }
  92. AMapLoader.load({
  93. key: "c60c1af6d4f39c5220f3ad36298a8830", // 申请好的Web端开发者Key,首次调用 load 时必填
  94. version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
  95. // plugins:[''], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
  96. }).then(async (AMap) => {
  97. const map = new AMap.Map("container", { //设置地图容器id
  98. viewMode: "3D", //是否为3D地图模式
  99. zoom: 13, //初始化地图级别
  100. center: [119.807462, 26.958413], //初始化地图中心点位置
  101. });
  102. console.log(11111111111);
  103. console.log(props.darkType)
  104. if (props.darkType) {
  105. var styleName = "amap://styles/darkblue";
  106. map.setMapStyle(styleName);
  107. }
  108. mapObj.value = map;
  109. const infoWindow = new AMap.InfoWindow({
  110. offset: new AMap.Pixel(16, -45),
  111. //隐藏原生弹窗边框和关闭按钮
  112. isCustom: false,
  113. autoMove: true,
  114. avoid: [20, 20, 20, 20],
  115. // 点击地图关闭
  116. closeWhenClickMap: false,
  117. });
  118. allInfoWindow.value = infoWindow
  119. infoWindow.on('close', closeVideo)
  120. // console.log(content)
  121. infoWindow.setContent(content.join(""));
  122. // infoWindow.open(map, [119.807462, 26.958413]);
  123. // 添加插件
  124. // AMap.plugin(["AMap.ToolBar", "AMap.Scale", "AMap.HawkEye", "AMap.Geolocation", "AMap.MapType", "AMap.MouseTool"], function () {
  125. // //异步同时加载多个插件
  126. // // 添加地图插件
  127. // // map.addControl(new AMap.ToolBar()); // 工具条控件;范围选择控件
  128. // map.addControl(new AMap.Scale()); // 显示当前地图中心的比例尺
  129. // map.addControl(new AMap.HawkEye()); // 显示缩略图
  130. // map.addControl(new AMap.Geolocation()); // 定位当前位置
  131. // // map.addControl(new AMap.MapType()); // 实现默认图层与卫星图,实时交通图层之间切换
  132. // });
  133. if (props.showMarket) {
  134. getDictsData("tbl_sites");
  135. }
  136. // 单击
  137. map.on('click', (e) => {
  138. // lng ==> 经度值 lat => 维度值
  139. current_position.value = [e.lnglat.lng, e.lnglat.lat];
  140. path.value.push([e.lnglat.lng, e.lnglat.lat]);
  141. //addMarker();
  142. // addPolyLine();
  143. })
  144. // 折线
  145. function addPolyLine() {
  146. const polyline = new AMap.Polyline({
  147. path: path.value,
  148. isOutline: true,
  149. outlineColor: "#ffeeff",
  150. borderWeight: 1,
  151. strokeColor: "#3366FF",
  152. strokeOpacity: 0.6,
  153. strokeWeight: 5,
  154. // 折线样式还支持 'dashed'
  155. strokeStyle: "solid",
  156. // strokeStyle是dashed时有效
  157. // strokeDasharray: [10, 5],
  158. lineJoin: "round",
  159. lineCap: "round",
  160. zIndex: 50,
  161. });
  162. map.add([polyline]);
  163. }
  164. }).catch(e => {
  165. console.log(e);
  166. if (e == '多个不一致的 key') {
  167. const map = new AMap.Map("container", { //设置地图容器id
  168. viewMode: "3D", //是否为3D地图模式
  169. zoom: 13, //初始化地图级别
  170. center: [119.807462, 26.958413], //初始化地图中心点位置
  171. });
  172. console.log(11111111111);
  173. console.log(props.darkType)
  174. if (props.darkType) {
  175. var styleName = "amap://styles/darkblue";
  176. map.setMapStyle(styleName);
  177. }
  178. mapObj.value = map;
  179. const infoWindow = new AMap.InfoWindow({
  180. offset: new AMap.Pixel(16, -45),
  181. //隐藏原生弹窗边框和关闭按钮
  182. isCustom: false,
  183. autoMove: true,
  184. avoid: [20, 20, 20, 20],
  185. // 点击地图关闭
  186. closeWhenClickMap: false,
  187. });
  188. allInfoWindow.value = infoWindow
  189. infoWindow.on('close', closeVideo)
  190. // console.log(content)
  191. infoWindow.setContent(content.join(""));
  192. if (props.showMarket) {
  193. getDictsData("tbl_sites");
  194. }
  195. // 单击
  196. map.on('click', (e) => {
  197. // lng ==> 经度值 lat => 维度值
  198. current_position.value = [e.lnglat.lng, e.lnglat.lat];
  199. path.value.push([e.lnglat.lng, e.lnglat.lat]);
  200. //addMarker();
  201. // addPolyLine();
  202. })
  203. // 折线
  204. function addPolyLine() {
  205. const polyline = new AMap.Polyline({
  206. path: path.value,
  207. isOutline: true,
  208. outlineColor: "#ffeeff",
  209. borderWeight: 1,
  210. strokeColor: "#3366FF",
  211. strokeOpacity: 0.6,
  212. strokeWeight: 5,
  213. // 折线样式还支持 'dashed'
  214. strokeStyle: "solid",
  215. // strokeStyle是dashed时有效
  216. // strokeDasharray: [10, 5],
  217. lineJoin: "round",
  218. lineCap: "round",
  219. zIndex: 50,
  220. });
  221. map.add([polyline]);
  222. }
  223. }
  224. })
  225. }
  226. const addMarker = (obj, data, src, type) => {
  227. const marker = new AMap.Marker({
  228. icon: new AMap.Icon({
  229. image: src,
  230. imageSize: new AMap.Size(22, 28) //图标大小
  231. }),
  232. position: obj, // 这里我们通过上面的点击获取经纬度坐标,实时添加标记
  233. // 通过设置 offset 来添加偏移量
  234. offset: new AMap.Pixel(-10, -20),
  235. });
  236. marker.setLabel({
  237. offset: new AMap.Pixel(-30, 20),
  238. content: data.dictLabel
  239. });
  240. if (type == 'car') {
  241. singleMarker.value = marker
  242. }
  243. marker.setMap(mapObj.value);
  244. //给marker添加属性
  245. marker.setExtData(data)
  246. //给marker添加点击事件
  247. marker.on('click', clickMarker)
  248. }
  249. const addLine = (list) => {
  250. if (lineObj.value != null) {
  251. mapObj.value.remove([lineObj.value])
  252. }
  253. var dataList = []
  254. for (var index in list) {
  255. // console.log('转换前坐标' + [list[index].lng, list[index].lat])
  256. var location = formatGCJ(list[index].lng, list[index].lat);
  257. // console.log('转换后坐标' + [location[0], location[1]])
  258. var obj = new AMap.LngLat(location[0], location[1]);
  259. dataList.push(obj)
  260. }
  261. var polyline = new AMap.Polyline({
  262. path: dataList,
  263. strokeWeight: 2, //线条宽度
  264. strokeColor: "red", //线条颜色
  265. lineJoin: "round", //折线拐点连接处样式
  266. });
  267. // polyline.setMap(mapObj);
  268. // console.log(mapObj)
  269. lineObj.value = polyline
  270. mapObj.value.add(polyline);
  271. }
  272. const emit = defineEmits(['childToParent']);
  273. const clickMarker = (e) => {
  274. // console.log(e.target.getExtData());
  275. // var json = JSON.parse(e.target.getExtData().remark)
  276. console.log(e.target.getExtData().remark)
  277. if (e.target.getExtData().remark != null && e.target.getExtData().remark != '') {
  278. var showVideo = false
  279. var json = JSON.parse(e.target.getExtData().remark)
  280. for (var index in json) {
  281. if (json[index].type == "camera") {
  282. showVideo = true;
  283. allInfoWindow.value.open(mapObj.value, e.target.getPosition());
  284. getCameraPlayerConfig(json[index].code).then(res => {
  285. console.log(res.data);
  286. var player = new EZUIKit.EZUIKitPlayer({
  287. template: "pcLive",
  288. id: 'video-container', // 视频容器ID
  289. ...res.data
  290. })
  291. playerObj.value = player
  292. })
  293. }
  294. }
  295. if (!showVideo) {
  296. ElMessage({
  297. message: '该站点未配置摄像头',
  298. type: 'warning',
  299. })
  300. }
  301. } else {
  302. ElMessage({
  303. message: '该站点未配置摄像头',
  304. type: 'warning',
  305. })
  306. }
  307. // emit('childToParent', e.target.getPosition())
  308. }
  309. const formatGCJ = (lng, lat) => {
  310. const x_PI = 3.14159265358979324 * 3000.0 / 180.0
  311. const PI = 3.1415926535897932384626
  312. const a = 6378245.0
  313. const ee = 0.00669342162296594323
  314. if (outOfChina(lng, lat)) {
  315. return [lng, lat]
  316. }
  317. else {
  318. var dlat = transformlat(lng - 105.0, lat - 35.0)
  319. var dlng = transformlng(lng - 105.0, lat - 35.0)
  320. var radlat = lat / 180.0 * PI
  321. var magic = Math.sin(radlat)
  322. magic = 1 - ee * magic * magic
  323. var sqrtmagic = Math.sqrt(magic)
  324. dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
  325. dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI)
  326. const mglat = lat + dlat
  327. const mglng = lng + dlng
  328. return [mglng, mglat]
  329. }
  330. /*判断是否在国内,不在国内则不做偏移*/
  331. function transformlat(lng, lat) {
  332. var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng))
  333. ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0
  334. ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0
  335. ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0
  336. return ret
  337. }
  338. /**
  339. * 纬度转换
  340. * @param { Number } lng
  341. * @param { Number } lat
  342. */
  343. function transformlng(lng, lat) {
  344. var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng))
  345. ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0
  346. ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0
  347. ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0
  348. return ret
  349. }
  350. /**
  351. * 判断是否在国内,不在国内则不做偏移
  352. * @param {*} lng
  353. * @param {*} lat
  354. */
  355. function outOfChina(lng, lat) {
  356. return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false)
  357. }
  358. }
  359. initMap()
  360. console.log(props.showMarket);
  361. // getGpsDataList();
  362. defineExpose({
  363. getGpsDataList,
  364. });
  365. </script>
  366. <style>
  367. #container {
  368. padding: 0px;
  369. margin: 0px;
  370. width: 100%;
  371. height: 100%;
  372. }
  373. .info {
  374. background-color: #fff;
  375. text-align: center;
  376. padding: 10px;
  377. position: relative;
  378. border: 1px solid #b9b9b9;
  379. }
  380. .info img {
  381. width: 30px;
  382. height: 23px;
  383. position: absolute;
  384. left: calc(50% - 15px);
  385. bottom: -23px;
  386. }
  387. .closeDiv {
  388. position: absolute;
  389. top: 1px;
  390. right: 10px;
  391. font-size: 20px;
  392. z-index: 30;
  393. color: red;
  394. }
  395. /* .amap-marker-label{
  396. border: 0px solid #00f;
  397. background-color: transparent;
  398. color: #ffffff;
  399. } */
  400. </style>