hookehuyr

新增别院和西园寺的模版-重新建立文件夹调整

...@@ -15,6 +15,7 @@ declare module '@vue/runtime-core' { ...@@ -15,6 +15,7 @@ declare module '@vue/runtime-core' {
15 ElButton: typeof import('element-plus/es')['ElButton'] 15 ElButton: typeof import('element-plus/es')['ElButton']
16 ElInput: typeof import('element-plus/es')['ElInput'] 16 ElInput: typeof import('element-plus/es')['ElInput']
17 ElOption: typeof import('element-plus/es')['ElOption'] 17 ElOption: typeof import('element-plus/es')['ElOption']
18 + ElProgress: typeof import('element-plus/es')['ElProgress']
18 ElSelect: typeof import('element-plus/es')['ElSelect'] 19 ElSelect: typeof import('element-plus/es')['ElSelect']
19 Floor: typeof import('./src/components/Floor/index.vue')['default'] 20 Floor: typeof import('./src/components/Floor/index.vue')['default']
20 InfoPopup: typeof import('./src/components/InfoPopup.vue')['default'] 21 InfoPopup: typeof import('./src/components/InfoPopup.vue')['default']
......
1 /* 1 /*
2 * @Date: 2023-05-29 11:10:19 2 * @Date: 2023-05-29 11:10:19
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-01-26 09:55:58 4 + * @LastEditTime: 2025-02-10 16:04:07
5 * @FilePath: /map-demo/src/route.js 5 * @FilePath: /map-demo/src/route.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
...@@ -84,4 +84,32 @@ export default [ ...@@ -84,4 +84,32 @@ export default [
84 title: '瓦片切图工具', 84 title: '瓦片切图工具',
85 }, 85 },
86 }, 86 },
87 + {
88 + path: '/xyx',
89 + component: () => import('@/views/xyx/index.vue'),
90 + meta: {
91 + title: '首页',
92 + },
93 + },
94 + {
95 + path: '/by',
96 + component: () => import('@/views/by/map.vue'),
97 + meta: {
98 + title: '地图',
99 + },
100 + },
101 + {
102 + path: '/by/scan',
103 + component: () => import('@/views/by/scan.vue'),
104 + meta: {
105 + title: '扫描',
106 + },
107 + },
108 + {
109 + path: '/by/info',
110 + component: () => import('@/views/by/info.vue'),
111 + meta: {
112 + title: '详情页',
113 + },
114 + },
87 ]; 115 ];
......
1 +<!--
2 + * @Date: 2024-09-14 17:48:55
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2024-09-21 23:04:40
5 + * @FilePath: /map-demo/src/views/bieyuan/index.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <div class="index-page">
10 + <div style="display: flex; flex-direction: column; align-items: center;">
11 + <van-image
12 + width="12rem"
13 + height="12rem"
14 + fit="contain"
15 + src="https://cdn.ipadbiz.cn/bieyuan/map/icon/index_logo@3x.png"
16 + />
17 + <div style="margin-top: 2rem; font-size: 0.95rem; letter-spacing: 5px; color: #47525F;">山水逢甘露,静心遇桃源</div>
18 + </div>
19 + <div @click="goTo" style="border: 1px solid #DD7850; padding: 0.8rem 5.5rem; border-radius: 5px; font-size: 1.15rem; color: #DD7850; background-color: white;">进&nbsp;入</div>
20 + </div>
21 +</template>
22 +
23 +<script setup>
24 +import { ref } from 'vue'
25 +import { useRoute, useRouter } from 'vue-router'
26 +
27 +//import { } from '@/utils/generateModules.js'
28 +//import { } from '@/utils/generateIcons.js'
29 +//import { } from '@/composables'
30 +const $route = useRoute();
31 +const $router = useRouter();
32 +
33 +const goTo = () => {
34 + $router.push({
35 + path: './map',
36 + query: {
37 + id: $route.query.id
38 + }
39 + });
40 + // 进入标记
41 + localStorage.setItem('first_in_bieyuan', 1);
42 +}
43 +
44 +onMounted(() => {
45 + // 记录第一次进入页面,之后判断是否第一次进入,直接跳转到MAP页面
46 + if (localStorage.getItem('first_in_bieyuan') === '1') {
47 + $router.push({
48 + path: './map',
49 + query: {
50 + id: $route.query.id
51 + }
52 + });
53 + }
54 +});
55 +</script>
56 +
57 +<style lang="less" scoped>
58 + .index-page {
59 + height: 100vh;
60 + display: flex;
61 + flex-direction: column;
62 + justify-content: space-evenly;
63 + align-items: center;
64 + background: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5)),
65 + url('https://cdn.ipadbiz.cn/bieyuan/map/MAP@3x.png');
66 + background-size: contain;
67 + }
68 +</style>
1 +<!--
2 + * @Date: 2024-09-15 22:08:49
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-02-10 16:11:47
5 + * @FilePath: /map-demo/src/views/by/info.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <div class="info-page">
10 + <div class="info-header-wrapper">
11 + <div v-if="showBack && page_details.banner?.length" style="position: absolute; top: 1rem; left: 0.5rem; z-index: 9;">
12 + <van-icon name="arrow-left" color="white" size="1.75rem" @click="goBack()" />
13 + </div>
14 + <van-config-provider :theme-vars="themeVars">
15 + <van-swipe class="my-swipe" indicator-color="#DD7850" lazy-render :autoplay="5000">
16 + <van-swipe-item v-for="(image, index) in page_details.banner" :key="index" style="position: relative;">
17 + <van-image fit="cover" width="100%" :height="img_height" :src="image" @click="onClickImg(index)" />
18 + <img src="https://cdn.ipadbiz.cn/bieyuan/map/icon/pageShade@3x.png" style="width: 100%; height: 5rem; position: absolute; right: 0; left: 0; bottom: 0;" alt="">
19 + </van-swipe-item>
20 + </van-swipe>
21 + </van-config-provider>
22 + <div class="header-z"></div>
23 + </div>
24 + <div class="info-content-wrapper">
25 + <div class="info-header">
26 + <div style="display: flex; justify-content: space-between;">
27 + <p class="info-title">{{ page_details.name }}</p>
28 + <div style="display: flex;">
29 + <div @click="onClickAudioList" style="margin-right: 0.75rem;">
30 + <van-icon v-if="!audio_list_height" name="https://cdn.ipadbiz.cn/bieyuan/map/icon/%E8%AF%AD%E9%9F%B31@3x.png" size="1.65rem" />
31 + <van-icon v-else name="https://cdn.ipadbiz.cn/bieyuan/map/icon/%E8%AF%AD%E9%9F%B32@3x.png" size="1.65rem" />
32 + </div>
33 + <div v-if="page_details.path?.length > 1" @click="goTo()" class="info-btn">前往</div>
34 + </div>
35 + </div>
36 + <div class="info-sub-title">{{ page_details.note }}</div>
37 + </div>
38 + <div id="tab-wrapper" style="margin-top: 0.5rem;">
39 + <van-config-provider :theme-vars="themeVars">
40 + <van-tabs ref="tabsRef" v-model:active="active" @click-tab="clickTab" color="#DD7850" title-active-color="#DD7850" title-inactive-color="#DD7850" :shrink="show_shrink" sticky animated>
41 + <van-tab title="介 绍" v-if="page_details.introduction">
42 + <div class="info-content">
43 + <div id="introduction" v-html="page_details.introduction" style="padding: 0 1rem;"></div>
44 + </div>
45 + </van-tab>
46 + <van-tab title="故 事" v-if="page_details.story">
47 + <div class="info-content">
48 + <div id="story" v-html="page_details.story" style="padding: 0 1rem;"></div>
49 + </div>
50 + </van-tab>
51 + <van-tab title="体 验" v-if="page_details.experience">
52 + <div class="info-content">
53 + <div id="experience" v-html="page_details.experience" style="padding: 0 1rem;"></div>
54 + </div>
55 + <div v-if="page_details.experience_audio.length" class="audio-wrapper">
56 + <div @click="toggleHandleAudio(item, index)" :class="['audio-item', play_audio_index === index ? 'click' : '']" v-for="(item, index) in page_details.experience_audio" :key="index">
57 + <div>{{ item.description }}</div>
58 + <van-icon @click.stop="stopAudio(item, index)" v-if="item.play" size="2rem" name="stop-circle-o" color="#DD7850" />
59 + <van-icon v-else @click="playAudio(item, index)" size="2rem" name="https://cdn.ipadbiz.cn/bieyuan/map/icon/audio_icon.png" />
60 + </div>
61 + </div>
62 + </van-tab>
63 + </van-tabs>
64 + </van-config-provider>
65 + </div>
66 + </div>
67 + <!-- <div class="info-logo" :style="{ marginBottom: audio_list_height ? `${audio_list_height * 1.5}px` : '3rem' }">
68 + <van-image width="3rem" height="3rem" fit="contain" src="https://cdn.ipadbiz.cn/bieyuan/map/icon/scan_logo.png" />
69 + </div> -->
70 +
71 + <van-toast v-model:show="show_toast" style="padding: 0">
72 + <template #message>
73 + <p style="padding: 0.5rem 1rem;">{{ toast_text }}</p>
74 + </template>
75 + </van-toast>
76 +
77 + <van-image-preview v-model:show="show_preview" :images="preview_images" @change="onChange" doubleScale>
78 + <template v-slot:index>第{{ index + 1 }}张</template>
79 + </van-image-preview>
80 +
81 + <van-back-top />
82 +
83 + <audio-play-list :height="audio_list_height" :status="audio_status" @close="onCloseAudioList" @status="onStatusAudioList"></audio-play-list>
84 + </div>
85 +</template>
86 +
87 +<script setup>
88 +import { ref, watch, watchEffect } from 'vue'
89 +import { useRoute, useRouter } from 'vue-router'
90 +import { showImagePreview } from 'vant';
91 +import { storeToRefs } from 'pinia'
92 +import audioPlayList from '@/components/audioList.vue'
93 +import { mainStore, useTitle } from '@/utils/generatePackage'
94 +import wx from 'weixin-js-sdk';
95 +import $ from 'jquery';
96 +
97 +import { mapAPI } from '@/api/map.js'
98 +
99 +const store = mainStore();
100 +const { audio_status, audio_entity, audio_list_status, audio_list_entity } = storeToRefs(store);
101 +
102 +const $route = useRoute();
103 +const $router = useRouter();
104 +
105 +const themeVars = ref({
106 + swipeIndicatorInactiveBackground: '#fff',
107 + swipeIndicatorMargin: '1.5rem',
108 + tabFontSize: '0.95rem',
109 +});
110 +
111 +const props = defineProps({
112 + info: Object,
113 + height: Number
114 +});
115 +
116 +const page_details = ref({});
117 +
118 +watch(
119 + () => props.info,
120 + (v) => {
121 + if (v.details.length) {
122 + page_details.value = { ...v.details[0], position: v.position, path: v.path };
123 + // 获取浏览器可视范围的高度
124 + $('.info-page').height(props.height + 'px');
125 + }
126 + }
127 +)
128 +
129 +const images = ref([
130 + 'https://cdn.ipadbiz.cn/bieyuan/map/swiper_img.png',
131 + 'https://cdn.ipadbiz.cn/bieyuan/map/Mix_20230612_201951.png',
132 + 'https://cdn.ipadbiz.cn/bieyuan/map/Mix_20240815_211927.png',
133 +]);
134 +
135 +const active = ref(0);
136 +const play_audio_index = ref(null);
137 +
138 +// const audioList = ref([{
139 +// text: '5分钟观呼吸',
140 +// src: 'https://cdn.ipadbiz.cn/bieyuan/map/audio/%E6%AD%A3%E5%BF%B5%E5%91%BC%E5%90%B8%EF%BC%8810%E5%88%86%E9%92%9F%EF%BC%89.mp3',
141 +// play: false,
142 +// }, {
143 +// text: '10分钟正念静坐',
144 +// src: 'https://cdn.ipadbiz.cn/bieyuan/map/audio/%E5%8D%81%E5%88%86%E9%92%9F%E6%AD%A3%E5%BF%B5%E9%9D%99%E5%9D%9020210510.mp3',
145 +// play: false,
146 +// }, {
147 +// text: '15分钟正念静坐',
148 +// src: 'https://cdn.ipadbiz.cn/bieyuan/map/audio/%E5%8D%81%E5%88%86%E9%92%9F%E6%AD%A3%E5%BF%B5%E9%9D%99%E5%9D%9020210510.mp3',
149 +// play: false,
150 +// }])
151 +const toggleHandleAudio = (item, index) => { // 切换播放或者暂停操作
152 + if (item.play) {
153 + stopAudio(item, index);
154 + } else {
155 + playAudio(item, index);
156 + }
157 +}
158 +
159 +const playAudio = (item, index) => {
160 + page_details.value.experience_audio.forEach(item => item.play = false);
161 + audio.value.src = item.src;
162 + // 后台有播放器运行时,先暂停
163 + if (audio_list_status.value === 'play'){
164 + audio_list_entity.value.pause();
165 + }
166 + play_audio_index.value = index;
167 + let play_status = audio.value.play() // 播放
168 + if (play_status) {
169 + play_status.then(() => {
170 + item.play = true;
171 + // 存放到pinia里面控制
172 + store.changeAudio(audio.value);
173 + store.changeAudioSrc(audio.value.src);
174 + store.changeAudioStatus('play');
175 + }).catch((e) => {
176 + // 失败
177 + console.log('Operation is too fast, audio play fails')
178 + })
179 + }
180 +}
181 +
182 +const stopAudio = (item, index) => {
183 + item.play = false;
184 + audio.value.pause();
185 +}
186 +
187 +const audio = ref(new Audio());
188 +const img_height = ref('15rem');
189 +const scrollTop = ref(0);
190 +
191 +onMounted(async () => {
192 + // 通过ID查询到标记点详情
193 + if (!props.info) {
194 + let id = $route.query.id;
195 + const { data } = await mapAPI({ i: id });
196 + const raw_list = data.list[0].list; // 获取标记点列表
197 + const marker_id = $route.query.marker_id;
198 + const current_marker = raw_list.filter(item => item.id == marker_id)[0];
199 + //
200 + page_details.value = { ...current_marker.details[0], position: current_marker.position, path: current_marker.path };
201 + // 富文本转义, 分割线样式转换
202 + page_details.value.introduction = page_details.value.introduction?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
203 + page_details.value.story = page_details.value.story?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
204 + page_details.value.experience = page_details.value.experience?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
205 + }
206 + // 介绍栏目,图片点击事件
207 + var imgs = $('#introduction').find('img');
208 + // 图片点击事件
209 + imgs.each(function(index, img) {
210 + $(img).on('click', function (e) {
211 + showImagePreview({
212 + images: [$(img).attr('src')],
213 + startPosition: 0,
214 + showIndex: false,
215 + onClose: () => {
216 + // console.log('close');
217 + }
218 + })
219 + });
220 + // 图片有2个像素的圆角
221 + $(img).css('border-radius', '5px');
222 + });
223 + // 获取屏幕宽度,设置高度4:3
224 + // 获取屏幕的宽度
225 + var screenWidth = $(window).width();
226 + // 计算4:3比例的高度
227 + var screenHeight = screenWidth * 3 / 4;
228 + // 设置容器的高度
229 + img_height.value = screenHeight + 'px';
230 + //
231 + nextTick(() => {
232 + $('.info-page').on('scroll', (evt) => {
233 + scrollTop.value = $(evt.currentTarget).scrollTop(); // 获取滚动的垂直距离
234 + })
235 + })
236 + // 如果是从浮动窗口点击进来音频播放,自动打开
237 + if ($route.query.source === 'click_audio') {
238 + setTimeout(() => {
239 + audio_list_height.value = (0.2 * window.innerHeight);
240 + // 修改当前路由参数,避免刷新再次播放
241 + $router.push({
242 + query: {
243 + ...$route.query,
244 + source: ''
245 + }
246 + });
247 + }, 500);
248 + }
249 + // 地图标题
250 + document.title = page_details.value.name;
251 + // 微信分享
252 + const shareData = {
253 + title: page_details.value.name, // 分享标题
254 + desc: '别院详情', // 分享描述
255 + link: location.origin + location.pathname + location.hash, // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
256 + imgUrl: '', // 分享图标
257 + success: function () {
258 + console.warn('设置成功');
259 + }
260 + }
261 + // 分享好友(微信好友或qq好友)
262 + wx.updateAppMessageShareData(shareData);
263 + // 分享到朋友圈或qq空间
264 + wx.updateTimelineShareData(shareData);
265 + // 分享到腾讯微博
266 + wx.onMenuShareWeibo(shareData);
267 +});
268 +
269 +// watchEffect(
270 +// () => useTitle(page_details.value.name) // 地图标题
271 +// )
272 +
273 +onUnmounted(() => { // 离开页面时关闭音频播放
274 + audio.value.pause();
275 + store.changeAudioStatus('pause');
276 +})
277 +
278 +const audio_play = (src, index) => {
279 + audio.value.src = src;
280 +}
281 +
282 +const outerStopAudio = () => {
283 + audio.value.pause();
284 +}
285 +
286 +const emit = defineEmits(["closeFloat", 'route']);
287 +
288 +const show_toast = ref(false);
289 +const toast_text = ref('');
290 +
291 +const goTo = () => { // 打开标记地图显示
292 + // 没有关联导航提示
293 + if (page_details.value.path.length <= 1) {
294 + show_toast.value = true;
295 + toast_text.value = '该标记点没有关联导航';
296 + return;
297 + }
298 + //
299 + if ($router.currentRoute.value.path === '/by/info') { // 详情页
300 + $router.push({
301 + path: '/by',
302 + query: {
303 + id: $route.query.id,
304 + marker_id: $route.query.marker_id
305 + }
306 + })
307 + } else { // 地图页
308 + //
309 + emit("closeFloat", false);
310 + //
311 + emit("route", {name: '参观路径', path: page_details.value.path});
312 + }
313 +}
314 +
315 +const goBack = () => { // 返回首页
316 + $router.push({
317 + path: '/by',
318 + query: {
319 + id: $route.query.id,
320 + }
321 + })
322 +}
323 +
324 +const showBack = computed(() => $router.currentRoute.value.path === '/by/info');
325 +
326 +const voicePause = () => {
327 + audio.value.pause();
328 + store.changeAudioStatus('pause');
329 +}
330 +
331 +const tabsRef = ref(null);
332 +const clickTab = (evt) => { // 标签切换
333 + tabsRef.value.resize();
334 + nextTick(() => {
335 + if (evt.title === '介 绍') { // 介绍
336 + var imgs = $('#introduction').find('img');
337 + }
338 + if (evt.title === '故 事') { // 故事
339 + var imgs = $('#story').find('img');
340 + }
341 + if (evt.title === '体 验') { // 体验
342 + var imgs = $('#experience').find('img');
343 + }
344 + // 图片点击事件
345 + imgs.each(function(index, img) {
346 + $(img).on('click', function (e) {
347 + showImagePreview({
348 + images: [$(img).attr('src')],
349 + startPosition: 0,
350 + showIndex: false,
351 + onClose: () => {
352 + // console.log('close');
353 + }
354 + })
355 + })
356 + // 图片有5个像素的圆角
357 + $(img).css('border-radius', '5px');
358 + });
359 + // 滚动高度大于tabs高度后才滚动到指定高度
360 + let offsetTop = $('#tab-wrapper')[0].offsetTop;
361 + if (scrollTop.value >= offsetTop) {
362 + $('.info-page').scrollTop(offsetTop);
363 + }
364 + });
365 +}
366 +
367 +watch(
368 + () => audio_status.value,
369 + (v) => {
370 + if (v === 'pause') {
371 + voicePause();
372 + page_details.value.experience_audio?.forEach(item => item.play = false);
373 + }
374 + },
375 + { immediate: true }
376 +);
377 +
378 +defineExpose({
379 + outerStopAudio
380 +})
381 +
382 +
383 +const show_preview = ref(false);
384 +const index = ref(0);
385 +const preview_images = [];
386 +const onChange = (newIndex) => {
387 + index.value = newIndex;
388 +};
389 +
390 +const onClickImg = (idx) => {
391 + showImagePreview({
392 + images: page_details.value.banner,
393 + startPosition: idx,
394 + showIndex: true,
395 + onClose: () => {
396 + // console.log('close');
397 + }
398 + })
399 +};
400 +
401 +const show_shrink = computed(() => {
402 + // 统计非空字段的个数
403 + let filledFields = 0;
404 +
405 + if (page_details.value.introduction) filledFields++;
406 + if (page_details.value.story) filledFields++;
407 + if (page_details.value.experience) filledFields++;
408 +
409 + // 判断是否只有一个字段有值
410 + if (filledFields === 1) {
411 + return true;
412 + }
413 + return false;
414 +});
415 +
416 +const audio_list_height = ref(0);
417 +
418 +const onClickAudioList = () => {
419 + if ($('.info-page').height() < $(window).height()) { // 在浮动模式下点击音频列表
420 + // 打开页面
421 + $router.push({
422 + path: '/by/info',
423 + query: {
424 + id: $route.query.id,
425 + marker_id: props.info.id,
426 + source: 'click_audio'
427 + }
428 + });
429 + } else { // 详情页内点击
430 + audio_list_height.value = (0.2 * window.innerHeight);
431 + }
432 +}
433 +
434 +const onCloseAudioList = () => {
435 + audio_list_height.value = 0;
436 +}
437 +
438 +// const show_audio = ref(true);
439 +
440 +const onStatusAudioList = (status) => { // 音频列表组件,状态改变
441 + page_details.value.experience_audio?.forEach(item => item.play = false);
442 + audio.value.pause();
443 + play_audio_index.value = null;
444 + store.changeAudioStatus('pause');
445 + // // 反馈播放列表数量为空时,隐藏图标
446 + // if (status === 'none') {
447 + // show_audio.value = false;
448 + // }
449 +}
450 +</script>
451 +
452 +<style lang="less">
453 +.info-page {
454 + background-color: #EBEBEB;
455 + height: 100vh;
456 + overflow: scroll;
457 + position: relative;
458 + .info-header-wrapper {
459 + position: relative;
460 + min-height: 2rem;
461 + .header-z {
462 + position: absolute;
463 + bottom: 0;
464 + left: 0;
465 + right: 0;
466 + height: 1rem;
467 + // box-shadow: rgba(241, 242, 248, 0.6) 0px -3px 25px 15px;
468 + // background-color: #f7f7f7;
469 + background-color: #fff;
470 + margin: 0 1rem;
471 + border-top-left-radius: 0.5rem;
472 + border-top-right-radius: 0.5rem;
473 + }
474 + }
475 + .info-content-wrapper {
476 + box-shadow: 0px -3px 6px 0 rgba(241, 242, 248, 0.8);
477 + margin: 1rem;
478 + margin-top: 0;
479 + // padding: 1rem;
480 + border-bottom-left-radius: 0.5rem;
481 + border-bottom-right-radius: 0.5rem;
482 + background-color: white;
483 + .info-header {
484 + padding: 1rem 2rem 0;
485 + // display: flex;
486 + // justify-content: space-between;
487 + // align-items: center;
488 + .info-title {
489 + font-size: 1.25rem;
490 + margin-bottom: 0.5rem;
491 + }
492 + .info-sub-title {
493 + font-size: 0.85rem;
494 + color: #A0A8B1;
495 + line-height: 1.75;
496 + }
497 + .info-btn {
498 + width: 3rem;
499 + height: 1.5rem;
500 + border: 1px solid #DD7850;
501 + color: #DD7850;
502 + border-radius: 0.8rem;
503 + font-size: 0.85rem;
504 + text-align: center;
505 + line-height: 1.5rem;
506 + }
507 + }
508 + .info-content {
509 + color: #47525F;
510 + padding: 1rem;
511 + line-height: 1.75;
512 + p {
513 + line-height: 1.75;
514 + // padding: 0 0.85rem;
515 + text-align: justify;
516 + img {
517 + width: 100%;
518 + }
519 + }
520 + }
521 + .audio-wrapper {
522 + padding: 1rem;
523 + .audio-item {
524 + color: #47525F;
525 + display: flex;
526 + justify-content: space-between;
527 + align-items: center;
528 + padding: 1rem;
529 + background-color: #FFF;
530 + border-radius: 0.25rem;
531 + margin: 1rem;
532 + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1);
533 + &.click {
534 + border: 1px solid #DD7850;
535 + }
536 + .audio-icon {
537 + width: 2rem;
538 + height: 2rem;
539 + background-image: url('https://cdn.ipadbiz.cn/bieyuan/map/icon/audio_icon.png'); /* 使用上传的图标 */
540 + background-size: cover;
541 + &.click {
542 + animation: pulse 1.5s infinite;
543 + }
544 + }
545 +
546 + @keyframes pulse {
547 + 0% {
548 + transform: scale(1);
549 + }
550 + 50% {
551 + transform: scale(1.2);
552 + }
553 + 100% {
554 + transform: scale(1);
555 + }
556 + }
557 + }
558 + }
559 + }
560 + .info-logo {
561 + display: flex;
562 + justify-content: center;
563 + margin: 3rem;
564 + }
565 +
566 + .van-tabs__wrap {
567 + border-bottom: 1px solid #F3F3F3;
568 + }
569 +
570 + .van-tabs__nav--line.van-tabs__nav--shrink {
571 + padding-left: 2rem;
572 + }
573 +}
574 +
575 +.van-back-top {
576 + background-color: #DD7850;
577 +}
578 +</style>
1 +<!--
2 + * @Date: 2024-09-15 22:08:49
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-02-10 16:11:41
5 + * @FilePath: /map-demo/src/views/by/info_w.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <div class="info-page">
10 + <div>
11 + <van-config-provider :theme-vars="themeVars">
12 + <van-swipe class="my-swipe" indicator-color="#DD7850" lazy-render :autoplay="5000">
13 + <van-swipe-item v-for="image in images" :key="image">
14 + <van-image fit="cover" width="100%" height="13rem" :src="image" />
15 + </van-swipe-item>
16 + </van-swipe>
17 + </van-config-provider>
18 + </div>
19 + <div class="info-content-wrapper">
20 + <div class="info-header">
21 + <div>
22 + <p class="info-title">选佛场</p>
23 + <p class="info-sub-title">南楼2层</p>
24 + </div>
25 + <div @click="goTo()" class="info-btn">前往</div>
26 + </div>
27 + <div class="van-hairline--bottom">
28 + <van-tabs v-model:active="active" color="#DD7850" title-active-color="#DD7850" sticky>
29 + <van-tab title="介 绍">
30 + <div class="info-content">
31 + <p style="line-height: 1.75; padding: 0 0.85rem; color: #47525F;">选佛场是一个宗教活动场所,集禅堂与讲堂功能于一体。其设计仿制古代石窟样式,把古代人修行的场所“石窟”搬进室内。禅堂内的释迦牟尼佛像,仿麦积山石窟第44号特窟“东方的微笑”的造型。禅堂设计自然古朴,匠心独运,星光、烛光、月光三光辉映,营造出“一个人不孤单,一千人不喧闹”的宁静祥和的氛围。禅堂可容纳千人,开展讲经、禅修、法会等多种活动,提供礼佛、供灯、静坐等体验。</p>
32 + <div class="van-hairline--bottom" style="margin: 1rem 0;"></div>
33 + <div style="padding: 1rem;">
34 + <div style="color: #DD7850;">•&nbsp;五方塔</div>
35 + <div style="color: #47525F; margin-top: 1rem; line-height: 1.75;">禅堂外的草坪,安立着大小不一五座佛塔。信众可绕塔或悬挂风铃祝愿祈福,在肃静庄严的氛围中得到佛力加持,自净其意,心想事成。</div>
36 + </div>
37 + </div>
38 + </van-tab>
39 + <van-tab title="故 事">
40 + <div style="padding: 0 1rem;">
41 + <div style="padding: 1rem;">
42 + <div style="color: #DD7850;">•&nbsp;选官何如选佛</div>
43 + </div>
44 + <div style="padding: 0 1rem;">
45 + <van-image width="100%" height="11rem" fit="cover" src="https://cdn.ipadbiz.cn/bieyuan/map/Mix_20230612_201951.png" />
46 + </div>
47 + <div style="padding: 1rem;">
48 + <p style="color: #47525F; line-height: 1.75;">过去把禅堂叫作选佛场,意思是选择作佛的场所。这个典故与丹霞禅师有关。 <br />他原本是一个秀才,赴京赶考的途中遇到一位禅师,这位禅师跟他讲,选官何如选佛?考官还不如成佛利益更大。世间功名如过眼云烟,即便追求得到也是暂时利益,执著于此就会烦恼重重,甚至不断造业。而学佛修行,考佛就是要成佛,成就生命永恒的福祉。这是永久的利益,尽未来际的利益。每个生命原本具备觉悟的潜质,具有无尽的功德宝藏,取之不尽用之不竭。成佛,可以断除一切迷惑烦恼,可以彻底地开发我们生命的潜质,全然觉醒,这个利益无量无边。丹霞禅师深具慧根,一经点拨,马上出家。</p>
49 + </div>
50 + <div class="van-hairline--bottom" style="margin: 0 1rem;"></div>
51 + </div>
52 + <div style="padding: 0 1rem;">
53 + <div style="padding: 1rem;">
54 + <div style="color: #DD7850;">•&nbsp;把洞窟搬进讲堂</div>
55 + </div>
56 + <div style="padding: 0 1rem;">
57 + <van-image width="100%" height="11rem" fit="cover" src="https://cdn.ipadbiz.cn/bieyuan/map/Mix_20240815_211927.png" />
58 + </div>
59 + <div style="padding: 1rem;">
60 + <p style="color: #47525F; line-height: 1.75;">洞窟,是传统的佛教建筑形式,最早在印度盛行,古代僧人喜欢在崇山峻岭的幽僻处开凿洞窟,遁世修行。选佛场集禅堂与讲堂的功能于一体,把洞窟搬进讲堂,既有回归佛教本怀的宁静温暖,又体现出融入泰宁岩穴文化的祥和之气。</p>
61 + </div>
62 + <div class="van-hairline--bottom" style="margin: 0 1rem;"></div>
63 + </div>
64 + </van-tab>
65 + <van-tab title="体 验">
66 + <div style="padding: 0 1rem;">
67 + <div style="padding: 1rem;">
68 + <div style="color: #DD7850;">•&nbsp;供灯</div>
69 + <div style="color: #47525F; margin-top: 1rem; line-height: 1.75;">禅堂内自助供灯。</div>
70 + </div>
71 + <div style="padding: 0 1rem;">
72 + <van-image width="100%" height="11rem" fit="cover" src="https://cdn.ipadbiz.cn/bieyuan/map/Mix_20240815_211927.png" />
73 + </div>
74 + <div class="van-hairline--bottom" style="margin: 0 1rem;"></div>
75 + </div>
76 + <div class="audio-wrapper">
77 + <div :class="['audio-item', play_audio_index === index ? 'click' : '']" v-for="(item, index) in audioList" :key="index">
78 + <div>{{ item.text }}</div>
79 + <!-- <div :class="['audio-icon', play_audio_index === index ? 'click' : '']"></div> -->
80 + <van-icon @click="stopAudio(item, index)" v-if="item.play" size="2rem" name="stop-circle-o" color="#DD7850" />
81 + <van-icon v-else @click="playAudio(item, index)" size="2rem" name="https://cdn.ipadbiz.cn/bieyuan/map/icon/audio_icon.png" />
82 + </div>
83 + </div>
84 + <div style="padding: 0 1rem;">
85 + <img src="https://cdn.ipadbiz.cn/bieyuan/map/xcx.png" style="width: 100%;">
86 + </div>
87 + </van-tab>
88 + </van-tabs>
89 + </div>
90 + </div>
91 + <!-- <div style="display: flex; justify-content: center; margin: 3rem;">
92 + <van-image
93 + width="3rem"
94 + height="3rem"
95 + fit="contain"
96 + src="https://cdn.ipadbiz.cn/bieyuan/map/icon/scan_logo.png"
97 + />
98 + </div> -->
99 + </div>
100 +</template>
101 +
102 +<script setup>
103 +import { ref, watch } from 'vue'
104 +import { useRoute, useRouter } from 'vue-router'
105 +
106 +import { storeToRefs } from 'pinia'
107 +import { mainStore } from '@/store';
108 +
109 +const store = mainStore();
110 +const { audio_status, audio_entity } = storeToRefs(store);
111 +
112 +const $route = useRoute();
113 +const $router = useRouter();
114 +
115 +const themeVars = ref({
116 + swipeIndicatorInactiveBackground: '#fff',
117 +});
118 +
119 +const images = ref([
120 + 'https://cdn.ipadbiz.cn/bieyuan/map/swiper_img.png',
121 + 'https://cdn.ipadbiz.cn/bieyuan/map/Mix_20230612_201951.png',
122 + 'https://cdn.ipadbiz.cn/bieyuan/map/Mix_20240815_211927.png',
123 +]);
124 +
125 +const active = ref(0);
126 +const play_audio_index = ref(null);
127 +
128 +const audioList = ref([{
129 + text: '5分钟观呼吸',
130 + src: 'https://cdn.ipadbiz.cn/bieyuan/map/audio/%E6%AD%A3%E5%BF%B5%E5%91%BC%E5%90%B8%EF%BC%8810%E5%88%86%E9%92%9F%EF%BC%89.mp3',
131 + play: false,
132 +}, {
133 + text: '10分钟正念静坐',
134 + src: 'https://cdn.ipadbiz.cn/bieyuan/map/audio/%E5%8D%81%E5%88%86%E9%92%9F%E6%AD%A3%E5%BF%B5%E9%9D%99%E5%9D%9020210510.mp3',
135 + play: false,
136 +}, {
137 + text: '15分钟正念静坐',
138 + src: 'https://cdn.ipadbiz.cn/bieyuan/map/audio/%E5%8D%81%E5%88%86%E9%92%9F%E6%AD%A3%E5%BF%B5%E9%9D%99%E5%9D%9020210510.mp3',
139 + play: false,
140 +}])
141 +
142 +const playAudio = (item, index) => {
143 + audioList.value.forEach(item => item.play = false);
144 + audio.value.src = item.src;
145 + play_audio_index.value = index;
146 + let play_status = audio.value.play() // 播放
147 + if (play_status) {
148 + console.warn('start');
149 + // if (audio_status.value === 'play') {
150 + // audio_entity.value.pause();
151 + // }
152 + play_status.then(() => {
153 + console.warn('success');
154 + item.play = true;
155 + // 存放到pinia里面控制
156 + // store.changeAudioSrc(audio.value.src);
157 + // store.changeAudioStatus('play');
158 + }).catch((e) => {
159 + // 失败
160 + console.log('Operation is too fast, audio play fails')
161 + })
162 + }
163 +}
164 +
165 +const stopAudio = (item, index) => {
166 + item.play = false;
167 + audio.value.pause();
168 +}
169 +
170 +const audio = ref(new Audio());
171 +
172 +onMounted(() => {
173 + // 存放到pinia里面控制
174 + store.changeAudio(audio.value);
175 + // store.changeAudioStatus('pause');
176 +})
177 +
178 +onUnmounted(() => {
179 + audio.value.pause();
180 + store.changeAudioStatus('pause');
181 +})
182 +
183 +const audio_play = (src, index) => {
184 + audio.value.src = src;
185 +}
186 +
187 +const outerStopAudio = () => {
188 + audio.value.pause();
189 +}
190 +
191 +const emit = defineEmits(["closeFloat", 'route']);
192 +const goTo = () => { // 打开标记地图显示
193 + if ($router.currentRoute.value.path === '/by/info') { // 详情页
194 + $router.push({
195 + path: '/by',
196 + query: {
197 + id: $route.query.id,
198 + marker_id: '12345'
199 + }
200 + })
201 + } else { // 地图页
202 + //
203 + emit("closeFloat", false);
204 + //
205 + emit("route", 'marker_id');
206 + }
207 +}
208 +
209 +const voicePause = () => {
210 + audio.value.pause();
211 + store.changeAudioStatus('pause');
212 +}
213 +
214 +watch(
215 + () => audio_status.value,
216 + (v) => {
217 + if (v === 'pause') {
218 + voicePause();
219 + audioList.value.forEach(item => item.play = false);
220 + }
221 + },
222 + { immediate: true }
223 +);
224 +
225 +defineExpose({
226 + outerStopAudio
227 +})
228 +</script>
229 +
230 +<style lang="less">
231 +.info-page {
232 + background-color: #EBEBEB;
233 + height: 100vh;
234 + overflow: scroll;
235 + position: relative;
236 + .info-content-wrapper {
237 + // position: absolute;
238 + // top: 14.9rem;
239 + margin: 1rem;
240 + margin-top: 0;
241 + // padding: 1rem;
242 + border-radius: 0.5rem;
243 + background-color: white;
244 + .info-header {
245 + padding: 1rem 2rem 0;
246 + display: flex;
247 + justify-content: space-between;
248 + // align-items: center;
249 + .info-title {
250 + font-size: 1.25rem;
251 + margin-bottom: 0.5rem;
252 + }
253 + .info-sub-title {
254 + font-size: 0.85rem;
255 + color: #A0A8B1;
256 + }
257 + .info-btn {
258 + width: 3rem;
259 + height: 1.5rem;
260 + border: 1px solid #DD7850;
261 + color: #DD7850;
262 + border-radius: 0.8rem;
263 + font-size: 0.85rem;
264 + text-align: center;
265 + line-height: 1.5rem;
266 + }
267 + }
268 + .info-content {
269 + padding: 1rem;
270 + }
271 + .audio-wrapper {
272 + padding: 1rem;
273 + .audio-item {
274 + color: #47525F;
275 + display: flex;
276 + justify-content: space-between;
277 + align-items: center;
278 + padding: 1rem;
279 + background-color: #FFF;
280 + border-radius: 0.25rem;
281 + margin: 1rem;
282 + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1);
283 + &.click {
284 + border: 1px solid #DD7850;
285 + }
286 + .audio-icon {
287 + width: 2rem;
288 + height: 2rem;
289 + background-image: url('https://cdn.ipadbiz.cn/bieyuan/map/icon/audio_icon.png'); /* 使用上传的图标 */
290 + background-size: cover;
291 + &.click {
292 + animation: pulse 1.5s infinite;
293 + }
294 + }
295 +
296 + @keyframes pulse {
297 + 0% {
298 + transform: scale(1);
299 + }
300 + 50% {
301 + transform: scale(1.2);
302 + }
303 + 100% {
304 + transform: scale(1);
305 + }
306 + }
307 + }
308 + }
309 + }
310 +}
311 +</style>
1 +<!--
2 + * @Date: 2023-05-19 14:54:27
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-02-10 16:15:37
5 + * @FilePath: /map-demo/src/views/by/map.vue
6 + * @Description: 公众地图主体页面
7 +-->
8 +<template>
9 + <div ref="root" style="height: 100vh; position: relative; overflow: hidden;">
10 + <div id="container"></div>
11 + <div style="position: absolute; top: 2rem; right: 1rem; display: flex; flex-direction: column;">
12 + <!-- <van-icon size="2rem" name="search" color="#DD7850" style="margin-bottom: 1rem;" /> -->
13 + <van-image
14 + width="2rem"
15 + height="2rem"
16 + fit="contain"
17 + src="https://cdn.ipadbiz.cn/bieyuan/map/icon/NAV@3x.png"
18 + />
19 + </div>
20 + <div v-if="data_logo" style="position: absolute; top: 2rem; left: calc(50% - 1.5rem); opacity: 0.5;">
21 + <van-image
22 + width="3rem"
23 + height="3rem"
24 + fit="contain"
25 + :src="data_logo"
26 + />
27 + </div>
28 + <div @click="scanQrcode" style="position: absolute; bottom: 1rem; left: calc(50% - 2.5rem);">
29 + <van-image
30 + width="5rem"
31 + height="5rem"
32 + fit="contain"
33 + src="https://cdn.ipadbiz.cn/bieyuan/map/icon/scan@3x.png"
34 + />
35 + </div>
36 +
37 + <van-config-provider :theme-vars="themeVars">
38 + <van-floating-panel v-model:height="info_height" :anchors="anchors" @height-change="onHeightChange">
39 + <!-- <template #header>
40 + <div class="custom-header">
41 + <h3>自定义标题</h3>
42 + <button @click="show = false">关闭</button>
43 + </div>
44 + </template> -->
45 + <page-info ref="pageInfo" :info="itemInfo" :height="info_height" @close-float="onCloseFloat" @route="onRoute"></page-info>
46 + <!-- <div v-if="showClose" @click="closeFloatPanel" class="close-float-panel">
47 + <van-icon name="arrow-left" color="#FFF" size="1.5rem" />
48 + </div> -->
49 + </van-floating-panel>
50 + </van-config-provider>
51 +
52 + <div v-if="!show_walk_route" @click="removeSafeRoute({ name: '参观路径' })" class="walk-nav-text">
53 + 关闭步行导航
54 + </div>
55 +
56 + <van-dialog v-model:show="dialog_show" title="温馨提示">
57 + <div style="padding: 1rem; text-align: center;">{{ dialog_text }}</div>
58 + </van-dialog>
59 +
60 + <!-- 背景音乐控制 -->
61 + <!-- <audioBackground1></audioBackground1> -->
62 +
63 + <div class="operate-bar-wrapper">
64 + <div class="box-wrapper">
65 + <div v-if="open_current_location" class="item" @click="handleLocation(true)">
66 + <van-icon name="https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8Dloc@2x.png" size="1.5rem"
67 + style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);;" />
68 + </div>
69 + <div v-else class="item" @click="handleLocation(false)">
70 + <van-icon name="https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8Dloc@2x.png" size="1.5rem"
71 + style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
72 + </div>
73 + </div>
74 + </div>
75 +
76 + <van-toast v-model:show="show_toast" style="padding: 0">
77 + <template #message>
78 + <p style="padding: 0.5rem 1rem;">{{ toast_text }}</p>
79 + </template>
80 + </van-toast>
81 + </div>
82 +</template>
83 +
84 +<script>
85 +import "@vant/touch-emulator";
86 +// import { mapState } from 'vuex'
87 +import coord from '@/common/map_data'
88 +import my_router from '@/common/my_router'
89 +import _ from 'lodash';
90 +import $ from 'jquery';
91 +import { useRect } from '@vant/use';
92 +import { mapAPI } from '@/api/map.js'
93 +import wx from 'weixin-js-sdk'
94 +import pageInfo from '@/views/by/info.vue'
95 +import audioBackground1 from '@/components/audioBackground1.vue'
96 +import { mapState, mapActions } from 'pinia'
97 +import { mainStore } from '@/store'
98 +import { parseQueryString } from '@/utils/tools'
99 +
100 +const GPS = {
101 + PI: 3.14159265358979324,
102 + x_pi: 3.14159265358979324 * 3000.0 / 180.0,
103 + delta: function (lat, lon) {
104 + var a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
105 + var ee = 0.00669342162296594323; // ee: 椭球的偏心率。
106 + var dLat = this.transformLat(lon - 105.0, lat - 35.0);
107 + var dLon = this.transformLon(lon - 105.0, lat - 35.0);
108 + var radLat = lat / 180.0 * this.PI;
109 + var magic = Math.sin(radLat);
110 + magic = 1 - ee * magic * magic;
111 + var sqrtMagic = Math.sqrt(magic);
112 + dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * this.PI);
113 + dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * this.PI);
114 + return {
115 + 'lat': dLat,
116 + 'lon': dLon
117 + };
118 + },
119 + //WGS-84 to GCJ-02
120 + gcj_encrypt: function (wgsLat, wgsLon) {
121 + if (this.outOfChina(wgsLat, wgsLon))
122 + return {
123 + 'lat': wgsLat,
124 + 'lon': wgsLon
125 + };
126 +
127 + var d = this.delta(wgsLat, wgsLon);
128 + return {
129 + 'lat': wgsLat + d.lat,
130 + 'lon': wgsLon + d.lon
131 + };
132 + },
133 + outOfChina: function (lat, lon) {
134 + if (lon < 72.004 || lon > 137.8347)
135 + return true;
136 + if (lat < 0.8293 || lat > 55.8271)
137 + return true;
138 + return false;
139 + },
140 + transformLat: function (x, y) {
141 + var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
142 + ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
143 + ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0;
144 + ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0;
145 + return ret;
146 + },
147 + transformLon: function (x, y) {
148 + var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
149 + ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
150 + ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0;
151 + ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0;
152 + return ret;
153 + }
154 +};
155 +
156 +export default {
157 + components: { pageInfo, audioBackground1 },
158 + data() {
159 + return {
160 + map: '',
161 + geolocation: '',
162 + current_lng: '',
163 + current_lat: '',
164 + dialog_show: false,
165 + dialog_text: '',
166 + location_marker: '',
167 + itemInfo: {},
168 + navBarList: [],
169 + navList: [],
170 + navKey: '',
171 + markerSum: [], // marker合集
172 + mapTiles: [],
173 + data_center: [], // 接口获取-地图中心点
174 + data_zoom: '', // 接口获取-地图默认缩放
175 + data_zooms: '', // 接口获取-地图默认缩放范围
176 + data_rotation: 0, // 接口获取-地图旋转角度
177 + data_paths: {}, // 接口获取-地图导航路径
178 + data_path_list: [], // 接口获取-地图导航路径
179 + info_height: 0,
180 + anchors: [0, (0.65 * window.innerHeight), (1 * window.innerHeight)],
181 + themeVars: {
182 + floatingPanelHeaderHeight: 0,
183 + floatingPanelBorderRadius: '1.25rem'
184 + },
185 + showClose: false,
186 + markerStyle2: { // 选中
187 + //设置文本样式,Object 同 css 样式表
188 + "padding": ".5rem .2rem .5rem .2rem",
189 + // "margin-bottom": "1rem",
190 + "border-color": "#DD7850",
191 + "border-radius": ".25rem",
192 + "background-color": "#FFF",
193 + // "width": "1rem",
194 + // "border-width": 0,
195 + // "box-shadow": "0 2px 6px 0 rgba(114, 124, 245, .5)",
196 + // "text-align": "center",
197 + "font-size": "0.8rem",
198 + "color": "#DD7850",
199 + "writing-mode": "vertical-rl",
200 + "text-orientation": "mixed",
201 + "display": "flex",
202 + "justify-content": "center",
203 + "align-items": "center",
204 + },
205 + markerStyle1: { // 未选中
206 + //设置文本样式,Object 同 css 样式表
207 + "padding": ".5rem .2rem .5rem .2rem",
208 + // "margin-bottom": "1rem",
209 + "border-color": "#fcfbfa",
210 + "border-radius": ".25rem",
211 + "background-color": "#DD7850",
212 + // "width": "1rem",
213 + // "border-width": 0,
214 + // "box-shadow": "0 2px 6px 0 rgba(114, 124, 245, .5)",
215 + // "text-align": "center",
216 + "font-size": "0.8rem",
217 + "color": "white",
218 + "writing-mode": "vertical-rl",
219 + "text-orientation": "mixed",
220 + "display": "flex",
221 + "justify-content": "center",
222 + "align-items": "center",
223 + },
224 + current_safe_route: [],
225 + route_safe_marker: [],
226 + show_walk_route: true,
227 + open_current_location: true,
228 + show_toast: false,
229 + toast_text: '',
230 + data_logo: '',
231 + data_layers: [],
232 + }
233 + },
234 + async mounted() {
235 + const code = this.$route.query.id;
236 + const { data } = await mapAPI({ i: code });
237 + this.navBarList = data.list; // 底部导航条
238 + this.mapTiles = data.level; // 获取图层
239 + this.navKey = data.list[0]['id']; // 默认选中 第一个 id
240 + this.navList = data.list.filter(item => item.id === this.navKey)[0]['list']; // 返回默认选中项的实体信息
241 + this.data_center = data.map.center.map(item => Number(item)); // 地图中心点
242 + this.data_zoom = data.map.zoom; // 地图默认缩放
243 + this.data_rotation = data.map.rotation; // 地图旋转角度
244 + this.data_zooms = data.map.zooms.map(item => Number(item)); // 地图默认缩放范围
245 + this.data_paths = data.map.path ? data.map.path : {}; // 地图默认导航路径
246 + this.data_logo = data.map.map_logo ? data.map.map_logo : ''; // 地图logo
247 + if (data.map.map_layers) { // 地图默认图层
248 + if (data.map.map_layers === 'satellite') { // 卫星和路网
249 + this.data_layers = [new AMap.TileLayer.Satellite(), new AMap.TileLayer.RoadNet()]
250 + } else { // 平面图
251 + this.data_layers = [];
252 + }
253 + }
254 + if (data.map.path) {
255 + for (const key in data.map.path) {
256 + const element = data.map.path[key];
257 + this.data_path_list.push({
258 + name: key,
259 + path: element,
260 + status: true
261 + })
262 + }
263 + }
264 + // 地图标题
265 + document.title = data.map.map_title;
266 + // 微信分享
267 + const shareData = {
268 + title: data.map.map_title, // 分享标题
269 + desc: '别院地图', // 分享描述
270 + link: location.origin + location.pathname + location.hash, // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
271 + imgUrl: '', // 分享图标
272 + success: function () {
273 + console.warn('设置成功');
274 + }
275 + }
276 + // 分享好友(微信好友或qq好友)
277 + wx.updateAppMessageShareData(shareData);
278 + // 分享到朋友圈或qq空间
279 + wx.updateTimelineShareData(shareData);
280 + // 分享到腾讯微博
281 + wx.onMenuShareWeibo(shareData);
282 + // 初始化地图
283 + this.initMap();
284 + // this.setMapBoundary();
285 + // 使用之前获取当前地址,判断当前是否能够获取经纬度
286 + wx.getLocation({
287 + type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
288 + success: (res) => {
289 + var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
290 + var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
291 + var speed = res.speed; // 速度,以米/每秒计
292 + var accuracy = res.accuracy; // 位置精度
293 + this.current_lng = GPS.gcj_encrypt(latitude, longitude).lon;
294 + this.current_lat = GPS.gcj_encrypt(latitude, longitude).lat;
295 + },
296 + });
297 + // 设置贴片地图
298 + this.setTitleLayer();
299 + //
300 + // setTimeout(() => {
301 + // this.info_height = (0.5 * window.innerHeight);
302 + // // 浮动面板样式
303 + // $('.van-floating-panel__content').css('borderRadius', '1.5rem');
304 + // }, 2000);
305 + },
306 + watch: {
307 + // // 监听 $route 对象的 query 属性
308 + // '$route.query': {
309 + // handler(newQuery, oldQuery) {
310 + // if (newQuery.marker_id) {
311 +
312 + // }
313 + // },
314 + // immediate: true, // 设置为 true,确保在初始化时也执行一次 handler
315 + // deep: true // 如果 query 是嵌套对象,可以设置 deep 监听深层变化
316 + // }
317 + },
318 + computed: {
319 + ...mapState(mainStore, ['audio_entity', 'audio_src', 'audio_status'])
320 + },
321 + methods: {
322 + ...mapActions(mainStore, ['changeAudio', 'changeAudioSrc', 'changeAudioStatus']),
323 + initMap() {
324 + // 初始化地图
325 + this.map = new AMap.Map('container', {
326 + viewMode: '2D', // 设置地图模式
327 + turboMode: false,
328 + showIndoorMap: false,
329 + defaultCursor: 'pointer', // 地图默认鼠标样式
330 + showBuildingBlock: false, // 是否展示地图 3D 楼块
331 + zooms: this.data_zooms, // 地图显示的缩放级别范围, 默认为 [2, 20] ,取值范围 [2 ~ 30]
332 + showLabel: true, // 是否展示地图文字和 POI 信息
333 + zoom: this.data_zoom, // 设置地图显示的缩放级别
334 + pitch: 0, // 俯仰角度,默认 0,最大值根据地图当前 zoom 级别不断增大,2D地图下无效 。
335 + rotation: this.data_rotation, // 地图顺时针旋转角度,取值范围 [0-360] ,默认值:0
336 + center: this.data_center, // 设置地图中心点坐标
337 + forceVector: false,
338 + // rotateEnable: true,
339 + layers: this.data_layers,
340 + features: ['bg', 'road'], // 设置地图上显示的元素种类
341 + animateEnable: false, // 地图平移过程中是否使用动画
342 + resizeEnable: true,
343 + });
344 + // 添加地图点击事件
345 + this.map.on("click", this.showInfoClick);
346 + // 加载景点图层
347 + this.loadMaker(this.navKey);
348 + //
349 + this.map.setRotation(this.data_rotation, true);
350 + },
351 + loadMaker(id) {
352 + var zoomStyleMapping = { 14: 0, 15: 0, 16: 0, 17: 0, 18: 0, 19: 0, 20: 0 };
353 + const entity_info = this.navBarList.filter(item => item.id === id)[0]['list'];
354 + this.markerSum = [];
355 + _.each(entity_info, (x, i) => {
356 + let marker_icon = '';
357 + if (entity_info[i].window_type === 'warn' && entity_info[i].details.length === 1) { // 如果是预警类型并且内部预警项目只有一个取details第一个icon
358 + marker_icon = entity_info[i].details[0]['icon'];
359 + } else {
360 + marker_icon = entity_info[i].icon;
361 + }
362 + if (entity_info[i]?.writing_mode === 'vertical') { // 标题文字垂直
363 + let textMarker = new AMap.Text({
364 + zooms: [18, 20], // 点标记显示的层级范围,超过范围不显示。
365 + text: entity_info[i].name, //标记显示的文本内容
366 + anchor: "center", //设置文本标记锚点位置
367 + // draggable: true, //是否可拖拽
368 + // cursor: "pointer", //指定鼠标悬停时的鼠标样式。
369 + // angle: 10, //点标记的旋转角度
370 + style: this.markerStyle1,
371 + position: entity_info[i].position, //点标记在地图上显示的位置
372 + });
373 + textMarker.setMap(this.map); //将文本标记设置到地图上
374 + this.markerSum.push(textMarker);
375 + if (clickListener1) {
376 + textMarker.off('click', clickListener)
377 + }
378 + // 绑定景点的点击事件 - 文字出现才能触发
379 + var clickListener1 = textMarker.on('click', (e) => {
380 + // 还原样式
381 + this.markerSum.forEach(item => {
382 + if (e.target.hS !== item.hS) {
383 + // 修改文本的样式
384 + item.setStyle(this.markerStyle2);
385 + }
386 + })
387 + // 修改文本的样式
388 + e.target.setStyle(this.markerStyle1);
389 +
390 + // 修改文本内容
391 + // textMarker.setText('样式已修改');
392 + //
393 +
394 + // console.warn(e);
395 + this.itemInfo = entity_info[i];
396 +
397 + // 详情为空提示
398 + if (!this.itemInfo.details.length) {
399 + this.show_toast = true;
400 + this.toast_text = '该景点暂无详情'
401 + return;
402 + }
403 +
404 +
405 + // 打开浮动面板
406 + this.info_height = (0.65 * window.innerHeight);
407 + // 浮动面板样式
408 + $('.van-floating-panel__content').css('borderRadius', '1.25rem');
409 + $('.van-floating-panel').css('boxShadow', '0 0 15px black');
410 +
411 + // 定位到当前位置中心
412 + this.map.setZoomAndCenter(this.zoom, this.itemInfo.position);
413 + // 获取地图容器的高度
414 + const mapHeight = this.map.getSize().height;
415 +
416 + // 计算需要向上移动的像素值,比如向上移动地图高度的一半左右
417 + const offsetY = -mapHeight / 3.5;
418 +
419 + // 使用 panBy 方法进行视图偏移
420 + this.map.panBy(0, offsetY);
421 + })
422 + }
423 + // TODO: 获取详情定位信息用来导航
424 + // 导航路径
425 + let marker_id = this.$route.query.marker_id;
426 + if (marker_id) {
427 + this.$nextTick(() => {
428 + let marker = this.navBarList[0]['list'].filter(item => item.id == marker_id)
429 + let path = marker[0].path;
430 + this.addSafeRoute({name: '参观路径', path});
431 + // 获取当前 URL 的查询参数
432 + let query = { ...this.$route.query };
433 +
434 + // 删除 marker_id 参数
435 + delete query.marker_id;
436 +
437 + // 使用 Vue Router 更新 URL,并且不刷新页面
438 + this.$router.replace({ query });
439 + });
440 + }
441 + });
442 + this.map.add(this.markerSum);
443 + //
444 + // setTimeout(() => {
445 + // // 获取定位打标记
446 + // this.setLocation();
447 + // }, 1000);
448 + },
449 + isPointInRing() { // 是否在景区范围
450 + let isPointInRing = AMap.GeometryUtil.isPointInRing([this.current_lng, this.current_lat], [
451 + [117.044223,26.835105], [117.044227,26.842448], [117.0552,26.842452], [117.055195,26.8351]
452 + ]);
453 + return isPointInRing
454 + },
455 + setLocation() { // 开启定位服务
456 + // 获取失败
457 + if (!this.current_lng || !this.current_lat) {
458 + this.dialog_show = true;
459 + this.dialog_text = '获取经纬度失败';
460 + }
461 + this.getLocation();
462 + },
463 + getLocation() { // 获取经纬度
464 + // PC端无法获取定位
465 + // 微信获取地址
466 + wx.getLocation({
467 + type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
468 + success: (res) => {
469 + var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
470 + var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
471 + var speed = res.speed; // 速度,以米/每秒计
472 + var accuracy = res.accuracy; // 位置精度
473 + this.current_lng = GPS.gcj_encrypt(latitude, longitude).lon;
474 + this.current_lat = GPS.gcj_encrypt(latitude, longitude).lat;
475 + // 判断是否在范围内
476 + if (!this.isPointInRing()) {
477 + this.dialog_show = true;
478 + this.dialog_text = '您不在景区范围内';
479 + } else {
480 + // 使用纠正偏移后的地址,打一个定位标记
481 + this.location_marker = new AMap.LabelMarker({
482 + icon: {
483 + image: 'https://cdn.ipadbiz.cn/bieyuan/map/icon/Group%2034@3x.png',
484 + anchor: 'bottom-center',
485 + size: [65, 65],
486 + },
487 + position: new AMap.LngLat(this.current_lng, this.current_lat), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
488 + });
489 + this.map.add(this.location_marker);
490 + // 定位到当前位置中心
491 + this.map.setZoomAndCenter(this.zoom, [this.current_lng, this.current_lat]);
492 + }
493 + },
494 + complete: () => {
495 + // 获取失败
496 + if (!this.current_lng || !this.current_lat) {
497 + this.dialog_show = true;
498 + this.dialog_text = '获取经纬度失败';
499 + }
500 + },
501 + });
502 + },
503 + setZoom(type) { // 设置放大缩小地图
504 + const zoom = this.map.getZoom();
505 + if (type === 'plus') {
506 + this.map.setZoom(zoom + 1)
507 + }
508 + if (type === 'minus') {
509 + this.map.setZoom(zoom - 1)
510 + }
511 + },
512 + computedMapSource(x, y, z) { // 根据图层信息生成图层实际地址
513 + for (const id in this.mapTiles) {
514 + if (z == id) {
515 + const scope = this.mapTiles[id];
516 + return scope[`${x}-${y}`]
517 + }
518 + }
519 + },
520 + setTitleLayer() { // 生成瓦片图
521 + // 获取瓦片图渲染范围
522 + function getFirstProperty(obj) {
523 + for (var prop in obj) {
524 + return prop;
525 + }
526 + }
527 + function getLastProperty(obj) {
528 + var props = [];
529 + for (var prop in obj) {
530 + props.push(prop);
531 + }
532 + return props[props.length - 1];
533 + }
534 + let obj_scope = {};
535 + for (const key in this.mapTiles) {
536 + const element = this.mapTiles[key];
537 + let first = getFirstProperty(element).split('-');
538 + let last = getLastProperty(element).split('-');
539 + obj_scope[key] = {
540 + x: [first[0], last[0]],
541 + y: [first[1], last[1]]
542 + }
543 + }
544 + const _this = this;
545 + var layer = new AMap.TileLayer.Flexible({
546 + cacheSize: 50,
547 + opacity: 1,
548 + zIndex: 100,
549 + createTile: function (x, y, z, success, fail) {
550 + // 控制地图等级显示图片范围-过滤不显示的图层渲染
551 + for (const id in obj_scope) {
552 + if (z == id) {
553 + const scope = obj_scope[id];
554 + if (x < scope.x[0] || x > scope.x[1]) {
555 + fail()
556 + return;
557 + }
558 + if (y < scope.y[0] || y > scope.y[1]) {
559 + fail()
560 + return;
561 + }
562 + }
563 + }
564 +
565 + var img = document.createElement('img');
566 + img.onload = function () {
567 + success(img)
568 + };
569 + img.crossOrigin = "anonymous";// 必须添加,同时图片要有跨域头
570 + img.onerror = function () {
571 + fail()
572 + };
573 +
574 + // img.src = `images/tiles/${z}/${x}_${y}.png`;
575 + img.src = _this.computedMapSource(x, y, z);
576 + },
577 + });
578 +
579 + this.map.addLayer(layer);
580 +
581 + // Canvas作为切片
582 + var layer1 = new AMap.TileLayer.Flexible({
583 + // tileSize: 128,
584 + cacheSize: 300,
585 + zIndex: 200,
586 + createTile: function (x, y, z, success, fail) {
587 + var c = document.createElement('canvas');
588 + c.width = c.height = 256;
589 +
590 + var cxt = c.getContext("2d");
591 + cxt.font = "15px Verdana";
592 + cxt.fillStyle = "#ff0000";
593 + cxt.strokeStyle = "#FF0000";
594 + cxt.strokeRect(0, 0, 256, 256);
595 + cxt.fillText('(' + [x, y, z].join(',') + ')', 10, 30);
596 +
597 + // 通知API切片创建完成
598 + success(c);
599 + }
600 + });
601 +
602 + // layer1.setMap(this.map);
603 +
604 + // 只显示相应区域,移动会回到选定范围
605 + // this.lockMapBounds()
606 + },
607 + // 限制地图范围
608 + lockMapBounds() {
609 + // var bounds = this.map.getBounds();
610 + var myBounds = new AMap.Bounds( // 移动范围,对角线
611 + [117.04384,26.833629],
612 + [117.055975,26.843652]
613 + );
614 +
615 + this.map.setLimitBounds(myBounds);
616 +
617 + let list =[ // 四个角,覆盖填充范围
618 + [117.04421,26.833875],
619 + [117.045012,26.842089],
620 + [117.054749,26.84219],
621 + [117.056013,26.83387]
622 + ]
623 +
624 + // 隐藏边界以外的区域
625 + let outer = [
626 + new AMap.LngLat(-360, 90, true),
627 + new AMap.LngLat(-360, -90, true),
628 + new AMap.LngLat(360, -90, true),
629 + new AMap.LngLat(360, 90, true),
630 + ] // 遮盖填充反向
631 +
632 + let pathArray = [
633 + outer,
634 + list
635 + ]
636 +
637 + var polygon = new AMap.Polygon({
638 + pathL: pathArray,
639 + strokeColor: "#fcfbf9",
640 + strokeWeight: 2,
641 + fillColor: "#fcfbf9",
642 + fillOpacity: 1,
643 + })
644 +
645 + polygon.setPath(pathArray)
646 + this.map.add(polygon)
647 +
648 + },
649 + showInfoClick(e) {
650 + // console.log(e);
651 + var zoom = this.map.getZoom(); //获取当前地图级别
652 + // var text =
653 + // "您在 [" +
654 + // e.lnglat.getLng() +
655 + // "," +
656 + // e.lnglat.getLat() +
657 + // "] 的位置单击了地图!当前层级" +
658 + // zoom;
659 + var text =
660 + "[" +
661 + e.lnglat.getLng() +
662 + "," +
663 + e.lnglat.getLat() +
664 + "],"
665 + console.log(text);
666 + /*// 关闭浮动面板
667 + this.info_height = 0;
668 + $('.van-floating-panel').css('boxShadow', 'none');
669 + // 还原样式
670 + this.resetMarkStyle();*/
671 + },
672 + scanQrcode() { // 扫码跳转详情页
673 + wx.scanQRCode({
674 + needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
675 + scanType: ["qrCode","barCode"], // 可以指定扫二维码还是一维码,默认二者都有
676 + success: (res) => {
677 + var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
678 + let id = parseQueryString(result).id;
679 + let marker_id = parseQueryString(result).marker_id;
680 + // 跳转详情页
681 + this.$router.push({
682 + path: '/by/info',
683 + query: {
684 + id,
685 + marker_id
686 + }
687 + })
688 + }
689 + });
690 + // 识别率太低
691 + // this.$router.push({
692 + // path: '/by/scan'
693 + // })
694 + },
695 + onHeightChange ({ height }) { // 监听浮动面板高度变化
696 + if (height > window.innerHeight * 0.6) {
697 + // // 浮动面板样式
698 + // $('.van-floating-panel__content').css('borderRadius', '0');
699 + // this.showClose = true;
700 + // 清空设置
701 + // this.changeAudio('');
702 + // this.changeAudioStatus('pause');
703 + //
704 + this.$router.push({
705 + path: '/by/info',
706 + query: {
707 + id: this.$route.query.id,
708 + marker_id: this.itemInfo.id
709 + }
710 + })
711 + } else {
712 + $('.van-floating-panel__content').css('borderRadius', '1.25rem');
713 + $('.van-floating-panel').css('boxShadow', 'none');
714 + this.showClose = false;
715 + }
716 + },
717 + closeFloatPanel () {
718 + this.info_height = (0.65 * window.innerHeight);
719 + $('.van-floating-panel__content').css('borderRadius', '1.25rem');
720 + this.showClose = false;
721 + // 关闭音频
722 + this.$refs.pageInfo.outerStopAudio();
723 + },
724 + resetMarkStyle () {
725 + this.markerSum.forEach(item => {
726 + item.setStyle(this.markerStyle1);
727 + })
728 + },
729 + onCloseFloat () {
730 + this.info_height = 0;
731 + $('.van-floating-panel__content').css('borderRadius', '1.25rem');
732 + $('.van-floating-panel').css('boxShadow', 'none');
733 + this.resetMarkStyle();
734 + },
735 + addSafeRoute({name, path}) { // 新增路径
736 + // 获取对象的第一个键和值
737 + // let firstKey = Object.keys(this.data_paths)[0];
738 + // let firstValue = this.data_paths[firstKey];
739 + // 行动路线
740 + // var path = [
741 + // [120.587645, 31.314833],
742 + // [120.587709, 31.314338],
743 + // [120.588211, 31.314377],
744 + // ];
745 + // console.warn(firstValue);
746 + // var path = firstValue;
747 + // 生成折线地图路径
748 + let current_safe_route = new AMap.Polyline({
749 + path,
750 + isOutline: true,
751 + outlineColor: '#179FB1',
752 + borderWeight: 1,
753 + strokeColor: '#179FB1',
754 + strokeOpacity: 1,
755 + strokeWeight: 3,
756 + // 折线样式还支持 'dashed'
757 + strokeStyle: 'solid',
758 + // strokeStyle是dashed时有效
759 + strokeDasharray: [10, 5],
760 + lineJoin: 'round',
761 + lineCap: 'round',
762 + zIndex: 50
763 + })
764 + this.map.add([current_safe_route]);
765 + this.current_safe_route.push({
766 + key: name,
767 + path: current_safe_route
768 + })
769 + // 设置起始点标记
770 + var marker1 = new AMap.Marker({
771 + icon: new AMap.Icon({
772 + image: 'https://cdn.ipadbiz.cn/bieyuan/map/icon/Ellipse%2013@3x.png',
773 + size: new AMap.Size(15, 15),
774 + // 图标所用图片大小
775 + imageSize: new AMap.Size(15, 15),
776 + // 图标取图偏移量
777 + imageOffset: new AMap.Pixel(0, 0)
778 + }),
779 + position: new AMap.LngLat(path[0][0], path[0][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
780 + anchor: 'bottom-center',
781 + offset: new AMap.Pixel(0, 0)
782 + });
783 + // marker1.setLabel({
784 + // direction: 'right',
785 + // offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
786 + // content: "<div class='info'>起点</div>", //设置文本标注内容
787 + // });
788 + var marker2 = new AMap.Marker({
789 + icon: new AMap.Icon({
790 + image: 'https://cdn.ipadbiz.cn/bieyuan/map/icon/Ellipse%2013@3x.png',
791 + size: new AMap.Size(15, 15),
792 + // 图标所用图片大小
793 + imageSize: new AMap.Size(15, 15),
794 + // 图标取图偏移量
795 + imageOffset: new AMap.Pixel(0, 0)
796 + }),
797 + position: new AMap.LngLat(path[path.length - 1][0], path[path.length - 1][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
798 + anchor: 'bottom-center',
799 + offset: new AMap.Pixel(0, 0)
800 + });
801 + // marker2.setLabel({
802 + // direction: 'right',
803 + // offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
804 + // content: "<div class='info'>终点</div>", //设置文本标注内容
805 + // });
806 + // 新增逃生路线标记
807 + // this.route_safe_marker = [marker1, marker2]
808 + // this.map.add(this.route_safe_marker);
809 + // 新增逃生路线标记
810 + let route_safe_marker = [marker1, marker2]
811 + this.map.add(route_safe_marker);
812 + this.route_safe_marker.push({
813 + key: name,
814 + path: route_safe_marker
815 + });
816 + // 关闭导航提示
817 + this.show_walk_route = false;
818 + },
819 + removeSafeRoute({name}) { // 移除地图路线
820 + this.current_safe_route.forEach(item => {
821 + if (item.key === name) {
822 + this.map.remove([item.path]); // 删除地图折线
823 + }
824 + });
825 + // this.map.remove(this.route_safe_marker); // 删除起始点标记
826 + this.route_safe_marker.forEach(item => {
827 + if (item.key === name) {
828 + this.map.remove(item.path); // 删除起始点标记
829 + }
830 + });
831 + // 关闭导航提示
832 + this.show_walk_route = true;
833 + },
834 + onRoute (path) {
835 + console.warn(path);
836 + // 模拟新增路线
837 + this.addSafeRoute(path);
838 + // 定位到当前位置中心
839 + this.map.setZoomAndCenter(this.zoom, this.data_center);
840 + },
841 + handleLocation(status) { // 打开/关闭 当前定位
842 + if (status) {
843 + this.setLocation()
844 + this.open_current_location = false;
845 + } else {
846 + this.removeLocation()
847 + this.open_current_location = true;
848 + }
849 + },
850 + removeLocation() { // 移除定位标记
851 + this.current_lng = '';
852 + this.current_lat = '';
853 + this.map.remove(this.location_marker); // 删除当前定位标记
854 + },
855 + }
856 +}
857 +</script>
858 +
859 +<style lang="less">
860 +#container {
861 + position: absolute;
862 + top: 0;
863 + left: 0;
864 + right: 0;
865 + bottom: 0;
866 + width: 100%;
867 + height: 100%;
868 +}
869 +
870 +// 遮挡地图logo
871 +.amap-logo {
872 + display: none!important;
873 + visibility: hidden!important;
874 +}
875 +
876 +.amap-copyright {
877 + display: none!important;
878 + visibility: hidden!important;
879 +}
880 +
881 +/* 标记文字样式 */
882 +.amap-marker-label {
883 + padding: 0.25rem 0.5rem;
884 + width: auto;
885 + border: none;
886 + border-radius: 2px;
887 + background: rgba(86, 65, 23, 0.8);
888 + color: white;
889 +}
890 +
891 +.amap-marker {
892 + .amap-icon {
893 + margin-top: 0.25rem;
894 + }
895 +}
896 +
897 +.input-card {
898 + display: flex;
899 + flex-direction: column;
900 + min-width: 0;
901 + word-wrap: break-word;
902 + background-color: #fff;
903 + background-clip: border-box;
904 + border-radius: .25rem;
905 + width: 20rem;
906 + border-width: 0;
907 + border-radius: 0.4rem;
908 + box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
909 + position: fixed;
910 + top: 4rem;
911 + right: 1rem;
912 + -ms-flex: 1 1 auto;
913 + flex: 1 1 auto;
914 + padding: 0.75rem 1.25rem;
915 +}
916 +
917 +.tool-bar-wrapper {
918 + position: absolute;
919 + left: 20px;
920 + bottom: 8rem;
921 + width: 20px;
922 +}
923 +
924 +.nav-bar-wrapper {
925 + position: fixed;
926 + bottom: 0;
927 + left: 0;
928 + height: 5.5rem;
929 + width: 100%;
930 + background-color: white;
931 + text-align: center;
932 + box-shadow: 0 -1px 0 rgba(80, 80, 80, 0.1);
933 + z-index: 999;
934 + // padding: 0.5rem 0;
935 + padding-bottom: 0.5rem;
936 +
937 + .nav-bar-content {
938 + display: flex;
939 + overflow-x: scroll;
940 + overflow-y: hidden;
941 + -webkit-overflow-scrolling: touch;
942 + position: relative;
943 + }
944 +
945 + .item {
946 + padding-top: 0.5rem;
947 + color: #888;
948 + width: 21.5%;
949 + flex-shrink: 0;
950 + padding-top: 1rem;
951 + }
952 +
953 + .checked {
954 + color: #965f13;
955 + }
956 +}
957 +
958 +.safe-route-wrapper {
959 + position: absolute;
960 + bottom: 2rem;
961 + right: 1rem;
962 + background-color: white;
963 +}
964 +
965 +.operate-bar-wrapper {
966 + position: fixed;
967 + left: 20px;
968 + bottom: 6rem;
969 + width: 20px;
970 + height: auto;
971 + z-index: 100;
972 +
973 + .box-wrapper {
974 + display: flex;
975 + flex-direction: column;
976 + align-items: center;
977 + justify-content: center;
978 +
979 + .item {
980 + position: relative;
981 + text-align: center;
982 + font-size: 0.85rem;
983 + width: 2rem;
984 + height: 2rem;
985 + background-color: white;
986 + margin-bottom: 1rem;
987 + border-radius: 50%;
988 + padding: 2.5px;
989 + line-height: 2rem;
990 + }
991 + }
992 +}
993 +
994 +.popup-wrapper {
995 + margin-top: 1rem;
996 +
997 + .title {
998 + font-size: 1.25rem;
999 + margin-bottom: 0.85rem;
1000 + }
1001 +
1002 + .content {
1003 + line-height: 1.75;
1004 + font-size: 0.95rem;
1005 + }
1006 +}
1007 +
1008 +
1009 +.hideScrollBar::-webkit-scrollbar {
1010 + display: none;
1011 +}
1012 +
1013 +.hideScrollBar {
1014 + -ms-overflow-style: none;
1015 + overflow: -moz-scrollbars-none;
1016 +}
1017 +
1018 +.van-dialog__confirm,
1019 +.van-dialog__confirm:active {
1020 + color: #AB8F57;
1021 +}
1022 +
1023 +.walk-nav-text {
1024 + position: fixed;
1025 + bottom: 6rem;
1026 + left: 50%;
1027 + transform: translate(-50%, -50%);
1028 + z-index: 9;
1029 + background: rgba(86, 65, 23, 0.8);
1030 + color: white;
1031 + border-radius: 10px;
1032 + padding: 5px 12px;
1033 + font-size: 0.8rem;
1034 +}
1035 +
1036 +.close-float-panel {
1037 + position: absolute;
1038 + top: 1rem;
1039 + left: 1rem;
1040 +}
1041 +
1042 +.custom-header {
1043 + display: flex;
1044 + justify-content: space-between;
1045 + align-items: center;
1046 + padding: 10px;
1047 + background-color: #f7f8fa;
1048 +}
1049 +
1050 +.van-floating-panel__header-bar {
1051 + background: none;
1052 +}
1053 +</style>
1 +<!--
2 + * @Date: 2024-09-15 11:45:13
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2024-09-18 11:15:56
5 + * @FilePath: /map-demo/src/views/bieyuan/scan.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <div class="scan-page">
10 + <!--<div class="scan_wrapper">
11 + <div class="scan_box">
12 + <!~~ 镜头区域 ~~>
13 + <video ref="video" id="video" class="scan-video" autoplay></video>
14 + </div>
15 + <div @click="openScan" class="scan_text">点击扫描二维码查看详情</div>
16 + </div>
17 + <div class="sys_logo">
18 + <van-image
19 + width="3rem"
20 + height="3rem"
21 + fit="contain"
22 + src="https://cdn.ipadbiz.cn/bieyuan/map/icon/scan_logo.png"
23 + />
24 + </div>-->
25 +
26 + <video ref="video" id="video" class="video vjs-fluid" autoplay></video>
27 + <div v-show="tipShow" class="tip">{{tipMsg}}</div>
28 + </div>
29 +</template>
30 +
31 +<script setup>
32 +import { ref } from "vue";
33 +import { useRoute, useRouter } from "vue-router";
34 +
35 +const $route = useRoute();
36 +const $router = useRouter();
37 +
38 +import { BrowserMultiFormatReader } from "@zxing/library";
39 +// import axios from 'axios'
40 +const codeReader = ref(null);
41 +
42 +const scanText = ref("");
43 +// 初始化相机
44 +const openScan = () => {
45 + codeReader.value = new BrowserMultiFormatReader();
46 + codeReader.value
47 + .getVideoInputDevices()
48 + .then((videoDevices) => {
49 + let firstDeviceId = videoDevices[videoDevices.length - 1].deviceId;
50 + if (videoDevices.length > 1) {
51 + // 一般通过判断摄像头列表项里的 label 字段,'camera2 0, facing back' 字符串含有 'back' 和 '0',大部分机型是这样,如果有些机型没有,那就还是默认获取最后一个
52 + firstDeviceId = videoDevices.find((el) => {
53 + return el.label.indexOf("back") > -1 && el.label.indexOf("0") > -1;
54 + })
55 + ? videoDevices.find((el) => {
56 + return el.label.indexOf("back") > -1 && el.label.indexOf("0") > -1;
57 + }).deviceId
58 + : videoDevices[videoDevices.length - 1].deviceId;
59 + }
60 + decodeFromInputVideoFunc(firstDeviceId);
61 + })
62 + .catch((err) => {
63 + console.log(err);
64 + });
65 +};
66 +
67 +// 扫码
68 +const decodeFromInputVideoFunc = (firstDeviceId) => {
69 + // 使用摄像头扫描
70 + codeReader.value.reset(); // 重置
71 + codeReader.value.decodeFromInputVideoDeviceContinuously(
72 + firstDeviceId,
73 + "video",
74 + (result, err) => {
75 + if (result) {
76 + alert(result.text);
77 + console.log("扫码结果", result);
78 + scanText.value = result.text;
79 + if (scanText.value) {
80 + // 识别成功关闭摄像头
81 + codeReader.value.reset();
82 + codeReader.value.stopContinuousDecodeFromInputVideoDevice();
83 + }
84 + }
85 + }
86 + );
87 +};
88 +
89 +onMounted(() => {});
90 +</script>
91 +
92 +
93 +<style lang="less" scoped>
94 +.scan-page {
95 + position: relative;
96 + height: 100vh;
97 + .scan_wrapper {
98 + display: flex;
99 + padding: 1rem;
100 + flex-direction: column;
101 + align-items: center;
102 + padding-top: 5rem;
103 + .scan_box {
104 + padding: 1rem;
105 + background-image: url('https://cdn.ipadbiz.cn/bieyuan/map/icon/scan_bg.png');
106 + background-size: contain;
107 + background-repeat: no-repeat;
108 + background-position: center center;
109 + height: 20rem;
110 + width: 90%;
111 + position: relative;
112 + overflow: hidden; /* 确保溢出内容被隐藏 */
113 + box-sizing: border-box; /* 包括 padding 在内的尺寸计算 */
114 + .scan-video {
115 + object-fit: cover; /* 保持视频内容的比例,同时填满容器 */
116 + position: absolute; /* 确保视频覆盖整个容器 */
117 + top: -1rem;
118 + left: -1rem;
119 + width: calc(100% - 2rem); /* 视频宽度减去左右1rem的padding */
120 + height: calc(100% - 2rem); /* 视频高度减去上下1rem的padding */
121 + padding: 2rem;
122 + object-fit: cover; /* 保持视频比例,同时填充容器 */
123 + }
124 + }
125 + .scan_text {
126 + font-size: 0.9rem;
127 + color: #DD7850;
128 + padding: 0.5rem 1rem;
129 + margin: 1rem;
130 + border: 1px solid #DD7850;
131 + border-radius: 5px;
132 + }
133 + }
134 + .sys_logo {
135 + position: absolute;
136 + bottom: 3rem;
137 + left: calc(50% - 1.5rem);
138 + }
139 +}
140 +</style>
1 +<!--
2 + * @Date: 2023-05-19 14:54:27
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-02-10 15:55:58
5 + * @FilePath: /map-demo/src/views/xyx/index.vue
6 + * @Description: 公众地图主体页面
7 +-->
8 +<template>
9 + <div ref="root" style="height: 100vh; position: relative; overflow: hidden;">
10 + <div id="container"></div>
11 + <div class="nav-bar-wrapper">
12 + <div class="hideScrollBar nav-bar-content">
13 + <div v-for="(item, index) in navBarList" :key="index" :class="[isActive === index ? 'checked' : '', 'item']"
14 + @click="setNavLayer(item, index)">
15 + <van-icon :name="isActive === index ? item.icon[1] : item.icon[0]" size="2rem" /><br />
16 + <span style="font-size: 0.85rem; margin-top: 0rem; display: inline-block;">{{ item.name }}</span>
17 + </div>
18 + <div style="width: 4rem;flex-shrink: 0;"></div>
19 + <div style="position: fixed; right: 0; background-color: white; height: 5.5rem; width: 4rem;">
20 + <div style="padding-top: 40%;">
21 + <van-icon v-if="!show_nav_popup" name="arrow-up" @click="handleNavPopup" size="1.15rem" />
22 + <van-icon v-else name="arrow-down" @click="handleNavPopup" size="1.15rem" />
23 + </div>
24 + </div>
25 + </div>
26 +
27 + <van-popup v-model:show="show_nav_popup" position="bottom" duration="0" :overlay="false"
28 + :style="{ padding: '1rem', bottom: '6rem' }">
29 + <div style="text-align: left;">
30 + <div v-for="(item, index) in navList" :key="index" @click="handleNavMarker(item)"
31 + style="margin-bottom: 1rem; font-size: 1.15rem;">
32 + <van-icon name="fire-o" color="#965f13" />&nbsp;&nbsp;<span style="color: #000;">{{ item.name }}</span>
33 + </div>
34 + </div>
35 + </van-popup>
36 + </div>
37 + <div class="operate-bar-wrapper">
38 + <div class="box-wrapper">
39 + <div v-if="open_current_location" class="item" @click="handleLocation(true)">
40 + <van-icon name="https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8Dloc@2x.png" size="1.5rem"
41 + style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);;" />
42 + </div>
43 + <div v-else class="item" @click="handleLocation(false)">
44 + <van-icon name="https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8Dloc@2x.png" size="1.5rem"
45 + style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
46 + </div>
47 + <!-- <div class="item" @click="selectRoute">
48 + <van-icon name="https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8Dloc@2x.png" size="1.5rem"
49 + style="vertical-align: middle;" />
50 + </div> -->
51 + <div v-for="(item, index) in data_path_list" :key="index">
52 + <div v-if="item.status" class="item" @click="handleSafeRoute(true, item)">
53 + <van-icon name="https://cdn.ipadbiz.cn/xys/map/%E7%BA%BF%E8%B7%AF01.png" size="1.5rem"
54 + style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
55 + <span style="position: absolute; left: 40px; display: inline-block; width: 10rem; text-align: left;">{{ item.name }}</span>
56 + </div>
57 + <div v-else class="item" @click="handleSafeRoute(false, item)">
58 + <van-icon name="https://cdn.ipadbiz.cn/xys/map/%E7%BA%BF%E8%B7%AF02.png" size="1.5rem"
59 + style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
60 + <span style="position: absolute; left: 40px; display: inline-block; width: 10rem; text-align: left;">{{ item.name }}</span>
61 + </div>
62 + </div>
63 + </div>
64 + </div>
65 +
66 + <!-- <div v-if="!show_walk_route" @click="removeWalkRoute" class="walk-nav-text">
67 + 关闭步行导航
68 + </div> -->
69 + <div v-if="!show_walk_route" @click="removeNavRoute" class="walk-nav-text">
70 + 关闭步行导航
71 + </div>
72 +
73 + <!-- <van-popup v-model:show="show_popup" position="bottom" :overlay="true" teleport="body" :close-on-click-overlay="false" :style="{ padding: '1rem', height: '50%' }">
74 + <van-icon name="cross" size="1.35rem" @click="show_popup = false" style="float: right; color: gray;" />
75 + <div class="popup-wrapper">
76 + <div class="title">
77 + {{ popup_title }}
78 + </div>
79 + <div class="content" v-html="popup_content"></div>
80 + <video-player ref="videoPlayer" style="width: 100%; height: 30vh; margin-top: 1rem;"
81 + poster="https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100"
82 + :src="video_src" class="video-player vjs-big-play-centered" controls :loop="true" :volume="0.6"></video-player>
83 + </div>
84 + </van-popup> -->
85 +
86 + <van-dialog v-model:show="dialog_show" title="温馨提示">
87 + <div style="padding: 1rem; text-align: center;">{{ dialog_text }}</div>
88 + </van-dialog>
89 +
90 + <!-- 自定义组件InfoWindow,初始时需要隐藏 -->
91 + <!-- 隐藏不要使用v-if,因为我们需要渲染完成后的原生html结构作为信息框的dom对象使用 -->
92 + <InfoWindow v-show="showInfoWindow" ref="infoWindow" :info-window="infoWindow" :info="itemInfo" :rect="rect"
93 + @onLocation="infoWindowLocation" @onPlay="onPlay" @onPause="onPause"></InfoWindow>
94 + <InfoWindowLite v-show="showInfoWindowLite" ref="infoWindowLite" :info-window="infoWindowLite" :info="itemInfo"
95 + :rect="rect" @onLocation="infoWindowLocation"></InfoWindowLite>
96 + <InfoWindowWarn v-show="showInfoWindowWarn" ref="infoWindowWarn" :info-window="infoWindowWarn" :info="itemInfo"
97 + :rect="rect"></InfoWindowWarn>
98 +
99 + <audioBackground></audioBackground>
100 +
101 + <InfoPopup :show="showInfoPopup" :info-window="infoWindow" :info="itemInfo"
102 + @onLocation="infoWindowLocation" @onPlay="onPlay" @onPause="onPause" @onClose="onClose"></InfoPopup>
103 + <InfoPopupLite :show="showInfoLitePopup" :info="itemInfo" @onClose="onLiteClose"></InfoPopupLite>
104 + <InfoPopupWarn :show="showInfoWarnPopup" :info="itemInfo" @onClose="onWarnClose"></InfoPopupWarn>
105 + </div>
106 +</template>
107 +
108 +<script>
109 +// import { mapState } from 'vuex'
110 +import coord from '@/common/map_data'
111 +import map_max from '@/common/max'
112 +import map_alert from '@/common/alert'
113 +import my_router from '@/common/my_router'
114 +import _ from 'lodash';
115 +import $ from 'jquery';
116 +//引入定义的信息窗组件
117 +import InfoWindow from '@/components/InfoWindow'
118 +import InfoWindowLite from '@/components/InfoWindowLite'
119 +import InfoWindowWarn from '@/components/InfoWindowWarn'
120 +import audioBackground from '@/components/audioBackground'
121 +import { useRect } from '@vant/use';
122 +import { mapAPI } from '@/api/map.js'
123 +import wx from 'weixin-js-sdk'
124 +import InfoPopup from '@/components/InfoPopup'
125 +import InfoPopupLite from '@/components/InfoPopupLite'
126 +import InfoPopupWarn from '@/components/InfoPopupWarn'
127 +
128 +const GPS = {
129 + PI: 3.14159265358979324,
130 + x_pi: 3.14159265358979324 * 3000.0 / 180.0,
131 + delta: function (lat, lon) {
132 + var a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
133 + var ee = 0.00669342162296594323; // ee: 椭球的偏心率。
134 + var dLat = this.transformLat(lon - 105.0, lat - 35.0);
135 + var dLon = this.transformLon(lon - 105.0, lat - 35.0);
136 + var radLat = lat / 180.0 * this.PI;
137 + var magic = Math.sin(radLat);
138 + magic = 1 - ee * magic * magic;
139 + var sqrtMagic = Math.sqrt(magic);
140 + dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * this.PI);
141 + dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * this.PI);
142 + return {
143 + 'lat': dLat,
144 + 'lon': dLon
145 + };
146 + },
147 + //WGS-84 to GCJ-02
148 + gcj_encrypt: function (wgsLat, wgsLon) {
149 + if (this.outOfChina(wgsLat, wgsLon))
150 + return {
151 + 'lat': wgsLat,
152 + 'lon': wgsLon
153 + };
154 +
155 + var d = this.delta(wgsLat, wgsLon);
156 + return {
157 + 'lat': wgsLat + d.lat,
158 + 'lon': wgsLon + d.lon
159 + };
160 + },
161 + outOfChina: function (lat, lon) {
162 + if (lon < 72.004 || lon > 137.8347)
163 + return true;
164 + if (lat < 0.8293 || lat > 55.8271)
165 + return true;
166 + return false;
167 + },
168 + transformLat: function (x, y) {
169 + var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
170 + ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
171 + ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0;
172 + ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0;
173 + return ret;
174 + },
175 + transformLon: function (x, y) {
176 + var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
177 + ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
178 + ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0;
179 + ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0;
180 + return ret;
181 + }
182 +};
183 +
184 +export default {
185 + components: { InfoWindow, InfoWindowLite, InfoWindowWarn, audioBackground, InfoPopup, InfoPopupLite, InfoPopupWarn },
186 + data() {
187 + return {
188 + map: '',
189 + location_options: {
190 + 'showButton': true, // 是否显示定位按钮
191 + 'buttonPosition': 'LB', // 定位按钮的位置
192 + /* LT LB RT RB */
193 + 'buttonOffset': new AMap.Pixel(10, 20), // 定位按钮距离对应角落的距离
194 + 'showMarker': true, // 是否显示定位点
195 + 'markerOptions': { // 自定义定位点样式,同Marker的Options
196 + 'offset': new AMap.Pixel(-18, -36),
197 + 'content': '<img src="https://a.amap.com/jsapi_demos/static/resource/img/user.png" style="width:36px;height:36px"/>'
198 + },
199 + 'showCircle': true, // 是否显示定位精度圈
200 + 'circleOptions': { // 定位精度圈的样式
201 + 'strokeColor': '#0093FF',
202 + 'noSelect': true,
203 + 'strokeOpacity': 0.5,
204 + 'strokeWeight': 1,
205 + 'fillColor': '#02B0FF',
206 + 'fillOpacity': 0.25
207 + },
208 + enableHighAccuracy: true
209 + },
210 + geolocation: '',
211 + current_lng: '',
212 + current_lat: '',
213 + current_route: '',
214 + current_safe_route: [],
215 + show_popup: false,
216 + dialog_show: false,
217 + dialog_text: '',
218 + walk_route: '',
219 + isActive: 0,
220 + location_marker: '',
221 + // route_marker: [],
222 + route_safe_marker: [],
223 + open_safe_route: true,
224 + show_walk_route: true,
225 + popup_title: '',
226 + popup_content: '',
227 + video_src: '',
228 + show_nav_popup: false,
229 + showInfoWindow: false,
230 + showInfoWindowLite: false,
231 + showInfoWindowWarn: false,
232 + infoWindow: {},
233 + infoWindowLite: {},
234 + infoWindowWarn: {},
235 + itemInfo: {},
236 + navBarList: [],
237 + rect: {},
238 + navList: [],
239 + navKey: '',
240 + markerSum: [], // marker合集
241 + // titleLayerSet: {
242 + // 17: {
243 + // x: [109439, 109441],
244 + // y: [53519, 53521]
245 + // },
246 + // 18: {
247 + // x: [218879, 218882],
248 + // y: [107039, 107042]
249 + // }
250 + // },
251 + defaultZoom: 18,
252 + defaultCenter: [120.587382, 31.313900],
253 + mapTiles: [],
254 + open_current_location: true,
255 + data_center: [], // 接口获取-地图中心点
256 + data_zoom: '', // 接口获取-地图默认缩放
257 + data_zooms: '', // 接口获取-地图默认缩放范围
258 + data_rotation: 0, // 接口获取-地图旋转角度
259 + data_paths: {}, // 接口获取-地图导航路径
260 + data_path_list: [], // 接口获取-地图导航路径
261 + showInfoPopup: false,
262 + showInfoLitePopup: false,
263 + showInfoWarnPopup: false,
264 + data_layers: [],
265 + }
266 + },
267 + async mounted() {
268 + const code = this.$route.query.id;
269 + const { data } = await mapAPI({ i: code });
270 + // TAG:预警显示
271 + // data.list = data.list.concat(map_alert);
272 + this.navBarList = data.list; // 底部导航条
273 + this.mapTiles = data.level; // 获取图层
274 + this.navKey = data.list[0]['id']; // 默认选中 第一个 id
275 + this.navList = data.list.filter(item => item.id === this.navKey)[0]['list']; // 返回默认选中项的实体信息
276 + this.data_center = data.map.center.map(item => Number(item)); // 地图中心点
277 + this.data_zoom = data.map.zoom; // 地图默认缩放
278 + this.data_rotation = data.map.rotation; // 地图旋转角度
279 + this.data_zooms = data.map.zooms.map(item => Number(item)); // 地图默认缩放范围
280 + this.data_paths = data.map.path ? data.map.path : {}; // 地图默认导航路径
281 + if (data.map.map_layers) { // 地图默认图层
282 + if (data.map.map_layers === 'satellite') { // 卫星和路网
283 + this.data_layers = [new AMap.TileLayer.Satellite(), new AMap.TileLayer.RoadNet()]
284 + } else { // 平面图
285 + this.data_layers = [];
286 + }
287 + }
288 + if (data.map.path) {
289 + for (const key in data.map.path) {
290 + const element = data.map.path[key];
291 + this.data_path_list.push({
292 + name: key,
293 + path: element,
294 + status: true
295 + })
296 + }
297 + }
298 + // 初始化地图
299 + this.initMap();
300 + // this.setMapBoundary();
301 + // 使用之前获取当前地址,判断当前是否能够获取经纬度
302 + wx.getLocation({
303 + type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
304 + success: (res) => {
305 + var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
306 + var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
307 + var speed = res.speed; // 速度,以米/每秒计
308 + var accuracy = res.accuracy; // 位置精度
309 + this.current_lng = GPS.gcj_encrypt(latitude, longitude).lon;
310 + this.current_lat = GPS.gcj_encrypt(latitude, longitude).lat;
311 + },
312 + });
313 + // 设置贴片地图
314 + this.setTitleLayer();
315 + },
316 + watch: {
317 + // show_popup(val) {
318 + // if (!val) {
319 + // this.$nextTick(() => {
320 + // // 弹框关闭时,暂停视频
321 + // $('.vjs-tech')[0].pause();
322 + // $('.vjs-tech')[0].currentTime = 0;
323 + // })
324 + // }
325 + // },
326 + showInfoWindow(val) {
327 + if (val) {
328 + // 元素的大小及其相对于视口的位置
329 + const rect = useRect(this.$refs.root);
330 + this.rect = rect;
331 + }
332 + },
333 + showInfoWindowLite(val) {
334 + if (val) {
335 + // 元素的大小及其相对于视口的位置
336 + const rect = useRect(this.$refs.root);
337 + this.rect = rect;
338 + }
339 + },
340 + showInfoWindowWarn(val) {
341 + if (val) {
342 + // 元素的大小及其相对于视口的位置
343 + const rect = useRect(this.$refs.root);
344 + this.rect = rect;
345 + }
346 + }
347 + },
348 + methods: {
349 + initMap() {
350 + // 初始化地图
351 + this.map = new AMap.Map('container', {
352 + viewMode: '2D', // 设置地图模式
353 + turboMode: false,
354 + showIndoorMap: false,
355 + defaultCursor: 'pointer', // 地图默认鼠标样式
356 + showBuildingBlock: false, // 是否展示地图 3D 楼块
357 + zooms: this.data_zooms, // 地图显示的缩放级别范围, 默认为 [2, 20] ,取值范围 [2 ~ 30]
358 + showLabel: true, // 是否展示地图文字和 POI 信息
359 + zoom: this.data_zoom, // 设置地图显示的缩放级别
360 + pitch: 0, // 俯仰角度,默认 0,最大值根据地图当前 zoom 级别不断增大,2D地图下无效 。
361 + rotation: this.data_rotation, // 地图顺时针旋转角度,取值范围 [0-360] ,默认值:0
362 + center: this.data_center, // 设置地图中心点坐标
363 + forceVector: false,
364 + // rotateEnable: true,
365 + layers: this.data_layers,
366 + features: ['bg', 'road'], // 设置地图上显示的元素种类
367 + animateEnable: false, // 地图平移过程中是否使用动画
368 + resizeEnable: true,
369 + });
370 + // 添加地图点击事件
371 + this.map.on("click", this.showInfoClick);
372 + // 加载景点图层
373 + this.loadMaker(this.navKey);
374 + //
375 + this.map.setRotation(this.data_rotation, true);
376 + },
377 + setNavLayer({ id }, index) { // 选择地图图层显示
378 + this.isActive = index;
379 + this.navList = this.navBarList.filter(item => item.id === id)[0]['list']; // 返回默认选中项的实体信息
380 + this.removeLayer();
381 + this.loadMaker(id);
382 + this.closeInfoWindow();
383 + setTimeout(() => {
384 + // 地图zooms调整
385 + this.map.setZoom(this.defaultZoom);
386 + this.map.setZoomAndCenter(this.defaultZoom, this.data_center);
387 + }, 100);
388 + this.removeNavRoute();
389 + },
390 + loadMaker(id) {
391 + var zoomStyleMapping = { 14: 0, 15: 0, 16: 0, 17: 0, 18: 0, 19: 0, 20: 0 };
392 + const entity_info = this.navBarList.filter(item => item.id === id)[0]['list'];
393 + this.markerSum = [];
394 + _.each(entity_info, (x, i) => {
395 + let marker_icon = '';
396 + if (entity_info[i].window_type === 'warn' && entity_info[i].details.length === 1) { // 如果是预警类型并且内部预警项目只有一个取details第一个icon
397 + marker_icon = entity_info[i].details[0]['icon'];
398 + } else {
399 + marker_icon = entity_info[i].icon;
400 + }
401 + // var marker = new AMap.ElasticMarker({
402 + // position: entity_info[i].position,
403 + // zooms: [17, 19],
404 + // styles: [{
405 + // icon: {
406 + // img: marker_icon, // 标记点图标
407 + // size: [28, 28], // 可见区域的大小
408 + // anchor: 'bottom-center', // 锚点
409 + // fitZoom: 14, // 最合适的级别
410 + // scaleFactor: 2, // 地图放大一级的缩放比例系数
411 + // maxScale: 1.4, // 最大放大比例
412 + // minScale: 0.8 // 最小放大比例
413 + // },
414 + // label: {
415 + // content: entity_info[i].name,
416 + // position: 'TM',
417 + // // position: 'BM',
418 + // // offset: new AMap.Pixel(0, 10),
419 + // minZoom: 18
420 + // }
421 + // }, {
422 + // icon: {},
423 + // label: {}
424 + // }],
425 + // zoomStyleMapping: entity_info[i].zoom ? entity_info[i].zoom : zoomStyleMapping
426 + // });
427 + // if (clickListener) {
428 + // marker.off('click', clickListener)
429 + // }
430 + // // 绑定景点的点击事件 - 文字出现才能触发
431 + // var clickListener = marker.on('click', (e) => {
432 + // // 不同弹框类型
433 + // if (entity_info[i].window_type === 'normal') {
434 + // this.positionMarker(entity_info[i])
435 + // } else if(entity_info[i].window_type === 'lite') {
436 + // this.positionLiteMarker(entity_info[i])
437 + // } else if (entity_info[i].window_type === 'warn') {
438 + // this.positionWarnMarker(entity_info[i])
439 + // }
440 + // })
441 + // // marker合集
442 + // this.markerSum.push(marker);
443 + // 创建一个 文字 实例 - 适配竖型文字
444 + // let writing_mode = 'vertical'; // 文字方向
445 + if (entity_info[i]?.writing_mode === 'vertical') { // 标题文字垂直
446 + var textMarker = new AMap.Text({
447 + zooms: [18, 20], // 点标记显示的层级范围,超过范围不显示。
448 + text: entity_info[i].name, //标记显示的文本内容
449 + anchor: "center", //设置文本标记锚点位置
450 + // draggable: true, //是否可拖拽
451 + // cursor: "pointer", //指定鼠标悬停时的鼠标样式。
452 + // angle: 10, //点标记的旋转角度
453 + style: {
454 + //设置文本样式,Object 同 css 样式表
455 + "padding": ".5rem .2rem .5rem .2rem",
456 + // "margin-bottom": "1rem",
457 + // "border-radius": ".25rem",
458 + "background-color": "#965f13",
459 + // "width": "1rem",
460 + // "border-width": 0,
461 + // "box-shadow": "0 2px 6px 0 rgba(114, 124, 245, .5)",
462 + // "text-align": "center",
463 + "font-size": "0.8rem",
464 + "color": "white",
465 + "writing-mode": "vertical-rl",
466 + "text-orientation": "mixed",
467 + "display": "flex",
468 + "justify-content": "center",
469 + "align-items": "center",
470 + },
471 + position: entity_info[i].position, //点标记在地图上显示的位置
472 + });
473 + if (clickListener) {
474 + textMarker.off('click', clickListener)
475 + }
476 + // 绑定景点的点击事件 - 文字出现才能触发
477 + var clickListener = textMarker.on('click', (e) => {
478 + // console.warn(e);
479 + this.itemInfo = entity_info[i];
480 + // 不同弹框类型
481 + if (entity_info[i].window_type === 'normal') {
482 + this.showInfoPopup = true;
483 + } else if (entity_info[i].window_type === 'lite') {
484 + this.showInfoLitePopup = true;
485 + } else if (entity_info[i].window_type === 'warn') {
486 + this.showInfoWarnPopup = true;
487 + }
488 + })
489 + textMarker.setMap(this.map); //将文本标记设置到地图上
490 + this.markerSum.push(textMarker);
491 + } else { // 默认方向
492 + // 创建一个 LabelMarker 实例
493 + var labelMarker = new AMap.LabelMarker({
494 + position: entity_info[i].position,
495 + opacity: 1,
496 + zIndex: 2,
497 + opacity: 0.9,
498 + // rank: 10,
499 + icon: {
500 + image: marker_icon,
501 + anchor: 'bottom-center',
502 + size: [36, 36],
503 + },
504 + text: {
505 + zooms: [18, 20],
506 + content: entity_info[i].name,
507 + direction: 'top',
508 + offset: [0, 5],
509 + style: {
510 + padding: [10, 10],
511 + fontSize: 14,
512 + fillColor: '#fff',
513 + strokeWidth: 0,
514 + backgroundColor: '#965f13',
515 + borderWidth: 0,
516 + }
517 + }
518 + });
519 + if (clickListener) {
520 + labelMarker.off('click', clickListener)
521 + }
522 + // 绑定景点的点击事件 - 文字出现才能触发
523 + var clickListener = labelMarker.on('click', (e) => {
524 + // 不同弹框类型
525 + if (entity_info[i].window_type === 'normal') {
526 + this.positionMarker(entity_info[i])
527 + } else if (entity_info[i].window_type === 'lite') {
528 + this.positionLiteMarker(entity_info[i])
529 + } else if (entity_info[i].window_type === 'warn') {
530 + this.positionWarnMarker(entity_info[i])
531 + }
532 + })
533 + // 使用LabelsLayer包裹缩放地图时,图标不会自动显示和消失需要配置rank排序
534 + // 创建一个 LabelsLayer 实例来承载 LabelMarker,[LabelsLayer 文档](https://lbs.amap.com/api/jsapi-v2/documentation#labelslayer)
535 + var labelsLayer = new AMap.LabelsLayer({
536 + collision: true,
537 + });
538 + // // 将 LabelMarker 实例添加到 LabelsLayer 上
539 + labelsLayer.add(labelMarker);
540 + // 将 LabelsLayer 添加到地图上
541 + // this.map.add(labelsLayer);
542 + this.markerSum.push(labelsLayer);
543 + }
544 + })
545 + this.map.add(this.markerSum);
546 + },
547 + removeLayer() {
548 + this.map.remove(this.markerSum);
549 + },
550 + // setMapBoundary() { // 地图描边
551 + // new AMap.Polygon({
552 + // cursor: 'pointer',
553 + // bubble: true,
554 + // path: [[120.587704, 31.312785], [120.587669, 31.313028], [120.587554, 31.313086], [120.586883, 31.313019], [120.586826, 31.314066], [120.586736, 31.314426], [120.585872, 31.314466], [120.585675, 31.315276], [120.585817, 31.315397], [120.586251, 31.31542], [120.586229, 31.31618], [120.588248, 31.316367], [120.588533, 31.314761], [120.588479, 31.31474], [120.588482, 31.314172], [120.588251, 31.314145], [120.588337, 31.313184], [120.588337, 31.313184], [120.588154, 31.313163], [120.588152, 31.312835]],
555 + // map: this.map,
556 + // fillOpacity: 0.5,
557 + // strokeWeight: 1,
558 + // fillColor: '#CFE7AA'
559 + // });
560 + // },
561 + isPointInRing() { // 是否在景区范围
562 + let isPointInRing = AMap.GeometryUtil.isPointInRing([this.current_lng, this.current_lat], [
563 + [120.585111, 31.316084], [120.585111, 31.316084], [120.589488, 31.313197], [120.585422, 31.313005]
564 + ]);
565 + return isPointInRing
566 + },
567 + setLocation() { // 开启定位服务
568 + // 获取失败
569 + if (!this.current_lng || !this.current_lat) {
570 + this.dialog_show = true;
571 + this.dialog_text = '获取经纬度失败';
572 + }
573 + this.getLocation();
574 + // // this.map.addControl(this.geolocation); // 添加定位图标 自动跳转当前位置
575 + // if (!this.current_lng || !this.current_lat) {
576 + // this.getLocation()
577 + // } else {
578 + // // 判断是否在范围内
579 + // if (!this.isPointInRing()) {
580 + // this.dialog_show = true;
581 + // this.dialog_text = '您不在景区范围内';
582 + // } else {
583 + // // this.current_lng = '120.587643'
584 + // // this.current_lat = '31.314853'
585 + // // 使用纠正偏移后的地址,打一个定位标记
586 + // this.location_marker = new AMap.LabelMarker({
587 + // icon: {
588 + // image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A.png',
589 + // anchor: 'bottom-center',
590 + // size: [36, 36],
591 + // },
592 + // position: new AMap.LngLat(this.current_lng, this.current_lat), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
593 + // });
594 + // this.map.add(this.location_marker);
595 + // // 定位到当前位置中心
596 + // this.map.setZoomAndCenter(this.zoom, [this.current_lng, this.current_lat]);
597 + // }
598 + // }
599 + },
600 + handleLocation(status) { // 打开/关闭 当前定位
601 + if (status) {
602 + this.setLocation()
603 + this.open_current_location = false;
604 + } else {
605 + this.removeLocation()
606 + this.open_current_location = true;
607 + }
608 + },
609 + removeLocation() { // 移除定位标记
610 + this.current_lng = '';
611 + this.current_lat = '';
612 + this.map.remove(this.location_marker); // 删除当前定位标记
613 + },
614 + getLocation() { // 获取经纬度
615 + // AMap.plugin(['AMap.Geolocation'], () => {
616 + // this.geolocation = new AMap.Geolocation(this.location_options);
617 + // this.geolocation.getCurrentPosition((status, result) => {
618 + // if (status === 'complete') {
619 + // let lat = result.position.lat;
620 + // let lng = result.position.lng;
621 + // // 行动路线
622 + // if (lng && lat) {
623 + // // this.current_lng = GPS.gcj_encrypt(lat, lng).lon;
624 + // // this.current_lat = GPS.gcj_encrypt(lat, lng).lat;
625 + // this.current_lng = lng;
626 + // this.current_lat = lat;
627 + // }
628 + // } else {
629 + // console.warn('获取失败');
630 + // }
631 + // })
632 + // });
633 + // PC端无法获取定位
634 + // 微信获取地址
635 + wx.getLocation({
636 + type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
637 + success: (res) => {
638 + var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
639 + var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
640 + var speed = res.speed; // 速度,以米/每秒计
641 + var accuracy = res.accuracy; // 位置精度
642 + this.current_lng = GPS.gcj_encrypt(latitude, longitude).lon;
643 + this.current_lat = GPS.gcj_encrypt(latitude, longitude).lat;
644 + // 判断是否在范围内
645 + if (!this.isPointInRing()) {
646 + this.dialog_show = true;
647 + this.dialog_text = '您不在景区范围内';
648 + } else {
649 + // 使用纠正偏移后的地址,打一个定位标记
650 + this.location_marker = new AMap.LabelMarker({
651 + icon: {
652 + image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A.png',
653 + anchor: 'bottom-center',
654 + size: [36, 36],
655 + },
656 + position: new AMap.LngLat(this.current_lng, this.current_lat), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
657 + });
658 + this.map.add(this.location_marker);
659 + // 定位到当前位置中心
660 + this.map.setZoomAndCenter(this.zoom, [this.current_lng, this.current_lat]);
661 + }
662 + },
663 + complete: () => {
664 + // 获取失败
665 + if (!this.current_lng || !this.current_lat) {
666 + this.dialog_show = true;
667 + this.dialog_text = '获取经纬度失败';
668 + }
669 + },
670 + });
671 + },
672 + setZoom(type) { // 设置放大缩小地图
673 + const zoom = this.map.getZoom();
674 + if (type === 'plus') {
675 + this.map.setZoom(zoom + 1)
676 + }
677 + if (type === 'minus') {
678 + this.map.setZoom(zoom - 1)
679 + }
680 + },
681 + addSafeRoute({name, path}) { // 新增路径
682 + // 获取对象的第一个键和值
683 + // let firstKey = Object.keys(this.data_paths)[0];
684 + // let firstValue = this.data_paths[firstKey];
685 + // 行动路线
686 + // var path = [
687 + // [120.587645, 31.314833],
688 + // [120.587709, 31.314338],
689 + // [120.588211, 31.314377],
690 + // ];
691 + // console.warn(firstValue);
692 + // var path = firstValue;
693 + // 生成折线地图路径
694 + let current_safe_route = new AMap.Polyline({
695 + path,
696 + isOutline: true,
697 + outlineColor: '#fba601',
698 + borderWeight: 1,
699 + strokeColor: '#fba601',
700 + strokeOpacity: 1,
701 + strokeWeight: 3,
702 + // 折线样式还支持 'dashed'
703 + strokeStyle: 'solid',
704 + // strokeStyle是dashed时有效
705 + strokeDasharray: [10, 5],
706 + lineJoin: 'round',
707 + lineCap: 'round',
708 + zIndex: 50
709 + })
710 + this.map.add([current_safe_route]);
711 + this.current_safe_route.push({
712 + key: name,
713 + path: current_safe_route
714 + })
715 + // 设置起始点标记
716 + var marker1 = new AMap.Marker({
717 + icon: new AMap.Icon({
718 + image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A.png',
719 + size: new AMap.Size(40, 40),
720 + // 图标所用图片大小
721 + imageSize: new AMap.Size(40, 40),
722 + // 图标取图偏移量
723 + imageOffset: new AMap.Pixel(0, 0)
724 + }),
725 + position: new AMap.LngLat(path[0][0], path[0][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
726 + anchor: 'bottom-center',
727 + offset: new AMap.Pixel(0, 0)
728 + });
729 + marker1.setLabel({
730 + direction: 'right',
731 + offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
732 + content: "<div class='info'>起点</div>", //设置文本标注内容
733 + });
734 + var marker2 = new AMap.Marker({
735 + icon: new AMap.Icon({
736 + image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A.png',
737 + size: new AMap.Size(40, 40),
738 + // 图标所用图片大小
739 + imageSize: new AMap.Size(40, 40),
740 + // 图标取图偏移量
741 + imageOffset: new AMap.Pixel(0, 0)
742 + }),
743 + position: new AMap.LngLat(path[path.length - 1][0], path[path.length - 1][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
744 + anchor: 'bottom-center',
745 + offset: new AMap.Pixel(0, 0)
746 + });
747 + marker2.setLabel({
748 + direction: 'right',
749 + offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
750 + content: "<div class='info'>终点</div>", //设置文本标注内容
751 + });
752 + // 新增逃生路线标记
753 + // this.route_safe_marker = [marker1, marker2]
754 + // this.map.add(this.route_safe_marker);
755 + // 新增逃生路线标记
756 + let route_safe_marker = [marker1, marker2]
757 + this.map.add(route_safe_marker);
758 + this.route_safe_marker.push({
759 + key: name,
760 + path: route_safe_marker
761 + })
762 + },
763 + removeSafeRoute({name}) { // 移除地图路线
764 + this.current_safe_route.forEach(item => {
765 + if (item.key === name) {
766 + this.map.remove([item.path]); // 删除地图折线
767 + }
768 + });
769 + // this.map.remove(this.route_safe_marker); // 删除起始点标记
770 + this.route_safe_marker.forEach(item => {
771 + if (item.key === name) {
772 + this.map.remove(item.path); // 删除起始点标记
773 + }
774 + });
775 + },
776 + // addNavRoute (path) { // 新增导航路径
777 + // // 生成折线地图路径
778 + // this.current_route = new AMap.Polyline({
779 + // path,
780 + // isOutline: true,
781 + // outlineColor: '#42a5f5',
782 + // borderWeight: 1,
783 + // strokeColor: '#42a5f5',
784 + // strokeOpacity: 1,
785 + // strokeWeight: 2,
786 + // // 折线样式还支持 'dashed'
787 + // strokeStyle: 'dashed',
788 + // // strokeStyle是dashed时有效
789 + // strokeDasharray: [10, 5],
790 + // lineJoin: 'round',
791 + // lineCap: 'round',
792 + // zIndex: 50
793 + // })
794 + // this.map.add([this.current_route]);
795 + // // 设置起始点标记
796 + // var marker1 = new AMap.Marker({
797 + // icon: new AMap.Icon({
798 + // image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8D-%E5%B0%8Fz@2x.png',
799 + // size: new AMap.Size(40, 40),
800 + // // 图标所用图片大小
801 + // imageSize: new AMap.Size(40, 40),
802 + // // 图标取图偏移量
803 + // imageOffset: new AMap.Pixel(0, 0)
804 + // }),
805 + // position: new AMap.LngLat(path[0][0], path[0][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
806 + // anchor: 'bottom-center',
807 + // offset: new AMap.Pixel(0, 0)
808 + // });
809 + // marker1.setLabel({
810 + // direction: 'right',
811 + // offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
812 + // content: "<div class='info'>终点</div>", //设置文本标注内容
813 + // });
814 + // var marker2 = new AMap.Marker({
815 + // icon: new AMap.Icon({
816 + // image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8D-%E5%B0%8Fz@2x.png',
817 + // size: new AMap.Size(40, 40),
818 + // // 图标所用图片大小
819 + // imageSize: new AMap.Size(40, 40),
820 + // // 图标取图偏移量
821 + // imageOffset: new AMap.Pixel(0, 0)
822 + // }),
823 + // position: new AMap.LngLat(path[path.length - 1][0], path[path.length - 1][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
824 + // anchor: 'bottom-center',
825 + // offset: new AMap.Pixel(0, 0)
826 + // });
827 + // marker2.setLabel({
828 + // direction: 'right',
829 + // offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
830 + // content: "<div class='info'>起点</div>", //设置文本标注内容
831 + // });
832 + // // 新增逃生路线标记
833 + // this.route_marker = [marker1, marker2]
834 + // this.map.add(this.route_marker);
835 + // // 关闭导航提示
836 + // this.show_walk_route = false;
837 + // },
838 + addNavRoute(position) { // 新增导航路径
839 + function arraysEqual(a, b) {
840 + // 如果两个数组的长度不等,则它们不相等
841 + if (a.length !== b.length) {
842 + return false;
843 + }
844 +
845 + // 比较每个元素是否相等
846 + return a.every(function (element, index) {
847 + return element == b[index];
848 + });
849 + }
850 + let array = []; // 导航点所在路径集合
851 + my_router.forEach((route) => {
852 + route.path.forEach((item) => {
853 + if (arraysEqual(position, item)) {
854 + array.push(route);
855 + return false;
856 + }
857 + })
858 + });
859 + let lngLat = [120.587234, 31.314147]; // 左边
860 + // const lngLat = [120.588178, 31.314396]; // 右边
861 + // console.log(this.current_lng ,this.current_lat);
862 + // 构建路线结构
863 + // 如果建筑不在导航路径上面需要单独处理, 直接查询建筑离哪个导航路径最近显示出来
864 + const route_obj = array.length ? array: my_router;
865 + if (!array.length) {
866 + lngLat = position; // 当前导航建筑经纬度
867 + }
868 + // 计算距离最近的路径
869 + route_obj.forEach((line) => {
870 + line.distance = AMap.GeometryUtil.distanceToLine(lngLat, line.path);
871 + });
872 + let min = Math.min(...route_obj.map((line) => line.distance))
873 + let result = route_obj.filter((line) => line.distance === min);
874 + // 标记示例
875 + this.current_route = new AMap.Polyline({
876 + path: result[0]['path'],
877 + isOutline: true,
878 + outlineColor: '#42a5f5',
879 + borderWeight: 1,
880 + strokeColor: '#42a5f5',
881 + strokeOpacity: 1,
882 + strokeWeight: 2,
883 + // 折线样式还支持 'dashed'
884 + strokeStyle: 'dashed',
885 + // strokeStyle是dashed时有效
886 + strokeDasharray: [10, 5],
887 + lineJoin: 'round',
888 + lineCap: 'round',
889 + zIndex: 50
890 + })
891 + this.map.add([this.current_route]);
892 + // 获取定位打标记
893 + this.setLocation();
894 + // this.location_marker = new AMap.Marker({
895 + // icon: new AMap.Icon({
896 + // image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A.png',
897 + // size: new AMap.Size(40, 40),
898 + // // 图标所用图片大小
899 + // imageSize: new AMap.Size(40, 40),
900 + // // 图标取图偏移量
901 + // imageOffset: new AMap.Pixel(0, 0)
902 + // }),
903 + // position: new AMap.LngLat(...lngLat), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
904 + // anchor: 'bottom-center',
905 + // offset: new AMap.Pixel(0, 0)
906 + // });
907 + // this.map.add([this.location_marker]);
908 + // 关闭导航提示
909 + this.show_walk_route = false;
910 + },
911 + removeNavRoute() { // 移除地图路线
912 + this.map.remove([this.current_route]); // 删除地图折线
913 + // this.map.remove(this.route_marker); // 删除起始点标记
914 + this.map.remove(this.location_marker); // 删除当前定位标记
915 + // 关闭导航提示
916 + this.show_walk_route = true;
917 + },
918 + computedMapSource(x, y, z) { // 根据图层信息生成图层实际地址
919 + for (const id in this.mapTiles) {
920 + if (z == id) {
921 + const scope = this.mapTiles[id];
922 + return scope[`${x}-${y}`]
923 + }
924 + }
925 + },
926 + setTitleLayer() { // 生成瓦片图
927 + // 获取瓦片图渲染范围
928 + function getFirstProperty(obj) {
929 + for (var prop in obj) {
930 + return prop;
931 + }
932 + }
933 + function getLastProperty(obj) {
934 + var props = [];
935 + for (var prop in obj) {
936 + props.push(prop);
937 + }
938 + return props[props.length - 1];
939 + }
940 + let obj_scope = {};
941 + for (const key in this.mapTiles) {
942 + const element = this.mapTiles[key];
943 + let first = getFirstProperty(element).split('-');
944 + let last = getLastProperty(element).split('-');
945 + obj_scope[key] = {
946 + x: [first[0], last[0]],
947 + y: [first[1], last[1]]
948 + }
949 + }
950 + const _this = this;
951 + var layer = new AMap.TileLayer.Flexible({
952 + cacheSize: 50,
953 + opacity: 1,
954 + zIndex: 100,
955 + createTile: function (x, y, z, success, fail) {
956 + // 控制地图等级显示图片范围-过滤不显示的图层渲染
957 + for (const id in obj_scope) {
958 + if (z == id) {
959 + const scope = obj_scope[id];
960 + if (x < scope.x[0] || x > scope.x[1]) {
961 + fail()
962 + return;
963 + }
964 + if (y < scope.y[0] || y > scope.y[1]) {
965 + fail()
966 + return;
967 + }
968 + }
969 + }
970 +
971 + var img = document.createElement('img');
972 + img.onload = function () {
973 + success(img)
974 + };
975 + img.crossOrigin = "anonymous";// 必须添加,同时图片要有跨域头
976 + img.onerror = function () {
977 + fail()
978 + };
979 +
980 + // img.src = `images/tiles/${z}/${x}_${y}.png`;
981 + img.src = _this.computedMapSource(x, y, z);
982 + },
983 + });
984 +
985 + this.map.addLayer(layer);
986 +
987 + // Canvas作为切片
988 + var layer1 = new AMap.TileLayer.Flexible({
989 + // tileSize: 128,
990 + cacheSize: 300,
991 + zIndex: 200,
992 + createTile: function (x, y, z, success, fail) {
993 + var c = document.createElement('canvas');
994 + c.width = c.height = 256;
995 +
996 + var cxt = c.getContext("2d");
997 + cxt.font = "15px Verdana";
998 + cxt.fillStyle = "#ff0000";
999 + cxt.strokeStyle = "#FF0000";
1000 + cxt.strokeRect(0, 0, 256, 256);
1001 + cxt.fillText('(' + [x, y, z].join(',') + ')', 10, 30);
1002 +
1003 + // 通知API切片创建完成
1004 + success(c);
1005 + }
1006 + });
1007 +
1008 + // layer1.setMap(this.map);
1009 +
1010 + // TODO: 暂时屏蔽等待数据设置字段
1011 + // 只显示相应区域,移动会回到选定范围
1012 + const id = this.$route.query.id;
1013 + if (id === '1759271') { // 定制开发
1014 + // this.lockMapBounds()
1015 + }
1016 +
1017 + },
1018 + // 限制地图范围
1019 + lockMapBounds() {
1020 + // var bounds = this.map.getBounds();
1021 + var myBounds = new AMap.Bounds(
1022 + [117.045587,26.838764],
1023 + [117.051081,26.8345723]
1024 + );
1025 +
1026 + this.map.setLimitBounds(myBounds);
1027 +
1028 + let list =[
1029 + [117.045598,26.838764],
1030 + [117.051075,26.838779],
1031 + [117.051075,26.835107],
1032 + [117.045598,26.835107]
1033 + ]
1034 +
1035 + // 隐藏边界以外的区域
1036 + let outer = [
1037 + new AMap.LngLat(-360, 90, true),
1038 + new AMap.LngLat(-360, -90, true),
1039 + new AMap.LngLat(360, -90, true),
1040 + new AMap.LngLat(360, 90, true),
1041 + ] // 遮盖填充反向
1042 +
1043 + let pathArray = [
1044 + outer,
1045 + list
1046 + ]
1047 +
1048 + var polygon = new AMap.Polygon({
1049 + pathL: pathArray,
1050 + strokeColor: "#fff",
1051 + strokeWeight: 2,
1052 + fillColor: "#fff",
1053 + fillOpacity: 1,
1054 + })
1055 +
1056 + polygon.setPath(pathArray)
1057 + this.map.add(polygon)
1058 +
1059 + },
1060 + showInfoClick(e) {
1061 + // console.log(e);
1062 + var zoom = this.map.getZoom(); //获取当前地图级别
1063 + // var text =
1064 + // "您在 [" +
1065 + // e.lnglat.getLng() +
1066 + // "," +
1067 + // e.lnglat.getLat() +
1068 + // "] 的位置单击了地图!当前层级" +
1069 + // zoom;
1070 + var text =
1071 + "[" +
1072 + e.lnglat.getLng() +
1073 + "," +
1074 + e.lnglat.getLat() +
1075 + "],"
1076 + console.log(text);
1077 + // 点击空白处 关闭弹框
1078 + this.closeInfoWindow();
1079 + // 关闭弹出底部导航弹框
1080 + this.show_nav_popup = false;
1081 + },
1082 + // setWalkRoute(start = { lng: 120.587799, lat: 31.313276 }, end = { lng: 120.587912, lat: 31.315169 }) {
1083 + // //步行导航
1084 + // let walking_CallBack = (result) => {
1085 + // if (result.type === 'complete') {
1086 + // console.warn(result);
1087 + // console.warn('绘制步行路线完成');
1088 + // } else {
1089 + // console.error('步行路线数据查询失败' + result);
1090 + // }
1091 + // }
1092 + // AMap.plugin(["AMap.Walking"], () => { //加载步行导航插件
1093 + // this.walk_route = new AMap.Walking({
1094 + // map: this.map,
1095 + // }); //构造步行导航类
1096 + // AMap.Event.addListener(this.walk_route, "complete", walking_CallBack); //返回导航查询结果
1097 + // //根据起、终点坐标规划步行路线
1098 + // this.walk_route.search(new AMap.LngLat(start.lng, start.lat), new AMap.LngLat(end.lng, end.lat));
1099 + // });
1100 + // this.show_walk_route = false;
1101 + // },
1102 + // removeWalkRoute() {
1103 + // this.walk_route.clear();
1104 + // this.show_walk_route = true;
1105 + // },
1106 + infoWindowLocation(path) { // 监听前往按钮回调
1107 + // TODO: 实际获取精确定位后导航到位置
1108 + // // 判断是否在范围内
1109 + // if (!this.isPointInRing()) {
1110 + // this.dialog_show = true;
1111 + // } else {
1112 + // this.setWalkRoute({ lng: this.current_lng, lat: this.current_lat }, { lng: position[0], lat: position[1] })
1113 + // }
1114 + // 测试
1115 + this.closeInfoWindow(); // 关闭弹框
1116 + this.onClose(); // 关闭弹框
1117 + // this.setWalkRoute({ lng: 120.59014, lat: 31.310306 }, { lng: position[0], lat: position[1] });
1118 + if (this.current_route) { // 清除前一条步行导航
1119 + this.map.remove([this.current_route]); // 删除地图折线
1120 + // this.map.remove(this.route_marker); // 删除起始点标记
1121 + this.map.remove(this.location_marker); // 删除当前定位标记
1122 + }
1123 + if (path.length) {
1124 + this.addNavRoute(path)
1125 + } else {
1126 + this.dialog_show = true;
1127 + this.dialog_text = '内部设施';
1128 + }
1129 + },
1130 + handleSafeRoute(status, item) { // 打开/关闭逃生路线线
1131 + if (status) {
1132 + this.addSafeRoute(item)
1133 + // this.open_safe_route = false;
1134 + } else {
1135 + this.removeSafeRoute(item)
1136 + // this.open_safe_route = true;
1137 + }
1138 + item.status = !status;
1139 + },
1140 + handleNavPopup() {
1141 + this.show_nav_popup = !this.show_nav_popup
1142 + },
1143 + handleNavMarker(item) { // 底部列表点击跳转弹框判断
1144 + if (item.window_type === 'normal') {
1145 + this.positionMarker(item)
1146 + } else if (item.window_type === 'lite') {
1147 + this.positionLiteMarker(item)
1148 + } else if (item.window_type === 'warn') {
1149 + this.positionWarnMarker(item)
1150 + }
1151 + },
1152 + positionMarker(item) {
1153 + // 点击后创建自定义信息窗口
1154 + this.setInfoWindows(item)
1155 + // 把地图中心点设置为当前点击的标记点
1156 + this.map.setZoomAndCenter(this.zoom, item.position);
1157 + //
1158 + this.show_nav_popup = false;
1159 + // 禁止缩放
1160 + this.map.setStatus({
1161 + zoomEnable: false
1162 + });
1163 + },
1164 + positionLiteMarker(item) {
1165 + // 点击后创建自定义信息窗口
1166 + this.setInfoWindowsLite(item)
1167 + // 把地图中心点设置为当前点击的标记点
1168 + this.map.setZoomAndCenter(this.zoom, item.position);
1169 + //
1170 + this.show_nav_popup = false;
1171 + // 禁止缩放
1172 + this.map.setStatus({
1173 + zoomEnable: false
1174 + });
1175 + },
1176 + positionWarnMarker(item) {
1177 + // 点击后创建自定义信息窗口
1178 + this.setInfoWindowsWarn(item)
1179 + // 把地图中心点设置为当前点击的标记点
1180 + this.map.setZoomAndCenter(this.zoom, [item.position[0], item.position[1] + 0.000300]);
1181 + //
1182 + this.show_nav_popup = false;
1183 + // 禁止缩放
1184 + this.map.setStatus({
1185 + zoomEnable: false
1186 + });
1187 + },
1188 + setInfoWindows(item) {
1189 + // 此时需要把组件的样式设置为可见
1190 + this.showInfoWindow = true
1191 + // 设置marker头部的标题信息窗口
1192 + const infoWindow = new AMap.InfoWindow({
1193 + // 使用自定义窗体
1194 + isCustom: true,
1195 + // 只有当组件渲染完毕后,通过$el才能拿到原生的dom对象
1196 + content: this.$refs['infoWindow'].$el,
1197 + // 设置定位偏移量
1198 + offset: new AMap.Pixel(0, -30),
1199 + })
1200 + this.infoWindow = infoWindow;
1201 + this.itemInfo = item;
1202 + this.itemInfo.map = this.map;
1203 + this.itemInfo.LngLat = {
1204 + lng: this.current_lng,
1205 + lat: this.current_lat,
1206 + }
1207 + // 信息窗口打开
1208 + infoWindow.open(this.map, item.position)
1209 + },
1210 + setInfoWindowsLite(item) {
1211 + // 此时需要把组件的样式设置为可见
1212 + this.showInfoWindowLite = true
1213 + // 设置marker头部的标题信息窗口
1214 + const infoWindowLite = new AMap.InfoWindow({
1215 + // 使用自定义窗体
1216 + isCustom: true,
1217 + // 只有当组件渲染完毕后,通过$el才能拿到原生的dom对象
1218 + content: this.$refs['infoWindowLite'].$el,
1219 + // 设置定位偏移量
1220 + offset: new AMap.Pixel(0, -30),
1221 + })
1222 + this.infoWindowLite = infoWindowLite;
1223 + this.itemInfo = item;
1224 + this.itemInfo.map = this.map;
1225 + this.itemInfo.LngLat = {
1226 + lng: this.current_lng,
1227 + lat: this.current_lat,
1228 + }
1229 + // 信息窗口打开
1230 + infoWindowLite.open(this.map, item.position)
1231 + },
1232 + setInfoWindowsWarn(item) {
1233 + // 此时需要把组件的样式设置为可见
1234 + this.showInfoWindowWarn = true
1235 + // 设置marker头部的标题信息窗口
1236 + const infoWindowWarn = new AMap.InfoWindow({
1237 + // 使用自定义窗体
1238 + isCustom: true,
1239 + // 只有当组件渲染完毕后,通过$el才能拿到原生的dom对象
1240 + content: this.$refs['infoWindowWarn'].$el,
1241 + // 设置定位偏移量
1242 + offset: new AMap.Pixel(0, -30),
1243 + })
1244 + this.infoWindowWarn = infoWindowWarn;
1245 + this.itemInfo = item;
1246 + this.itemInfo.map = this.map;
1247 + this.itemInfo.LngLat = {
1248 + lng: this.current_lng,
1249 + lat: this.current_lat,
1250 + }
1251 + // 信息窗口打开
1252 + infoWindowWarn.open(this.map, item.position);
1253 + },
1254 + closeInfoWindow() {
1255 + if (this.showInfoWindow) {
1256 + this.$refs['infoWindow'].close();
1257 + // 打开缩放
1258 + this.map.setStatus({
1259 + zoomEnable: true
1260 + });
1261 + }
1262 + if (this.showInfoWindowLite) {
1263 + this.$refs['infoWindowLite'].close();
1264 + // 打开缩放
1265 + this.map.setStatus({
1266 + zoomEnable: true
1267 + });
1268 + }
1269 + if (this.showInfoWindowWarn) {
1270 + this.$refs['infoWindowWarn'].close();
1271 + // 打开缩放
1272 + this.map.setStatus({
1273 + zoomEnable: true
1274 + });
1275 + }
1276 + },
1277 + onPlay(name) {
1278 + // var zoomStyleMapping = { 14: 0, 15: 0, 16: 0, 17: 0, 18: 0, 19: 0, 20: 0 };
1279 + // _.each(coord.spotInfo, (x, i) => {
1280 + // var marker = new AMap.ElasticMarker({
1281 + // position: coord.spotInfo[i].position,
1282 + // zooms: [17, 19],
1283 + // styles: [{
1284 + // icon: {
1285 + // img: x.name !== name ?coord.spotInfo[i].icon : 'https://cdn.ipadbiz.cn/xys/map/%E6%92%AD%E6%94%BE%E6%9A%82%E5%81%9C@2x.png',
1286 + // size: [28, 28], // 可见区域的大小
1287 + // anchor: 'bottom-center', // 锚点
1288 + // fitZoom: 14, // 最合适的级别
1289 + // scaleFactor: 2, // 地图放大一级的缩放比例系数
1290 + // maxScale: 1.4, // 最大放大比例
1291 + // minScale: 0.8 // 最小放大比例
1292 + // },
1293 + // label: {
1294 + // content: coord.spotInfo[i].name,
1295 + // position: 'TM',
1296 + // // position: 'BM',
1297 + // // offset: new AMap.Pixel(0, 10),
1298 + // minZoom: 18
1299 + // }
1300 + // }, {
1301 + // icon: {},
1302 + // label: {}
1303 + // }],
1304 + // zoomStyleMapping: coord.spotInfo[i].zoom ? coord.spotInfo[i].zoom : zoomStyleMapping
1305 + // });
1306 + // if (clickListener) {
1307 + // marker.off('click', clickListener)
1308 + // }
1309 + // // 绑定景点的点击事件 - 文字出现才能触发
1310 + // var clickListener = marker.on('click', (e) => {
1311 + // this.positionMarker(coord.spotInfo[i]);
1312 + // })
1313 +
1314 + // this.spotInfo.push(marker);
1315 + // })
1316 + // this.map.add(this.spotInfo);
1317 + },
1318 + onPause(name) { },
1319 + onClose () {
1320 + this.showInfoPopup = false;
1321 + },
1322 + onLiteClose () {
1323 + this.showInfoLitePopup = false;
1324 + },
1325 + onWarnClose () {
1326 + this.showInfoWarnPopup = false;
1327 + },
1328 + }
1329 +}
1330 +</script>
1331 +
1332 +<style lang="less">
1333 +#container {
1334 + position: absolute;
1335 + top: 0;
1336 + left: 0;
1337 + right: 0;
1338 + bottom: 0;
1339 + width: 100%;
1340 + height: 100%;
1341 +}
1342 +
1343 +/* 标记文字样式 */
1344 +.amap-marker-label {
1345 + padding: 0.25rem 0.5rem;
1346 + width: auto;
1347 + border: none;
1348 + border-radius: 2px;
1349 + background: rgba(86, 65, 23, 0.8);
1350 + color: white;
1351 +}
1352 +
1353 +.amap-marker {
1354 + .amap-icon {
1355 + margin-top: 0.25rem;
1356 + }
1357 +}
1358 +
1359 +.input-card {
1360 + display: flex;
1361 + flex-direction: column;
1362 + min-width: 0;
1363 + word-wrap: break-word;
1364 + background-color: #fff;
1365 + background-clip: border-box;
1366 + border-radius: .25rem;
1367 + width: 20rem;
1368 + border-width: 0;
1369 + border-radius: 0.4rem;
1370 + box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
1371 + position: fixed;
1372 + top: 4rem;
1373 + right: 1rem;
1374 + -ms-flex: 1 1 auto;
1375 + flex: 1 1 auto;
1376 + padding: 0.75rem 1.25rem;
1377 +}
1378 +
1379 +.tool-bar-wrapper {
1380 + position: absolute;
1381 + left: 20px;
1382 + bottom: 8rem;
1383 + width: 20px;
1384 +}
1385 +
1386 +.nav-bar-wrapper {
1387 + position: fixed;
1388 + bottom: 0;
1389 + left: 0;
1390 + height: 5.5rem;
1391 + width: 100%;
1392 + background-color: white;
1393 + text-align: center;
1394 + box-shadow: 0 -1px 0 rgba(80, 80, 80, 0.1);
1395 + z-index: 999;
1396 + // padding: 0.5rem 0;
1397 + padding-bottom: 0.5rem;
1398 +
1399 + .nav-bar-content {
1400 + display: flex;
1401 + overflow-x: scroll;
1402 + overflow-y: hidden;
1403 + -webkit-overflow-scrolling: touch;
1404 + position: relative;
1405 + }
1406 +
1407 + .item {
1408 + padding-top: 0.5rem;
1409 + color: #888;
1410 + width: 21.5%;
1411 + flex-shrink: 0;
1412 + padding-top: 1rem;
1413 + }
1414 +
1415 + .checked {
1416 + color: #965f13;
1417 + }
1418 +}
1419 +
1420 +.safe-route-wrapper {
1421 + position: absolute;
1422 + bottom: 2rem;
1423 + right: 1rem;
1424 + background-color: white;
1425 +}
1426 +
1427 +.operate-bar-wrapper {
1428 + position: fixed;
1429 + left: 20px;
1430 + bottom: 6rem;
1431 + width: 20px;
1432 + height: auto;
1433 + z-index: 100;
1434 +
1435 + .box-wrapper {
1436 + display: flex;
1437 + flex-direction: column;
1438 + align-items: center;
1439 + justify-content: center;
1440 +
1441 + .item {
1442 + position: relative;
1443 + text-align: center;
1444 + font-size: 0.85rem;
1445 + width: 2rem;
1446 + height: 2rem;
1447 + background-color: white;
1448 + margin-bottom: 1rem;
1449 + border-radius: 50%;
1450 + padding: 2.5px;
1451 + line-height: 2rem;
1452 + }
1453 + }
1454 +}
1455 +
1456 +.popup-wrapper {
1457 + margin-top: 1rem;
1458 +
1459 + .title {
1460 + font-size: 1.25rem;
1461 + margin-bottom: 0.85rem;
1462 + }
1463 +
1464 + .content {
1465 + line-height: 1.75;
1466 + font-size: 0.95rem;
1467 + }
1468 +}
1469 +
1470 +
1471 +.hideScrollBar::-webkit-scrollbar {
1472 + display: none;
1473 +}
1474 +
1475 +.hideScrollBar {
1476 + -ms-overflow-style: none;
1477 + overflow: -moz-scrollbars-none;
1478 +}
1479 +
1480 +.van-dialog__confirm,
1481 +.van-dialog__confirm:active {
1482 + color: #AB8F57;
1483 +}
1484 +
1485 +.walk-nav-text {
1486 + position: fixed;
1487 + bottom: 6rem;
1488 + left: 50%;
1489 + transform: translate(-50%, -50%);
1490 + z-index: 999;
1491 + background: rgba(86, 65, 23, 0.8);
1492 + color: white;
1493 + border-radius: 10px;
1494 + padding: 5px 12px;
1495 + font-size: 0.8rem;
1496 +}
1497 +</style>
1498 +@/common/map_data