PositionMap.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. <template>
  2. <div class="dialog">
  3. <div class="gradient-text dialog-title">请选择事发地点</div>
  4. <div class="icon-close" @click="handleClose"></div>
  5. <div class="dialog-content">
  6. <div class="form">
  7. <div class="line">
  8. <div class="form-item">
  9. <div class="text">详细地址</div>
  10. <el-input v-model="form.address" class="custom-input" placeholder="请输入" />
  11. <div v-if="searchPop" class="scroll_box">
  12. <div style="height: 60px; line-height: 60px">
  13. <span style="font-weight: bold">搜索结果列表</span>
  14. <i class="el-icon-close" style="float: right; font-size: 20px; cursor: pointer" @click="closeSearchList()" />
  15. </div>
  16. <el-scrollbar class="scroll" style="height: 250px">
  17. <div v-for="(item, index) in searchList" v-show="searchList.length" :key="index" class="item" @click="handlePanTo(index)">
  18. <el-image class="img" :src="item.img" :alt="item.name" lazy>
  19. <template #error>
  20. <div class="image-slot">
  21. <i class="el-icon-picture-outline"></i>
  22. </div>
  23. </template>
  24. </el-image>
  25. <div>
  26. <div class="text">{{ item.name }}</div>
  27. <div>{{ item.address }}</div>
  28. </div>
  29. </div>
  30. <div v-show="!searchList.length" class="empty" style="text-align: center">没有搜索到内容</div>
  31. </el-scrollbar>
  32. <el-pagination
  33. background
  34. small
  35. :hide-on-single-page="true"
  36. layout="prev, pager, next"
  37. :total="total"
  38. :page-size="pageSize"
  39. :current-page="pageNum"
  40. style="margin-top: 10px"
  41. @current-change="handleChangePage"
  42. >
  43. </el-pagination>
  44. </div>
  45. </div>
  46. <div class="btn1" @click="handleInput(0)">搜索</div>
  47. </div>
  48. <div class="line">
  49. <div class="form-item">
  50. <div class="text">经度</div>
  51. <el-input v-model="form.longitude" class="custom-input" placeholder="请输入" />
  52. </div>
  53. <div class="form-item" style="margin-left: 80px">
  54. <div class="text">详细地址</div>
  55. <el-input v-model="form.latitude" class="custom-input" placeholder="请输入" />
  56. </div>
  57. </div>
  58. </div>
  59. <div id="positionMap" class="map_box" :style="{ width: width, height: height }">
  60. <div id="map" class="map"></div>
  61. </div>
  62. <div class="footer">
  63. <div class="btn2" @click="handleClose">取消</div>
  64. <div class="btn1" style="margin-left: 40px" @click="submit">确定</div>
  65. </div>
  66. </div>
  67. </div>
  68. </template>
  69. <script setup name="PositionMap">
  70. import AMapLoader from '@amap/amap-jsapi-loader';
  71. import { useRouter } from 'vue-router';
  72. import { addEvent } from '@/api/emergencyCommandMap/JointDuty';
  73. const props = defineProps({
  74. visible: {
  75. type: Boolean,
  76. default: () => {
  77. return false;
  78. }
  79. }
  80. });
  81. const router = useRouter();
  82. const emits = defineEmits(['update:visible']);
  83. // 地图对象
  84. let map = null;
  85. let amap = {};
  86. let marker = null; //地图上的点标记
  87. let contextMenu = null;
  88. let lnglatPosition = ref([]); //选中的新坐标
  89. let rules = reactive({
  90. address: [{ required: true, message: '详细地址不能为空', trigger: 'blur' }],
  91. longitude: [{ required: true, message: '经度不能为空', trigger: 'blur' }],
  92. latitude: [{ required: true, message: '纬度不能为空', trigger: 'blur' }]
  93. });
  94. let pageNum = ref(1);
  95. let pageSize = ref(10);
  96. let total = ref(0);
  97. let searchList = ref([]);
  98. let searchPop = ref(false);
  99. let placeSearch;
  100. // 提交事件时间改为当前时间currentTime
  101. const now = new Date();
  102. const year = now.getFullYear();
  103. const month = String(now.getMonth() + 1).padStart(2, '0');
  104. const day = String(now.getDate()).padStart(2, '0');
  105. const hours = String(now.getHours()).padStart(2, '0');
  106. const minutes = String(now.getMinutes()).padStart(2, '0');
  107. const seconds = String(now.getSeconds()).padStart(2, '0');
  108. const currentTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  109. let form = reactive({
  110. address: '',
  111. longitude: '',
  112. latitude: '',
  113. event_title: '',
  114. event_type: '',
  115. event_level: '',
  116. event_status: '1',
  117. event_time: currentTime,
  118. report_time: '1977-01-01 00:00:00',
  119. deaths: '0',
  120. injuries: '0',
  121. missing: '0',
  122. event_source: '',
  123. event_description: '',
  124. casualties: '',
  125. del_flag: '9'
  126. });
  127. let geocoder = {};
  128. let width = ref('100%');
  129. let height = ref('100%');
  130. watch(
  131. () => props.visible,
  132. (n) => {
  133. if (n) {
  134. nextTick(() => {
  135. initMap();
  136. });
  137. }
  138. },
  139. {
  140. immediate: true
  141. }
  142. );
  143. onMounted(() => {
  144. window.addEventListener('resize', handleResize);
  145. });
  146. onUnmounted(() => {
  147. if (!!map) {
  148. map.off('rightclick');
  149. map.destroy();
  150. map = null;
  151. }
  152. window.removeEventListener('resize', handleResize);
  153. });
  154. function handleInput(flag) {
  155. if (!form.address) return;
  156. if (!flag) {
  157. //搜索
  158. total.value = 0;
  159. pageNum.value = 1;
  160. }
  161. if (!placeSearch) {
  162. placeSearch = new amap.PlaceSearch({
  163. pageSize: 10, // 每页条数,默认10,范围1-50
  164. pageIndex: 1, // 页码
  165. extensions: 'all' // 默认base,返回基本地址信息;all:返回详细信息
  166. });
  167. }
  168. searchPop.value = true;
  169. placeSearch.setPageIndex(pageNum.value);
  170. placeSearch.search(form.address, (status, result) => {
  171. // console.log(result.poiList.pois, 'result')
  172. if (!!result.poiList && result.poiList.pois && result.poiList.pois.length > 0) {
  173. let arr = [];
  174. const pois = result.poiList.pois;
  175. total.value = result.poiList ? result.poiList.count : 0;
  176. arr = pois.map((item) => {
  177. return {
  178. name: item.name,
  179. address: item.address,
  180. img: item.photos[0]?.url,
  181. lnglat: [item.location.lng, item.location.lat]
  182. };
  183. });
  184. searchList.value = arr;
  185. } else {
  186. total.value = 0;
  187. searchList.value = [];
  188. }
  189. });
  190. }
  191. function handleChangePage(newNum) {
  192. if (!searchPop.value) return;
  193. pageNum.value = newNum;
  194. handleInput(1);
  195. }
  196. function closeSearchList() {
  197. searchPop.value = false;
  198. searchList.value = [];
  199. total.value = 0;
  200. pageNum.value = 1;
  201. }
  202. // 地图中心的平移至指定点位置
  203. function handlePanTo(index) {
  204. let lnglat = searchList.value[index].lnglat;
  205. form.address = searchList.value[index].name + '(' + searchList.value[index].address + ')';
  206. form.longitude = lnglat[0];
  207. form.latitude = lnglat[1];
  208. map.panTo(lnglat);
  209. setMarks(lnglat);
  210. closeSearchList();
  211. }
  212. const initMap = async () => {
  213. let position = [110.93154257997, 21.669064031332];
  214. const AMap = await AMapLoader.load({
  215. key: '30d3d8448efd68cb0b284549fd41adcf', // 申请好的Web端开发者Key,首次调用 load 时必填
  216. version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
  217. plugins: ['AMap.PlaceSearch', 'AMap.ContextMenu', 'AMap.PolygonEditor', 'AMap.Geocoder'] // 插件列表
  218. });
  219. map = new AMap.Map('map', {
  220. viewMode: '3D', //是否为3D地图模式
  221. center: position,
  222. zoom: 15
  223. });
  224. amap = AMap;
  225. geocoder = new AMap.Geocoder({
  226. // city 指定进行编码查询的城市,支持传入城市名、adcode 和 citycode
  227. city: '010'
  228. });
  229. // 创建右键菜单
  230. ContextMenu();
  231. map.on('rightclick', handleRightclick);
  232. handleResize();
  233. };
  234. function ContextMenu() {
  235. contextMenu = new AMap.ContextMenu();
  236. contextMenu.addItem(
  237. '选择标点',
  238. () => {
  239. form.longitude = lnglatPosition.value[0];
  240. form.latitude = lnglatPosition.value[1];
  241. contextMenu.close();
  242. let lnglat = [form.longitude, form.latitude];
  243. geocoder.getAddress(lnglat, (status, result) => {
  244. if (status === 'complete' && result.info === 'OK') {
  245. form.address = result.regeocode.formattedAddress;
  246. }
  247. });
  248. setMarks(lnglat);
  249. },
  250. 1
  251. );
  252. }
  253. // 右键事件
  254. function handleRightclick(e) {
  255. let lnglat = [e.lnglat.getLng(), e.lnglat.getLat()];
  256. contextMenu.open(map, e.lnglat);
  257. lnglatPosition.value = lnglat;
  258. }
  259. function setMarks(lnglat) {
  260. //添加标记
  261. if (marker) map.remove(marker);
  262. marker = new AMap.Marker({
  263. position: lnglat,
  264. icon: new AMap.Icon({
  265. size: new AMap.Size(22, 28), //图标所处区域大小
  266. imageSize: new AMap.Size(22, 28), //图标大小
  267. image: '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png'
  268. }),
  269. anchor: 'bottom-center',
  270. offset: new AMap.Pixel(0, 0)
  271. });
  272. marker.setMap(map);
  273. }
  274. function handleClose() {
  275. emits('update:visible', false);
  276. }
  277. let queryFormRef = ref();
  278. let containerRef = ref();
  279. function handleResize() {
  280. const containerWidth = 2900 * (document.body.clientWidth / 8960);
  281. const containerHeight = 750 * (document.body.clientHeight / 2520);
  282. width.value = containerWidth + 'px';
  283. height.value = containerHeight + 'px';
  284. nextTick(() => {
  285. map.resize();
  286. });
  287. }
  288. function submit() {
  289. queryFormRef.value.validate((valid) => {
  290. if (valid) {
  291. console.log('提交数据', form);
  292. addEvent(form).then((res) => {
  293. router.push({
  294. path: '/emergencyCommandMap',
  295. query: {
  296. tempEventId: res.data
  297. }
  298. });
  299. });
  300. }
  301. });
  302. }
  303. </script>
  304. <style lang="scss" scoped>
  305. .map_box {
  306. position: relative;
  307. background: rgba(0, 0, 0, 0.3);
  308. margin-bottom: 20px;
  309. }
  310. .map {
  311. width: 100%;
  312. height: 100%;
  313. }
  314. .search {
  315. width: 50%;
  316. position: absolute;
  317. right: 2%;
  318. top: 10px;
  319. background: #fff;
  320. padding: 8px 8px;
  321. border-radius: 3px;
  322. display: flex;
  323. }
  324. .btn {
  325. margin-left: 10px;
  326. }
  327. .scroll_box {
  328. width: calc(100% - 165px);
  329. background: #0a2c5c;
  330. position: absolute;
  331. left: 165px;
  332. top: 80px;
  333. z-index: 9;
  334. padding: 20px;
  335. border-radius: 3px;
  336. .close {
  337. position: absolute;
  338. right: 10px;
  339. top: 10px;
  340. cursor: pointer;
  341. font-size: 40px;
  342. }
  343. :deep(.el-pagination__total) {
  344. color: #a7ccdf;
  345. font-size: 32px;
  346. }
  347. :deep(.el-pagination) {
  348. .btn-next,
  349. .btn-prev {
  350. background-color: transparent;
  351. border: none;
  352. .el-icon {
  353. font-size: 22px;
  354. color: #a7ccdf;
  355. }
  356. }
  357. .el-pager li {
  358. width: 64px;
  359. height: 64px;
  360. line-height: 64px;
  361. text-align: center;
  362. font-size: 32px;
  363. color: #a7ccdf;
  364. background-color: #0e3064;
  365. border: 1px solid #0c57a7;
  366. margin: 0 6px;
  367. &:hover {
  368. background-color: #038dff;
  369. border: 1px solid #038dff;
  370. }
  371. }
  372. .el-pager li.is-active {
  373. background-color: #038dff;
  374. border: 1px solid #038dff;
  375. }
  376. }
  377. }
  378. .scroll {
  379. width: 100%;
  380. height: auto !important;
  381. .item {
  382. display: flex;
  383. font-size: 32px;
  384. cursor: pointer;
  385. padding: 8px;
  386. &:hover {
  387. background-color: #102e76;
  388. }
  389. .img {
  390. width: 80px;
  391. height: 80px;
  392. min-width: 80px;
  393. margin-right: 15px;
  394. }
  395. :deep(.image-slot) {
  396. width: 100%;
  397. height: 100%;
  398. background-color: #f5f7fa;
  399. text-align: center;
  400. line-height: 50px;
  401. }
  402. :deep(.el-icon-picture-outline) {
  403. font-size: 32px;
  404. }
  405. .text {
  406. color: #3385ff;
  407. margin-bottom: 6px;
  408. }
  409. }
  410. }
  411. :deep(.el-scrollbar__wrap) {
  412. overflow-x: hidden !important;
  413. }
  414. :deep(.is-horizontal) {
  415. display: none;
  416. }
  417. .empty {
  418. margin: 20px 0;
  419. }
  420. .dialog {
  421. position: absolute;
  422. top: 90px;
  423. left: 900px;
  424. width: 3000px;
  425. height: 2000px;
  426. z-index: 2000;
  427. font-size: 36px;
  428. padding: 160px 60px 20px 65px;
  429. font-size: 36px;
  430. color: #fff;
  431. background: url('@/assets/images/position/positionDialog.png') no-repeat;
  432. .dialog-title {
  433. position: absolute;
  434. top: 40px;
  435. left: 60px;
  436. font-size: 72px;
  437. }
  438. .icon-close {
  439. cursor: pointer;
  440. position: absolute;
  441. top: 0px;
  442. right: -7px;
  443. width: 77px;
  444. height: 72px;
  445. background: url('@/assets/images/map/rightMenu/close.png') no-repeat;
  446. background-size: 100% 100%;
  447. }
  448. .dialog-content {
  449. padding: 10px 0;
  450. .map_box {
  451. overflow: hidden;
  452. }
  453. .footer {
  454. display: flex;
  455. align-items: center;
  456. position: absolute;
  457. bottom: 45px;
  458. right: 45px;
  459. }
  460. }
  461. }
  462. .form {
  463. .line {
  464. width: 100%;
  465. display: flex;
  466. align-items: center;
  467. margin-bottom: 30px;
  468. .form-item {
  469. flex: 1;
  470. display: flex;
  471. align-items: center;
  472. position: relative;
  473. .text {
  474. font-size: 36px;
  475. color: #eaf3fc;
  476. white-space: nowrap;
  477. margin-right: 20px;
  478. }
  479. :deep(.el-input__wrapper) {
  480. background-color: rgba(26, 144, 255, 0.15) !important;
  481. border: 4px solid rgba(26, 144, 255, 0.15) !important;
  482. }
  483. :deep(.el-input__inner) {
  484. height: 56px;
  485. line-height: 56px;
  486. }
  487. }
  488. }
  489. }
  490. .btn1 {
  491. width: 204px;
  492. height: 120px;
  493. background: url('@/assets/images/position/btn2.png') no-repeat;
  494. font-size: 36px;
  495. color: #ffffff;
  496. display: flex;
  497. justify-content: center;
  498. align-items: center;
  499. margin-right: -25px;
  500. cursor: pointer;
  501. margin-left: 70px;
  502. }
  503. .btn2 {
  504. width: 140px;
  505. height: 56px;
  506. background: url('@/assets/images/position/btn.png') no-repeat;
  507. font-size: 36px;
  508. color: #ffffff;
  509. display: flex;
  510. justify-content: center;
  511. align-items: center;
  512. margin-right: -25px;
  513. cursor: pointer;
  514. margin-left: 70px;
  515. }
  516. </style>