hookehuyr

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

......@@ -15,6 +15,7 @@ declare module '@vue/runtime-core' {
ElButton: typeof import('element-plus/es')['ElButton']
ElInput: typeof import('element-plus/es')['ElInput']
ElOption: typeof import('element-plus/es')['ElOption']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElSelect: typeof import('element-plus/es')['ElSelect']
Floor: typeof import('./src/components/Floor/index.vue')['default']
InfoPopup: typeof import('./src/components/InfoPopup.vue')['default']
......
/*
* @Date: 2023-05-29 11:10:19
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-01-26 09:55:58
* @LastEditTime: 2025-02-10 16:04:07
* @FilePath: /map-demo/src/route.js
* @Description: 文件描述
*/
......@@ -84,4 +84,32 @@ export default [
title: '瓦片切图工具',
},
},
{
path: '/xyx',
component: () => import('@/views/xyx/index.vue'),
meta: {
title: '首页',
},
},
{
path: '/by',
component: () => import('@/views/by/map.vue'),
meta: {
title: '地图',
},
},
{
path: '/by/scan',
component: () => import('@/views/by/scan.vue'),
meta: {
title: '扫描',
},
},
{
path: '/by/info',
component: () => import('@/views/by/info.vue'),
meta: {
title: '详情页',
},
},
];
......
<!--
* @Date: 2024-09-14 17:48:55
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2024-09-21 23:04:40
* @FilePath: /map-demo/src/views/bieyuan/index.vue
* @Description: 文件描述
-->
<template>
<div class="index-page">
<div style="display: flex; flex-direction: column; align-items: center;">
<van-image
width="12rem"
height="12rem"
fit="contain"
src="https://cdn.ipadbiz.cn/bieyuan/map/icon/index_logo@3x.png"
/>
<div style="margin-top: 2rem; font-size: 0.95rem; letter-spacing: 5px; color: #47525F;">山水逢甘露,静心遇桃源</div>
</div>
<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>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
//import { } from '@/utils/generateModules.js'
//import { } from '@/utils/generateIcons.js'
//import { } from '@/composables'
const $route = useRoute();
const $router = useRouter();
const goTo = () => {
$router.push({
path: './map',
query: {
id: $route.query.id
}
});
// 进入标记
localStorage.setItem('first_in_bieyuan', 1);
}
onMounted(() => {
// 记录第一次进入页面,之后判断是否第一次进入,直接跳转到MAP页面
if (localStorage.getItem('first_in_bieyuan') === '1') {
$router.push({
path: './map',
query: {
id: $route.query.id
}
});
}
});
</script>
<style lang="less" scoped>
.index-page {
height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: center;
background: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5)),
url('https://cdn.ipadbiz.cn/bieyuan/map/MAP@3x.png');
background-size: contain;
}
</style>
<!--
* @Date: 2024-09-15 22:08:49
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-02-10 16:11:47
* @FilePath: /map-demo/src/views/by/info.vue
* @Description: 文件描述
-->
<template>
<div class="info-page">
<div class="info-header-wrapper">
<div v-if="showBack && page_details.banner?.length" style="position: absolute; top: 1rem; left: 0.5rem; z-index: 9;">
<van-icon name="arrow-left" color="white" size="1.75rem" @click="goBack()" />
</div>
<van-config-provider :theme-vars="themeVars">
<van-swipe class="my-swipe" indicator-color="#DD7850" lazy-render :autoplay="5000">
<van-swipe-item v-for="(image, index) in page_details.banner" :key="index" style="position: relative;">
<van-image fit="cover" width="100%" :height="img_height" :src="image" @click="onClickImg(index)" />
<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="">
</van-swipe-item>
</van-swipe>
</van-config-provider>
<div class="header-z"></div>
</div>
<div class="info-content-wrapper">
<div class="info-header">
<div style="display: flex; justify-content: space-between;">
<p class="info-title">{{ page_details.name }}</p>
<div style="display: flex;">
<div @click="onClickAudioList" style="margin-right: 0.75rem;">
<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" />
<van-icon v-else name="https://cdn.ipadbiz.cn/bieyuan/map/icon/%E8%AF%AD%E9%9F%B32@3x.png" size="1.65rem" />
</div>
<div v-if="page_details.path?.length > 1" @click="goTo()" class="info-btn">前往</div>
</div>
</div>
<div class="info-sub-title">{{ page_details.note }}</div>
</div>
<div id="tab-wrapper" style="margin-top: 0.5rem;">
<van-config-provider :theme-vars="themeVars">
<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>
<van-tab title="介 绍" v-if="page_details.introduction">
<div class="info-content">
<div id="introduction" v-html="page_details.introduction" style="padding: 0 1rem;"></div>
</div>
</van-tab>
<van-tab title="故 事" v-if="page_details.story">
<div class="info-content">
<div id="story" v-html="page_details.story" style="padding: 0 1rem;"></div>
</div>
</van-tab>
<van-tab title="体 验" v-if="page_details.experience">
<div class="info-content">
<div id="experience" v-html="page_details.experience" style="padding: 0 1rem;"></div>
</div>
<div v-if="page_details.experience_audio.length" class="audio-wrapper">
<div @click="toggleHandleAudio(item, index)" :class="['audio-item', play_audio_index === index ? 'click' : '']" v-for="(item, index) in page_details.experience_audio" :key="index">
<div>{{ item.description }}</div>
<van-icon @click.stop="stopAudio(item, index)" v-if="item.play" size="2rem" name="stop-circle-o" color="#DD7850" />
<van-icon v-else @click="playAudio(item, index)" size="2rem" name="https://cdn.ipadbiz.cn/bieyuan/map/icon/audio_icon.png" />
</div>
</div>
</van-tab>
</van-tabs>
</van-config-provider>
</div>
</div>
<!-- <div class="info-logo" :style="{ marginBottom: audio_list_height ? `${audio_list_height * 1.5}px` : '3rem' }">
<van-image width="3rem" height="3rem" fit="contain" src="https://cdn.ipadbiz.cn/bieyuan/map/icon/scan_logo.png" />
</div> -->
<van-toast v-model:show="show_toast" style="padding: 0">
<template #message>
<p style="padding: 0.5rem 1rem;">{{ toast_text }}</p>
</template>
</van-toast>
<van-image-preview v-model:show="show_preview" :images="preview_images" @change="onChange" doubleScale>
<template v-slot:index>第{{ index + 1 }}张</template>
</van-image-preview>
<van-back-top />
<audio-play-list :height="audio_list_height" :status="audio_status" @close="onCloseAudioList" @status="onStatusAudioList"></audio-play-list>
</div>
</template>
<script setup>
import { ref, watch, watchEffect } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { showImagePreview } from 'vant';
import { storeToRefs } from 'pinia'
import audioPlayList from '@/components/audioList.vue'
import { mainStore, useTitle } from '@/utils/generatePackage'
import wx from 'weixin-js-sdk';
import $ from 'jquery';
import { mapAPI } from '@/api/map.js'
const store = mainStore();
const { audio_status, audio_entity, audio_list_status, audio_list_entity } = storeToRefs(store);
const $route = useRoute();
const $router = useRouter();
const themeVars = ref({
swipeIndicatorInactiveBackground: '#fff',
swipeIndicatorMargin: '1.5rem',
tabFontSize: '0.95rem',
});
const props = defineProps({
info: Object,
height: Number
});
const page_details = ref({});
watch(
() => props.info,
(v) => {
if (v.details.length) {
page_details.value = { ...v.details[0], position: v.position, path: v.path };
// 获取浏览器可视范围的高度
$('.info-page').height(props.height + 'px');
}
}
)
const images = ref([
'https://cdn.ipadbiz.cn/bieyuan/map/swiper_img.png',
'https://cdn.ipadbiz.cn/bieyuan/map/Mix_20230612_201951.png',
'https://cdn.ipadbiz.cn/bieyuan/map/Mix_20240815_211927.png',
]);
const active = ref(0);
const play_audio_index = ref(null);
// const audioList = ref([{
// text: '5分钟观呼吸',
// 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',
// play: false,
// }, {
// text: '10分钟正念静坐',
// 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',
// play: false,
// }, {
// text: '15分钟正念静坐',
// 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',
// play: false,
// }])
const toggleHandleAudio = (item, index) => { // 切换播放或者暂停操作
if (item.play) {
stopAudio(item, index);
} else {
playAudio(item, index);
}
}
const playAudio = (item, index) => {
page_details.value.experience_audio.forEach(item => item.play = false);
audio.value.src = item.src;
// 后台有播放器运行时,先暂停
if (audio_list_status.value === 'play'){
audio_list_entity.value.pause();
}
play_audio_index.value = index;
let play_status = audio.value.play() // 播放
if (play_status) {
play_status.then(() => {
item.play = true;
// 存放到pinia里面控制
store.changeAudio(audio.value);
store.changeAudioSrc(audio.value.src);
store.changeAudioStatus('play');
}).catch((e) => {
// 失败
console.log('Operation is too fast, audio play fails')
})
}
}
const stopAudio = (item, index) => {
item.play = false;
audio.value.pause();
}
const audio = ref(new Audio());
const img_height = ref('15rem');
const scrollTop = ref(0);
onMounted(async () => {
// 通过ID查询到标记点详情
if (!props.info) {
let id = $route.query.id;
const { data } = await mapAPI({ i: id });
const raw_list = data.list[0].list; // 获取标记点列表
const marker_id = $route.query.marker_id;
const current_marker = raw_list.filter(item => item.id == marker_id)[0];
//
page_details.value = { ...current_marker.details[0], position: current_marker.position, path: current_marker.path };
// 富文本转义, 分割线样式转换
page_details.value.introduction = page_details.value.introduction?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
page_details.value.story = page_details.value.story?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
page_details.value.experience = page_details.value.experience?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
}
// 介绍栏目,图片点击事件
var imgs = $('#introduction').find('img');
// 图片点击事件
imgs.each(function(index, img) {
$(img).on('click', function (e) {
showImagePreview({
images: [$(img).attr('src')],
startPosition: 0,
showIndex: false,
onClose: () => {
// console.log('close');
}
})
});
// 图片有2个像素的圆角
$(img).css('border-radius', '5px');
});
// 获取屏幕宽度,设置高度4:3
// 获取屏幕的宽度
var screenWidth = $(window).width();
// 计算4:3比例的高度
var screenHeight = screenWidth * 3 / 4;
// 设置容器的高度
img_height.value = screenHeight + 'px';
//
nextTick(() => {
$('.info-page').on('scroll', (evt) => {
scrollTop.value = $(evt.currentTarget).scrollTop(); // 获取滚动的垂直距离
})
})
// 如果是从浮动窗口点击进来音频播放,自动打开
if ($route.query.source === 'click_audio') {
setTimeout(() => {
audio_list_height.value = (0.2 * window.innerHeight);
// 修改当前路由参数,避免刷新再次播放
$router.push({
query: {
...$route.query,
source: ''
}
});
}, 500);
}
// 地图标题
document.title = page_details.value.name;
// 微信分享
const shareData = {
title: page_details.value.name, // 分享标题
desc: '别院详情', // 分享描述
link: location.origin + location.pathname + location.hash, // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
imgUrl: '', // 分享图标
success: function () {
console.warn('设置成功');
}
}
// 分享好友(微信好友或qq好友)
wx.updateAppMessageShareData(shareData);
// 分享到朋友圈或qq空间
wx.updateTimelineShareData(shareData);
// 分享到腾讯微博
wx.onMenuShareWeibo(shareData);
});
// watchEffect(
// () => useTitle(page_details.value.name) // 地图标题
// )
onUnmounted(() => { // 离开页面时关闭音频播放
audio.value.pause();
store.changeAudioStatus('pause');
})
const audio_play = (src, index) => {
audio.value.src = src;
}
const outerStopAudio = () => {
audio.value.pause();
}
const emit = defineEmits(["closeFloat", 'route']);
const show_toast = ref(false);
const toast_text = ref('');
const goTo = () => { // 打开标记地图显示
// 没有关联导航提示
if (page_details.value.path.length <= 1) {
show_toast.value = true;
toast_text.value = '该标记点没有关联导航';
return;
}
//
if ($router.currentRoute.value.path === '/by/info') { // 详情页
$router.push({
path: '/by',
query: {
id: $route.query.id,
marker_id: $route.query.marker_id
}
})
} else { // 地图页
//
emit("closeFloat", false);
//
emit("route", {name: '参观路径', path: page_details.value.path});
}
}
const goBack = () => { // 返回首页
$router.push({
path: '/by',
query: {
id: $route.query.id,
}
})
}
const showBack = computed(() => $router.currentRoute.value.path === '/by/info');
const voicePause = () => {
audio.value.pause();
store.changeAudioStatus('pause');
}
const tabsRef = ref(null);
const clickTab = (evt) => { // 标签切换
tabsRef.value.resize();
nextTick(() => {
if (evt.title === '介 绍') { // 介绍
var imgs = $('#introduction').find('img');
}
if (evt.title === '故 事') { // 故事
var imgs = $('#story').find('img');
}
if (evt.title === '体 验') { // 体验
var imgs = $('#experience').find('img');
}
// 图片点击事件
imgs.each(function(index, img) {
$(img).on('click', function (e) {
showImagePreview({
images: [$(img).attr('src')],
startPosition: 0,
showIndex: false,
onClose: () => {
// console.log('close');
}
})
})
// 图片有5个像素的圆角
$(img).css('border-radius', '5px');
});
// 滚动高度大于tabs高度后才滚动到指定高度
let offsetTop = $('#tab-wrapper')[0].offsetTop;
if (scrollTop.value >= offsetTop) {
$('.info-page').scrollTop(offsetTop);
}
});
}
watch(
() => audio_status.value,
(v) => {
if (v === 'pause') {
voicePause();
page_details.value.experience_audio?.forEach(item => item.play = false);
}
},
{ immediate: true }
);
defineExpose({
outerStopAudio
})
const show_preview = ref(false);
const index = ref(0);
const preview_images = [];
const onChange = (newIndex) => {
index.value = newIndex;
};
const onClickImg = (idx) => {
showImagePreview({
images: page_details.value.banner,
startPosition: idx,
showIndex: true,
onClose: () => {
// console.log('close');
}
})
};
const show_shrink = computed(() => {
// 统计非空字段的个数
let filledFields = 0;
if (page_details.value.introduction) filledFields++;
if (page_details.value.story) filledFields++;
if (page_details.value.experience) filledFields++;
// 判断是否只有一个字段有值
if (filledFields === 1) {
return true;
}
return false;
});
const audio_list_height = ref(0);
const onClickAudioList = () => {
if ($('.info-page').height() < $(window).height()) { // 在浮动模式下点击音频列表
// 打开页面
$router.push({
path: '/by/info',
query: {
id: $route.query.id,
marker_id: props.info.id,
source: 'click_audio'
}
});
} else { // 详情页内点击
audio_list_height.value = (0.2 * window.innerHeight);
}
}
const onCloseAudioList = () => {
audio_list_height.value = 0;
}
// const show_audio = ref(true);
const onStatusAudioList = (status) => { // 音频列表组件,状态改变
page_details.value.experience_audio?.forEach(item => item.play = false);
audio.value.pause();
play_audio_index.value = null;
store.changeAudioStatus('pause');
// // 反馈播放列表数量为空时,隐藏图标
// if (status === 'none') {
// show_audio.value = false;
// }
}
</script>
<style lang="less">
.info-page {
background-color: #EBEBEB;
height: 100vh;
overflow: scroll;
position: relative;
.info-header-wrapper {
position: relative;
min-height: 2rem;
.header-z {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1rem;
// box-shadow: rgba(241, 242, 248, 0.6) 0px -3px 25px 15px;
// background-color: #f7f7f7;
background-color: #fff;
margin: 0 1rem;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
}
}
.info-content-wrapper {
box-shadow: 0px -3px 6px 0 rgba(241, 242, 248, 0.8);
margin: 1rem;
margin-top: 0;
// padding: 1rem;
border-bottom-left-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
background-color: white;
.info-header {
padding: 1rem 2rem 0;
// display: flex;
// justify-content: space-between;
// align-items: center;
.info-title {
font-size: 1.25rem;
margin-bottom: 0.5rem;
}
.info-sub-title {
font-size: 0.85rem;
color: #A0A8B1;
line-height: 1.75;
}
.info-btn {
width: 3rem;
height: 1.5rem;
border: 1px solid #DD7850;
color: #DD7850;
border-radius: 0.8rem;
font-size: 0.85rem;
text-align: center;
line-height: 1.5rem;
}
}
.info-content {
color: #47525F;
padding: 1rem;
line-height: 1.75;
p {
line-height: 1.75;
// padding: 0 0.85rem;
text-align: justify;
img {
width: 100%;
}
}
}
.audio-wrapper {
padding: 1rem;
.audio-item {
color: #47525F;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background-color: #FFF;
border-radius: 0.25rem;
margin: 1rem;
box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1);
&.click {
border: 1px solid #DD7850;
}
.audio-icon {
width: 2rem;
height: 2rem;
background-image: url('https://cdn.ipadbiz.cn/bieyuan/map/icon/audio_icon.png'); /* 使用上传的图标 */
background-size: cover;
&.click {
animation: pulse 1.5s infinite;
}
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
}
}
}
.info-logo {
display: flex;
justify-content: center;
margin: 3rem;
}
.van-tabs__wrap {
border-bottom: 1px solid #F3F3F3;
}
.van-tabs__nav--line.van-tabs__nav--shrink {
padding-left: 2rem;
}
}
.van-back-top {
background-color: #DD7850;
}
</style>
<!--
* @Date: 2024-09-15 22:08:49
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-02-10 16:11:41
* @FilePath: /map-demo/src/views/by/info_w.vue
* @Description: 文件描述
-->
<template>
<div class="info-page">
<div>
<van-config-provider :theme-vars="themeVars">
<van-swipe class="my-swipe" indicator-color="#DD7850" lazy-render :autoplay="5000">
<van-swipe-item v-for="image in images" :key="image">
<van-image fit="cover" width="100%" height="13rem" :src="image" />
</van-swipe-item>
</van-swipe>
</van-config-provider>
</div>
<div class="info-content-wrapper">
<div class="info-header">
<div>
<p class="info-title">选佛场</p>
<p class="info-sub-title">南楼2层</p>
</div>
<div @click="goTo()" class="info-btn">前往</div>
</div>
<div class="van-hairline--bottom">
<van-tabs v-model:active="active" color="#DD7850" title-active-color="#DD7850" sticky>
<van-tab title="介 绍">
<div class="info-content">
<p style="line-height: 1.75; padding: 0 0.85rem; color: #47525F;">选佛场是一个宗教活动场所,集禅堂与讲堂功能于一体。其设计仿制古代石窟样式,把古代人修行的场所“石窟”搬进室内。禅堂内的释迦牟尼佛像,仿麦积山石窟第44号特窟“东方的微笑”的造型。禅堂设计自然古朴,匠心独运,星光、烛光、月光三光辉映,营造出“一个人不孤单,一千人不喧闹”的宁静祥和的氛围。禅堂可容纳千人,开展讲经、禅修、法会等多种活动,提供礼佛、供灯、静坐等体验。</p>
<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>
<div style="padding: 1rem;">
<div style="color: #DD7850;">•&nbsp;五方塔</div>
<div style="color: #47525F; margin-top: 1rem; line-height: 1.75;">禅堂外的草坪,安立着大小不一五座佛塔。信众可绕塔或悬挂风铃祝愿祈福,在肃静庄严的氛围中得到佛力加持,自净其意,心想事成。</div>
</div>
</div>
</van-tab>
<van-tab title="故 事">
<div style="padding: 0 1rem;">
<div style="padding: 1rem;">
<div style="color: #DD7850;">•&nbsp;选官何如选佛</div>
</div>
<div style="padding: 0 1rem;">
<van-image width="100%" height="11rem" fit="cover" src="https://cdn.ipadbiz.cn/bieyuan/map/Mix_20230612_201951.png" />
</div>
<div style="padding: 1rem;">
<p style="color: #47525F; line-height: 1.75;">过去把禅堂叫作选佛场,意思是选择作佛的场所。这个典故与丹霞禅师有关。 <br />他原本是一个秀才,赴京赶考的途中遇到一位禅师,这位禅师跟他讲,选官何如选佛?考官还不如成佛利益更大。世间功名如过眼云烟,即便追求得到也是暂时利益,执著于此就会烦恼重重,甚至不断造业。而学佛修行,考佛就是要成佛,成就生命永恒的福祉。这是永久的利益,尽未来际的利益。每个生命原本具备觉悟的潜质,具有无尽的功德宝藏,取之不尽用之不竭。成佛,可以断除一切迷惑烦恼,可以彻底地开发我们生命的潜质,全然觉醒,这个利益无量无边。丹霞禅师深具慧根,一经点拨,马上出家。</p>
</div>
<div class="van-hairline--bottom" style="margin: 0 1rem;"></div>
</div>
<div style="padding: 0 1rem;">
<div style="padding: 1rem;">
<div style="color: #DD7850;">•&nbsp;把洞窟搬进讲堂</div>
</div>
<div style="padding: 0 1rem;">
<van-image width="100%" height="11rem" fit="cover" src="https://cdn.ipadbiz.cn/bieyuan/map/Mix_20240815_211927.png" />
</div>
<div style="padding: 1rem;">
<p style="color: #47525F; line-height: 1.75;">洞窟,是传统的佛教建筑形式,最早在印度盛行,古代僧人喜欢在崇山峻岭的幽僻处开凿洞窟,遁世修行。选佛场集禅堂与讲堂的功能于一体,把洞窟搬进讲堂,既有回归佛教本怀的宁静温暖,又体现出融入泰宁岩穴文化的祥和之气。</p>
</div>
<div class="van-hairline--bottom" style="margin: 0 1rem;"></div>
</div>
</van-tab>
<van-tab title="体 验">
<div style="padding: 0 1rem;">
<div style="padding: 1rem;">
<div style="color: #DD7850;">•&nbsp;供灯</div>
<div style="color: #47525F; margin-top: 1rem; line-height: 1.75;">禅堂内自助供灯。</div>
</div>
<div style="padding: 0 1rem;">
<van-image width="100%" height="11rem" fit="cover" src="https://cdn.ipadbiz.cn/bieyuan/map/Mix_20240815_211927.png" />
</div>
<div class="van-hairline--bottom" style="margin: 0 1rem;"></div>
</div>
<div class="audio-wrapper">
<div :class="['audio-item', play_audio_index === index ? 'click' : '']" v-for="(item, index) in audioList" :key="index">
<div>{{ item.text }}</div>
<!-- <div :class="['audio-icon', play_audio_index === index ? 'click' : '']"></div> -->
<van-icon @click="stopAudio(item, index)" v-if="item.play" size="2rem" name="stop-circle-o" color="#DD7850" />
<van-icon v-else @click="playAudio(item, index)" size="2rem" name="https://cdn.ipadbiz.cn/bieyuan/map/icon/audio_icon.png" />
</div>
</div>
<div style="padding: 0 1rem;">
<img src="https://cdn.ipadbiz.cn/bieyuan/map/xcx.png" style="width: 100%;">
</div>
</van-tab>
</van-tabs>
</div>
</div>
<!-- <div style="display: flex; justify-content: center; margin: 3rem;">
<van-image
width="3rem"
height="3rem"
fit="contain"
src="https://cdn.ipadbiz.cn/bieyuan/map/icon/scan_logo.png"
/>
</div> -->
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { storeToRefs } from 'pinia'
import { mainStore } from '@/store';
const store = mainStore();
const { audio_status, audio_entity } = storeToRefs(store);
const $route = useRoute();
const $router = useRouter();
const themeVars = ref({
swipeIndicatorInactiveBackground: '#fff',
});
const images = ref([
'https://cdn.ipadbiz.cn/bieyuan/map/swiper_img.png',
'https://cdn.ipadbiz.cn/bieyuan/map/Mix_20230612_201951.png',
'https://cdn.ipadbiz.cn/bieyuan/map/Mix_20240815_211927.png',
]);
const active = ref(0);
const play_audio_index = ref(null);
const audioList = ref([{
text: '5分钟观呼吸',
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',
play: false,
}, {
text: '10分钟正念静坐',
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',
play: false,
}, {
text: '15分钟正念静坐',
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',
play: false,
}])
const playAudio = (item, index) => {
audioList.value.forEach(item => item.play = false);
audio.value.src = item.src;
play_audio_index.value = index;
let play_status = audio.value.play() // 播放
if (play_status) {
console.warn('start');
// if (audio_status.value === 'play') {
// audio_entity.value.pause();
// }
play_status.then(() => {
console.warn('success');
item.play = true;
// 存放到pinia里面控制
// store.changeAudioSrc(audio.value.src);
// store.changeAudioStatus('play');
}).catch((e) => {
// 失败
console.log('Operation is too fast, audio play fails')
})
}
}
const stopAudio = (item, index) => {
item.play = false;
audio.value.pause();
}
const audio = ref(new Audio());
onMounted(() => {
// 存放到pinia里面控制
store.changeAudio(audio.value);
// store.changeAudioStatus('pause');
})
onUnmounted(() => {
audio.value.pause();
store.changeAudioStatus('pause');
})
const audio_play = (src, index) => {
audio.value.src = src;
}
const outerStopAudio = () => {
audio.value.pause();
}
const emit = defineEmits(["closeFloat", 'route']);
const goTo = () => { // 打开标记地图显示
if ($router.currentRoute.value.path === '/by/info') { // 详情页
$router.push({
path: '/by',
query: {
id: $route.query.id,
marker_id: '12345'
}
})
} else { // 地图页
//
emit("closeFloat", false);
//
emit("route", 'marker_id');
}
}
const voicePause = () => {
audio.value.pause();
store.changeAudioStatus('pause');
}
watch(
() => audio_status.value,
(v) => {
if (v === 'pause') {
voicePause();
audioList.value.forEach(item => item.play = false);
}
},
{ immediate: true }
);
defineExpose({
outerStopAudio
})
</script>
<style lang="less">
.info-page {
background-color: #EBEBEB;
height: 100vh;
overflow: scroll;
position: relative;
.info-content-wrapper {
// position: absolute;
// top: 14.9rem;
margin: 1rem;
margin-top: 0;
// padding: 1rem;
border-radius: 0.5rem;
background-color: white;
.info-header {
padding: 1rem 2rem 0;
display: flex;
justify-content: space-between;
// align-items: center;
.info-title {
font-size: 1.25rem;
margin-bottom: 0.5rem;
}
.info-sub-title {
font-size: 0.85rem;
color: #A0A8B1;
}
.info-btn {
width: 3rem;
height: 1.5rem;
border: 1px solid #DD7850;
color: #DD7850;
border-radius: 0.8rem;
font-size: 0.85rem;
text-align: center;
line-height: 1.5rem;
}
}
.info-content {
padding: 1rem;
}
.audio-wrapper {
padding: 1rem;
.audio-item {
color: #47525F;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background-color: #FFF;
border-radius: 0.25rem;
margin: 1rem;
box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1);
&.click {
border: 1px solid #DD7850;
}
.audio-icon {
width: 2rem;
height: 2rem;
background-image: url('https://cdn.ipadbiz.cn/bieyuan/map/icon/audio_icon.png'); /* 使用上传的图标 */
background-size: cover;
&.click {
animation: pulse 1.5s infinite;
}
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
}
}
}
}
</style>
<!--
* @Date: 2023-05-19 14:54:27
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-02-10 16:15:37
* @FilePath: /map-demo/src/views/by/map.vue
* @Description: 公众地图主体页面
-->
<template>
<div ref="root" style="height: 100vh; position: relative; overflow: hidden;">
<div id="container"></div>
<div style="position: absolute; top: 2rem; right: 1rem; display: flex; flex-direction: column;">
<!-- <van-icon size="2rem" name="search" color="#DD7850" style="margin-bottom: 1rem;" /> -->
<van-image
width="2rem"
height="2rem"
fit="contain"
src="https://cdn.ipadbiz.cn/bieyuan/map/icon/NAV@3x.png"
/>
</div>
<div v-if="data_logo" style="position: absolute; top: 2rem; left: calc(50% - 1.5rem); opacity: 0.5;">
<van-image
width="3rem"
height="3rem"
fit="contain"
:src="data_logo"
/>
</div>
<div @click="scanQrcode" style="position: absolute; bottom: 1rem; left: calc(50% - 2.5rem);">
<van-image
width="5rem"
height="5rem"
fit="contain"
src="https://cdn.ipadbiz.cn/bieyuan/map/icon/scan@3x.png"
/>
</div>
<van-config-provider :theme-vars="themeVars">
<van-floating-panel v-model:height="info_height" :anchors="anchors" @height-change="onHeightChange">
<!-- <template #header>
<div class="custom-header">
<h3>自定义标题</h3>
<button @click="show = false">关闭</button>
</div>
</template> -->
<page-info ref="pageInfo" :info="itemInfo" :height="info_height" @close-float="onCloseFloat" @route="onRoute"></page-info>
<!-- <div v-if="showClose" @click="closeFloatPanel" class="close-float-panel">
<van-icon name="arrow-left" color="#FFF" size="1.5rem" />
</div> -->
</van-floating-panel>
</van-config-provider>
<div v-if="!show_walk_route" @click="removeSafeRoute({ name: '参观路径' })" class="walk-nav-text">
关闭步行导航
</div>
<van-dialog v-model:show="dialog_show" title="温馨提示">
<div style="padding: 1rem; text-align: center;">{{ dialog_text }}</div>
</van-dialog>
<!-- 背景音乐控制 -->
<!-- <audioBackground1></audioBackground1> -->
<div class="operate-bar-wrapper">
<div class="box-wrapper">
<div v-if="open_current_location" class="item" @click="handleLocation(true)">
<van-icon name="https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8Dloc@2x.png" size="1.5rem"
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);;" />
</div>
<div v-else class="item" @click="handleLocation(false)">
<van-icon name="https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8Dloc@2x.png" size="1.5rem"
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
</div>
</div>
</div>
<van-toast v-model:show="show_toast" style="padding: 0">
<template #message>
<p style="padding: 0.5rem 1rem;">{{ toast_text }}</p>
</template>
</van-toast>
</div>
</template>
<script>
import "@vant/touch-emulator";
// import { mapState } from 'vuex'
import coord from '@/common/map_data'
import my_router from '@/common/my_router'
import _ from 'lodash';
import $ from 'jquery';
import { useRect } from '@vant/use';
import { mapAPI } from '@/api/map.js'
import wx from 'weixin-js-sdk'
import pageInfo from '@/views/by/info.vue'
import audioBackground1 from '@/components/audioBackground1.vue'
import { mapState, mapActions } from 'pinia'
import { mainStore } from '@/store'
import { parseQueryString } from '@/utils/tools'
const GPS = {
PI: 3.14159265358979324,
x_pi: 3.14159265358979324 * 3000.0 / 180.0,
delta: function (lat, lon) {
var a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
var ee = 0.00669342162296594323; // ee: 椭球的偏心率。
var dLat = this.transformLat(lon - 105.0, lat - 35.0);
var dLon = this.transformLon(lon - 105.0, lat - 35.0);
var radLat = lat / 180.0 * this.PI;
var magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
var sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * this.PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * this.PI);
return {
'lat': dLat,
'lon': dLon
};
},
//WGS-84 to GCJ-02
gcj_encrypt: function (wgsLat, wgsLon) {
if (this.outOfChina(wgsLat, wgsLon))
return {
'lat': wgsLat,
'lon': wgsLon
};
var d = this.delta(wgsLat, wgsLon);
return {
'lat': wgsLat + d.lat,
'lon': wgsLon + d.lon
};
},
outOfChina: function (lat, lon) {
if (lon < 72.004 || lon > 137.8347)
return true;
if (lat < 0.8293 || lat > 55.8271)
return true;
return false;
},
transformLat: function (x, y) {
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));
ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0;
return ret;
},
transformLon: function (x, y) {
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0;
return ret;
}
};
export default {
components: { pageInfo, audioBackground1 },
data() {
return {
map: '',
geolocation: '',
current_lng: '',
current_lat: '',
dialog_show: false,
dialog_text: '',
location_marker: '',
itemInfo: {},
navBarList: [],
navList: [],
navKey: '',
markerSum: [], // marker合集
mapTiles: [],
data_center: [], // 接口获取-地图中心点
data_zoom: '', // 接口获取-地图默认缩放
data_zooms: '', // 接口获取-地图默认缩放范围
data_rotation: 0, // 接口获取-地图旋转角度
data_paths: {}, // 接口获取-地图导航路径
data_path_list: [], // 接口获取-地图导航路径
info_height: 0,
anchors: [0, (0.65 * window.innerHeight), (1 * window.innerHeight)],
themeVars: {
floatingPanelHeaderHeight: 0,
floatingPanelBorderRadius: '1.25rem'
},
showClose: false,
markerStyle2: { // 选中
//设置文本样式,Object 同 css 样式表
"padding": ".5rem .2rem .5rem .2rem",
// "margin-bottom": "1rem",
"border-color": "#DD7850",
"border-radius": ".25rem",
"background-color": "#FFF",
// "width": "1rem",
// "border-width": 0,
// "box-shadow": "0 2px 6px 0 rgba(114, 124, 245, .5)",
// "text-align": "center",
"font-size": "0.8rem",
"color": "#DD7850",
"writing-mode": "vertical-rl",
"text-orientation": "mixed",
"display": "flex",
"justify-content": "center",
"align-items": "center",
},
markerStyle1: { // 未选中
//设置文本样式,Object 同 css 样式表
"padding": ".5rem .2rem .5rem .2rem",
// "margin-bottom": "1rem",
"border-color": "#fcfbfa",
"border-radius": ".25rem",
"background-color": "#DD7850",
// "width": "1rem",
// "border-width": 0,
// "box-shadow": "0 2px 6px 0 rgba(114, 124, 245, .5)",
// "text-align": "center",
"font-size": "0.8rem",
"color": "white",
"writing-mode": "vertical-rl",
"text-orientation": "mixed",
"display": "flex",
"justify-content": "center",
"align-items": "center",
},
current_safe_route: [],
route_safe_marker: [],
show_walk_route: true,
open_current_location: true,
show_toast: false,
toast_text: '',
data_logo: '',
data_layers: [],
}
},
async mounted() {
const code = this.$route.query.id;
const { data } = await mapAPI({ i: code });
this.navBarList = data.list; // 底部导航条
this.mapTiles = data.level; // 获取图层
this.navKey = data.list[0]['id']; // 默认选中 第一个 id
this.navList = data.list.filter(item => item.id === this.navKey)[0]['list']; // 返回默认选中项的实体信息
this.data_center = data.map.center.map(item => Number(item)); // 地图中心点
this.data_zoom = data.map.zoom; // 地图默认缩放
this.data_rotation = data.map.rotation; // 地图旋转角度
this.data_zooms = data.map.zooms.map(item => Number(item)); // 地图默认缩放范围
this.data_paths = data.map.path ? data.map.path : {}; // 地图默认导航路径
this.data_logo = data.map.map_logo ? data.map.map_logo : ''; // 地图logo
if (data.map.map_layers) { // 地图默认图层
if (data.map.map_layers === 'satellite') { // 卫星和路网
this.data_layers = [new AMap.TileLayer.Satellite(), new AMap.TileLayer.RoadNet()]
} else { // 平面图
this.data_layers = [];
}
}
if (data.map.path) {
for (const key in data.map.path) {
const element = data.map.path[key];
this.data_path_list.push({
name: key,
path: element,
status: true
})
}
}
// 地图标题
document.title = data.map.map_title;
// 微信分享
const shareData = {
title: data.map.map_title, // 分享标题
desc: '别院地图', // 分享描述
link: location.origin + location.pathname + location.hash, // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
imgUrl: '', // 分享图标
success: function () {
console.warn('设置成功');
}
}
// 分享好友(微信好友或qq好友)
wx.updateAppMessageShareData(shareData);
// 分享到朋友圈或qq空间
wx.updateTimelineShareData(shareData);
// 分享到腾讯微博
wx.onMenuShareWeibo(shareData);
// 初始化地图
this.initMap();
// this.setMapBoundary();
// 使用之前获取当前地址,判断当前是否能够获取经纬度
wx.getLocation({
type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
success: (res) => {
var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
var speed = res.speed; // 速度,以米/每秒计
var accuracy = res.accuracy; // 位置精度
this.current_lng = GPS.gcj_encrypt(latitude, longitude).lon;
this.current_lat = GPS.gcj_encrypt(latitude, longitude).lat;
},
});
// 设置贴片地图
this.setTitleLayer();
//
// setTimeout(() => {
// this.info_height = (0.5 * window.innerHeight);
// // 浮动面板样式
// $('.van-floating-panel__content').css('borderRadius', '1.5rem');
// }, 2000);
},
watch: {
// // 监听 $route 对象的 query 属性
// '$route.query': {
// handler(newQuery, oldQuery) {
// if (newQuery.marker_id) {
// }
// },
// immediate: true, // 设置为 true,确保在初始化时也执行一次 handler
// deep: true // 如果 query 是嵌套对象,可以设置 deep 监听深层变化
// }
},
computed: {
...mapState(mainStore, ['audio_entity', 'audio_src', 'audio_status'])
},
methods: {
...mapActions(mainStore, ['changeAudio', 'changeAudioSrc', 'changeAudioStatus']),
initMap() {
// 初始化地图
this.map = new AMap.Map('container', {
viewMode: '2D', // 设置地图模式
turboMode: false,
showIndoorMap: false,
defaultCursor: 'pointer', // 地图默认鼠标样式
showBuildingBlock: false, // 是否展示地图 3D 楼块
zooms: this.data_zooms, // 地图显示的缩放级别范围, 默认为 [2, 20] ,取值范围 [2 ~ 30]
showLabel: true, // 是否展示地图文字和 POI 信息
zoom: this.data_zoom, // 设置地图显示的缩放级别
pitch: 0, // 俯仰角度,默认 0,最大值根据地图当前 zoom 级别不断增大,2D地图下无效 。
rotation: this.data_rotation, // 地图顺时针旋转角度,取值范围 [0-360] ,默认值:0
center: this.data_center, // 设置地图中心点坐标
forceVector: false,
// rotateEnable: true,
layers: this.data_layers,
features: ['bg', 'road'], // 设置地图上显示的元素种类
animateEnable: false, // 地图平移过程中是否使用动画
resizeEnable: true,
});
// 添加地图点击事件
this.map.on("click", this.showInfoClick);
// 加载景点图层
this.loadMaker(this.navKey);
//
this.map.setRotation(this.data_rotation, true);
},
loadMaker(id) {
var zoomStyleMapping = { 14: 0, 15: 0, 16: 0, 17: 0, 18: 0, 19: 0, 20: 0 };
const entity_info = this.navBarList.filter(item => item.id === id)[0]['list'];
this.markerSum = [];
_.each(entity_info, (x, i) => {
let marker_icon = '';
if (entity_info[i].window_type === 'warn' && entity_info[i].details.length === 1) { // 如果是预警类型并且内部预警项目只有一个取details第一个icon
marker_icon = entity_info[i].details[0]['icon'];
} else {
marker_icon = entity_info[i].icon;
}
if (entity_info[i]?.writing_mode === 'vertical') { // 标题文字垂直
let textMarker = new AMap.Text({
zooms: [18, 20], // 点标记显示的层级范围,超过范围不显示。
text: entity_info[i].name, //标记显示的文本内容
anchor: "center", //设置文本标记锚点位置
// draggable: true, //是否可拖拽
// cursor: "pointer", //指定鼠标悬停时的鼠标样式。
// angle: 10, //点标记的旋转角度
style: this.markerStyle1,
position: entity_info[i].position, //点标记在地图上显示的位置
});
textMarker.setMap(this.map); //将文本标记设置到地图上
this.markerSum.push(textMarker);
if (clickListener1) {
textMarker.off('click', clickListener)
}
// 绑定景点的点击事件 - 文字出现才能触发
var clickListener1 = textMarker.on('click', (e) => {
// 还原样式
this.markerSum.forEach(item => {
if (e.target.hS !== item.hS) {
// 修改文本的样式
item.setStyle(this.markerStyle2);
}
})
// 修改文本的样式
e.target.setStyle(this.markerStyle1);
// 修改文本内容
// textMarker.setText('样式已修改');
//
// console.warn(e);
this.itemInfo = entity_info[i];
// 详情为空提示
if (!this.itemInfo.details.length) {
this.show_toast = true;
this.toast_text = '该景点暂无详情'
return;
}
// 打开浮动面板
this.info_height = (0.65 * window.innerHeight);
// 浮动面板样式
$('.van-floating-panel__content').css('borderRadius', '1.25rem');
$('.van-floating-panel').css('boxShadow', '0 0 15px black');
// 定位到当前位置中心
this.map.setZoomAndCenter(this.zoom, this.itemInfo.position);
// 获取地图容器的高度
const mapHeight = this.map.getSize().height;
// 计算需要向上移动的像素值,比如向上移动地图高度的一半左右
const offsetY = -mapHeight / 3.5;
// 使用 panBy 方法进行视图偏移
this.map.panBy(0, offsetY);
})
}
// TODO: 获取详情定位信息用来导航
// 导航路径
let marker_id = this.$route.query.marker_id;
if (marker_id) {
this.$nextTick(() => {
let marker = this.navBarList[0]['list'].filter(item => item.id == marker_id)
let path = marker[0].path;
this.addSafeRoute({name: '参观路径', path});
// 获取当前 URL 的查询参数
let query = { ...this.$route.query };
// 删除 marker_id 参数
delete query.marker_id;
// 使用 Vue Router 更新 URL,并且不刷新页面
this.$router.replace({ query });
});
}
});
this.map.add(this.markerSum);
//
// setTimeout(() => {
// // 获取定位打标记
// this.setLocation();
// }, 1000);
},
isPointInRing() { // 是否在景区范围
let isPointInRing = AMap.GeometryUtil.isPointInRing([this.current_lng, this.current_lat], [
[117.044223,26.835105], [117.044227,26.842448], [117.0552,26.842452], [117.055195,26.8351]
]);
return isPointInRing
},
setLocation() { // 开启定位服务
// 获取失败
if (!this.current_lng || !this.current_lat) {
this.dialog_show = true;
this.dialog_text = '获取经纬度失败';
}
this.getLocation();
},
getLocation() { // 获取经纬度
// PC端无法获取定位
// 微信获取地址
wx.getLocation({
type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
success: (res) => {
var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
var speed = res.speed; // 速度,以米/每秒计
var accuracy = res.accuracy; // 位置精度
this.current_lng = GPS.gcj_encrypt(latitude, longitude).lon;
this.current_lat = GPS.gcj_encrypt(latitude, longitude).lat;
// 判断是否在范围内
if (!this.isPointInRing()) {
this.dialog_show = true;
this.dialog_text = '您不在景区范围内';
} else {
// 使用纠正偏移后的地址,打一个定位标记
this.location_marker = new AMap.LabelMarker({
icon: {
image: 'https://cdn.ipadbiz.cn/bieyuan/map/icon/Group%2034@3x.png',
anchor: 'bottom-center',
size: [65, 65],
},
position: new AMap.LngLat(this.current_lng, this.current_lat), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
});
this.map.add(this.location_marker);
// 定位到当前位置中心
this.map.setZoomAndCenter(this.zoom, [this.current_lng, this.current_lat]);
}
},
complete: () => {
// 获取失败
if (!this.current_lng || !this.current_lat) {
this.dialog_show = true;
this.dialog_text = '获取经纬度失败';
}
},
});
},
setZoom(type) { // 设置放大缩小地图
const zoom = this.map.getZoom();
if (type === 'plus') {
this.map.setZoom(zoom + 1)
}
if (type === 'minus') {
this.map.setZoom(zoom - 1)
}
},
computedMapSource(x, y, z) { // 根据图层信息生成图层实际地址
for (const id in this.mapTiles) {
if (z == id) {
const scope = this.mapTiles[id];
return scope[`${x}-${y}`]
}
}
},
setTitleLayer() { // 生成瓦片图
// 获取瓦片图渲染范围
function getFirstProperty(obj) {
for (var prop in obj) {
return prop;
}
}
function getLastProperty(obj) {
var props = [];
for (var prop in obj) {
props.push(prop);
}
return props[props.length - 1];
}
let obj_scope = {};
for (const key in this.mapTiles) {
const element = this.mapTiles[key];
let first = getFirstProperty(element).split('-');
let last = getLastProperty(element).split('-');
obj_scope[key] = {
x: [first[0], last[0]],
y: [first[1], last[1]]
}
}
const _this = this;
var layer = new AMap.TileLayer.Flexible({
cacheSize: 50,
opacity: 1,
zIndex: 100,
createTile: function (x, y, z, success, fail) {
// 控制地图等级显示图片范围-过滤不显示的图层渲染
for (const id in obj_scope) {
if (z == id) {
const scope = obj_scope[id];
if (x < scope.x[0] || x > scope.x[1]) {
fail()
return;
}
if (y < scope.y[0] || y > scope.y[1]) {
fail()
return;
}
}
}
var img = document.createElement('img');
img.onload = function () {
success(img)
};
img.crossOrigin = "anonymous";// 必须添加,同时图片要有跨域头
img.onerror = function () {
fail()
};
// img.src = `images/tiles/${z}/${x}_${y}.png`;
img.src = _this.computedMapSource(x, y, z);
},
});
this.map.addLayer(layer);
// Canvas作为切片
var layer1 = new AMap.TileLayer.Flexible({
// tileSize: 128,
cacheSize: 300,
zIndex: 200,
createTile: function (x, y, z, success, fail) {
var c = document.createElement('canvas');
c.width = c.height = 256;
var cxt = c.getContext("2d");
cxt.font = "15px Verdana";
cxt.fillStyle = "#ff0000";
cxt.strokeStyle = "#FF0000";
cxt.strokeRect(0, 0, 256, 256);
cxt.fillText('(' + [x, y, z].join(',') + ')', 10, 30);
// 通知API切片创建完成
success(c);
}
});
// layer1.setMap(this.map);
// 只显示相应区域,移动会回到选定范围
// this.lockMapBounds()
},
// 限制地图范围
lockMapBounds() {
// var bounds = this.map.getBounds();
var myBounds = new AMap.Bounds( // 移动范围,对角线
[117.04384,26.833629],
[117.055975,26.843652]
);
this.map.setLimitBounds(myBounds);
let list =[ // 四个角,覆盖填充范围
[117.04421,26.833875],
[117.045012,26.842089],
[117.054749,26.84219],
[117.056013,26.83387]
]
// 隐藏边界以外的区域
let outer = [
new AMap.LngLat(-360, 90, true),
new AMap.LngLat(-360, -90, true),
new AMap.LngLat(360, -90, true),
new AMap.LngLat(360, 90, true),
] // 遮盖填充反向
let pathArray = [
outer,
list
]
var polygon = new AMap.Polygon({
pathL: pathArray,
strokeColor: "#fcfbf9",
strokeWeight: 2,
fillColor: "#fcfbf9",
fillOpacity: 1,
})
polygon.setPath(pathArray)
this.map.add(polygon)
},
showInfoClick(e) {
// console.log(e);
var zoom = this.map.getZoom(); //获取当前地图级别
// var text =
// "您在 [" +
// e.lnglat.getLng() +
// "," +
// e.lnglat.getLat() +
// "] 的位置单击了地图!当前层级" +
// zoom;
var text =
"[" +
e.lnglat.getLng() +
"," +
e.lnglat.getLat() +
"],"
console.log(text);
/*// 关闭浮动面板
this.info_height = 0;
$('.van-floating-panel').css('boxShadow', 'none');
// 还原样式
this.resetMarkStyle();*/
},
scanQrcode() { // 扫码跳转详情页
wx.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: ["qrCode","barCode"], // 可以指定扫二维码还是一维码,默认二者都有
success: (res) => {
var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
let id = parseQueryString(result).id;
let marker_id = parseQueryString(result).marker_id;
// 跳转详情页
this.$router.push({
path: '/by/info',
query: {
id,
marker_id
}
})
}
});
// 识别率太低
// this.$router.push({
// path: '/by/scan'
// })
},
onHeightChange ({ height }) { // 监听浮动面板高度变化
if (height > window.innerHeight * 0.6) {
// // 浮动面板样式
// $('.van-floating-panel__content').css('borderRadius', '0');
// this.showClose = true;
// 清空设置
// this.changeAudio('');
// this.changeAudioStatus('pause');
//
this.$router.push({
path: '/by/info',
query: {
id: this.$route.query.id,
marker_id: this.itemInfo.id
}
})
} else {
$('.van-floating-panel__content').css('borderRadius', '1.25rem');
$('.van-floating-panel').css('boxShadow', 'none');
this.showClose = false;
}
},
closeFloatPanel () {
this.info_height = (0.65 * window.innerHeight);
$('.van-floating-panel__content').css('borderRadius', '1.25rem');
this.showClose = false;
// 关闭音频
this.$refs.pageInfo.outerStopAudio();
},
resetMarkStyle () {
this.markerSum.forEach(item => {
item.setStyle(this.markerStyle1);
})
},
onCloseFloat () {
this.info_height = 0;
$('.van-floating-panel__content').css('borderRadius', '1.25rem');
$('.van-floating-panel').css('boxShadow', 'none');
this.resetMarkStyle();
},
addSafeRoute({name, path}) { // 新增路径
// 获取对象的第一个键和值
// let firstKey = Object.keys(this.data_paths)[0];
// let firstValue = this.data_paths[firstKey];
// 行动路线
// var path = [
// [120.587645, 31.314833],
// [120.587709, 31.314338],
// [120.588211, 31.314377],
// ];
// console.warn(firstValue);
// var path = firstValue;
// 生成折线地图路径
let current_safe_route = new AMap.Polyline({
path,
isOutline: true,
outlineColor: '#179FB1',
borderWeight: 1,
strokeColor: '#179FB1',
strokeOpacity: 1,
strokeWeight: 3,
// 折线样式还支持 'dashed'
strokeStyle: 'solid',
// strokeStyle是dashed时有效
strokeDasharray: [10, 5],
lineJoin: 'round',
lineCap: 'round',
zIndex: 50
})
this.map.add([current_safe_route]);
this.current_safe_route.push({
key: name,
path: current_safe_route
})
// 设置起始点标记
var marker1 = new AMap.Marker({
icon: new AMap.Icon({
image: 'https://cdn.ipadbiz.cn/bieyuan/map/icon/Ellipse%2013@3x.png',
size: new AMap.Size(15, 15),
// 图标所用图片大小
imageSize: new AMap.Size(15, 15),
// 图标取图偏移量
imageOffset: new AMap.Pixel(0, 0)
}),
position: new AMap.LngLat(path[0][0], path[0][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
anchor: 'bottom-center',
offset: new AMap.Pixel(0, 0)
});
// marker1.setLabel({
// direction: 'right',
// offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
// content: "<div class='info'>起点</div>", //设置文本标注内容
// });
var marker2 = new AMap.Marker({
icon: new AMap.Icon({
image: 'https://cdn.ipadbiz.cn/bieyuan/map/icon/Ellipse%2013@3x.png',
size: new AMap.Size(15, 15),
// 图标所用图片大小
imageSize: new AMap.Size(15, 15),
// 图标取图偏移量
imageOffset: new AMap.Pixel(0, 0)
}),
position: new AMap.LngLat(path[path.length - 1][0], path[path.length - 1][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
anchor: 'bottom-center',
offset: new AMap.Pixel(0, 0)
});
// marker2.setLabel({
// direction: 'right',
// offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
// content: "<div class='info'>终点</div>", //设置文本标注内容
// });
// 新增逃生路线标记
// this.route_safe_marker = [marker1, marker2]
// this.map.add(this.route_safe_marker);
// 新增逃生路线标记
let route_safe_marker = [marker1, marker2]
this.map.add(route_safe_marker);
this.route_safe_marker.push({
key: name,
path: route_safe_marker
});
// 关闭导航提示
this.show_walk_route = false;
},
removeSafeRoute({name}) { // 移除地图路线
this.current_safe_route.forEach(item => {
if (item.key === name) {
this.map.remove([item.path]); // 删除地图折线
}
});
// this.map.remove(this.route_safe_marker); // 删除起始点标记
this.route_safe_marker.forEach(item => {
if (item.key === name) {
this.map.remove(item.path); // 删除起始点标记
}
});
// 关闭导航提示
this.show_walk_route = true;
},
onRoute (path) {
console.warn(path);
// 模拟新增路线
this.addSafeRoute(path);
// 定位到当前位置中心
this.map.setZoomAndCenter(this.zoom, this.data_center);
},
handleLocation(status) { // 打开/关闭 当前定位
if (status) {
this.setLocation()
this.open_current_location = false;
} else {
this.removeLocation()
this.open_current_location = true;
}
},
removeLocation() { // 移除定位标记
this.current_lng = '';
this.current_lat = '';
this.map.remove(this.location_marker); // 删除当前定位标记
},
}
}
</script>
<style lang="less">
#container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
// 遮挡地图logo
.amap-logo {
display: none!important;
visibility: hidden!important;
}
.amap-copyright {
display: none!important;
visibility: hidden!important;
}
/* 标记文字样式 */
.amap-marker-label {
padding: 0.25rem 0.5rem;
width: auto;
border: none;
border-radius: 2px;
background: rgba(86, 65, 23, 0.8);
color: white;
}
.amap-marker {
.amap-icon {
margin-top: 0.25rem;
}
}
.input-card {
display: flex;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
background-clip: border-box;
border-radius: .25rem;
width: 20rem;
border-width: 0;
border-radius: 0.4rem;
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
position: fixed;
top: 4rem;
right: 1rem;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
padding: 0.75rem 1.25rem;
}
.tool-bar-wrapper {
position: absolute;
left: 20px;
bottom: 8rem;
width: 20px;
}
.nav-bar-wrapper {
position: fixed;
bottom: 0;
left: 0;
height: 5.5rem;
width: 100%;
background-color: white;
text-align: center;
box-shadow: 0 -1px 0 rgba(80, 80, 80, 0.1);
z-index: 999;
// padding: 0.5rem 0;
padding-bottom: 0.5rem;
.nav-bar-content {
display: flex;
overflow-x: scroll;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
position: relative;
}
.item {
padding-top: 0.5rem;
color: #888;
width: 21.5%;
flex-shrink: 0;
padding-top: 1rem;
}
.checked {
color: #965f13;
}
}
.safe-route-wrapper {
position: absolute;
bottom: 2rem;
right: 1rem;
background-color: white;
}
.operate-bar-wrapper {
position: fixed;
left: 20px;
bottom: 6rem;
width: 20px;
height: auto;
z-index: 100;
.box-wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.item {
position: relative;
text-align: center;
font-size: 0.85rem;
width: 2rem;
height: 2rem;
background-color: white;
margin-bottom: 1rem;
border-radius: 50%;
padding: 2.5px;
line-height: 2rem;
}
}
}
.popup-wrapper {
margin-top: 1rem;
.title {
font-size: 1.25rem;
margin-bottom: 0.85rem;
}
.content {
line-height: 1.75;
font-size: 0.95rem;
}
}
.hideScrollBar::-webkit-scrollbar {
display: none;
}
.hideScrollBar {
-ms-overflow-style: none;
overflow: -moz-scrollbars-none;
}
.van-dialog__confirm,
.van-dialog__confirm:active {
color: #AB8F57;
}
.walk-nav-text {
position: fixed;
bottom: 6rem;
left: 50%;
transform: translate(-50%, -50%);
z-index: 9;
background: rgba(86, 65, 23, 0.8);
color: white;
border-radius: 10px;
padding: 5px 12px;
font-size: 0.8rem;
}
.close-float-panel {
position: absolute;
top: 1rem;
left: 1rem;
}
.custom-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background-color: #f7f8fa;
}
.van-floating-panel__header-bar {
background: none;
}
</style>
<!--
* @Date: 2024-09-15 11:45:13
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2024-09-18 11:15:56
* @FilePath: /map-demo/src/views/bieyuan/scan.vue
* @Description: 文件描述
-->
<template>
<div class="scan-page">
<!--<div class="scan_wrapper">
<div class="scan_box">
<!~~ 镜头区域 ~~>
<video ref="video" id="video" class="scan-video" autoplay></video>
</div>
<div @click="openScan" class="scan_text">点击扫描二维码查看详情</div>
</div>
<div class="sys_logo">
<van-image
width="3rem"
height="3rem"
fit="contain"
src="https://cdn.ipadbiz.cn/bieyuan/map/icon/scan_logo.png"
/>
</div>-->
<video ref="video" id="video" class="video vjs-fluid" autoplay></video>
<div v-show="tipShow" class="tip">{{tipMsg}}</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { useRoute, useRouter } from "vue-router";
const $route = useRoute();
const $router = useRouter();
import { BrowserMultiFormatReader } from "@zxing/library";
// import axios from 'axios'
const codeReader = ref(null);
const scanText = ref("");
// 初始化相机
const openScan = () => {
codeReader.value = new BrowserMultiFormatReader();
codeReader.value
.getVideoInputDevices()
.then((videoDevices) => {
let firstDeviceId = videoDevices[videoDevices.length - 1].deviceId;
if (videoDevices.length > 1) {
// 一般通过判断摄像头列表项里的 label 字段,'camera2 0, facing back' 字符串含有 'back' 和 '0',大部分机型是这样,如果有些机型没有,那就还是默认获取最后一个
firstDeviceId = videoDevices.find((el) => {
return el.label.indexOf("back") > -1 && el.label.indexOf("0") > -1;
})
? videoDevices.find((el) => {
return el.label.indexOf("back") > -1 && el.label.indexOf("0") > -1;
}).deviceId
: videoDevices[videoDevices.length - 1].deviceId;
}
decodeFromInputVideoFunc(firstDeviceId);
})
.catch((err) => {
console.log(err);
});
};
// 扫码
const decodeFromInputVideoFunc = (firstDeviceId) => {
// 使用摄像头扫描
codeReader.value.reset(); // 重置
codeReader.value.decodeFromInputVideoDeviceContinuously(
firstDeviceId,
"video",
(result, err) => {
if (result) {
alert(result.text);
console.log("扫码结果", result);
scanText.value = result.text;
if (scanText.value) {
// 识别成功关闭摄像头
codeReader.value.reset();
codeReader.value.stopContinuousDecodeFromInputVideoDevice();
}
}
}
);
};
onMounted(() => {});
</script>
<style lang="less" scoped>
.scan-page {
position: relative;
height: 100vh;
.scan_wrapper {
display: flex;
padding: 1rem;
flex-direction: column;
align-items: center;
padding-top: 5rem;
.scan_box {
padding: 1rem;
background-image: url('https://cdn.ipadbiz.cn/bieyuan/map/icon/scan_bg.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center center;
height: 20rem;
width: 90%;
position: relative;
overflow: hidden; /* 确保溢出内容被隐藏 */
box-sizing: border-box; /* 包括 padding 在内的尺寸计算 */
.scan-video {
object-fit: cover; /* 保持视频内容的比例,同时填满容器 */
position: absolute; /* 确保视频覆盖整个容器 */
top: -1rem;
left: -1rem;
width: calc(100% - 2rem); /* 视频宽度减去左右1rem的padding */
height: calc(100% - 2rem); /* 视频高度减去上下1rem的padding */
padding: 2rem;
object-fit: cover; /* 保持视频比例,同时填充容器 */
}
}
.scan_text {
font-size: 0.9rem;
color: #DD7850;
padding: 0.5rem 1rem;
margin: 1rem;
border: 1px solid #DD7850;
border-radius: 5px;
}
}
.sys_logo {
position: absolute;
bottom: 3rem;
left: calc(50% - 1.5rem);
}
}
</style>
<!--
* @Date: 2023-05-19 14:54:27
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-02-10 15:55:58
* @FilePath: /map-demo/src/views/xyx/index.vue
* @Description: 公众地图主体页面
-->
<template>
<div ref="root" style="height: 100vh; position: relative; overflow: hidden;">
<div id="container"></div>
<div class="nav-bar-wrapper">
<div class="hideScrollBar nav-bar-content">
<div v-for="(item, index) in navBarList" :key="index" :class="[isActive === index ? 'checked' : '', 'item']"
@click="setNavLayer(item, index)">
<van-icon :name="isActive === index ? item.icon[1] : item.icon[0]" size="2rem" /><br />
<span style="font-size: 0.85rem; margin-top: 0rem; display: inline-block;">{{ item.name }}</span>
</div>
<div style="width: 4rem;flex-shrink: 0;"></div>
<div style="position: fixed; right: 0; background-color: white; height: 5.5rem; width: 4rem;">
<div style="padding-top: 40%;">
<van-icon v-if="!show_nav_popup" name="arrow-up" @click="handleNavPopup" size="1.15rem" />
<van-icon v-else name="arrow-down" @click="handleNavPopup" size="1.15rem" />
</div>
</div>
</div>
<van-popup v-model:show="show_nav_popup" position="bottom" duration="0" :overlay="false"
:style="{ padding: '1rem', bottom: '6rem' }">
<div style="text-align: left;">
<div v-for="(item, index) in navList" :key="index" @click="handleNavMarker(item)"
style="margin-bottom: 1rem; font-size: 1.15rem;">
<van-icon name="fire-o" color="#965f13" />&nbsp;&nbsp;<span style="color: #000;">{{ item.name }}</span>
</div>
</div>
</van-popup>
</div>
<div class="operate-bar-wrapper">
<div class="box-wrapper">
<div v-if="open_current_location" class="item" @click="handleLocation(true)">
<van-icon name="https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8Dloc@2x.png" size="1.5rem"
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);;" />
</div>
<div v-else class="item" @click="handleLocation(false)">
<van-icon name="https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8Dloc@2x.png" size="1.5rem"
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
</div>
<!-- <div class="item" @click="selectRoute">
<van-icon name="https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8Dloc@2x.png" size="1.5rem"
style="vertical-align: middle;" />
</div> -->
<div v-for="(item, index) in data_path_list" :key="index">
<div v-if="item.status" class="item" @click="handleSafeRoute(true, item)">
<van-icon name="https://cdn.ipadbiz.cn/xys/map/%E7%BA%BF%E8%B7%AF01.png" size="1.5rem"
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
<span style="position: absolute; left: 40px; display: inline-block; width: 10rem; text-align: left;">{{ item.name }}</span>
</div>
<div v-else class="item" @click="handleSafeRoute(false, item)">
<van-icon name="https://cdn.ipadbiz.cn/xys/map/%E7%BA%BF%E8%B7%AF02.png" size="1.5rem"
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
<span style="position: absolute; left: 40px; display: inline-block; width: 10rem; text-align: left;">{{ item.name }}</span>
</div>
</div>
</div>
</div>
<!-- <div v-if="!show_walk_route" @click="removeWalkRoute" class="walk-nav-text">
关闭步行导航
</div> -->
<div v-if="!show_walk_route" @click="removeNavRoute" class="walk-nav-text">
关闭步行导航
</div>
<!-- <van-popup v-model:show="show_popup" position="bottom" :overlay="true" teleport="body" :close-on-click-overlay="false" :style="{ padding: '1rem', height: '50%' }">
<van-icon name="cross" size="1.35rem" @click="show_popup = false" style="float: right; color: gray;" />
<div class="popup-wrapper">
<div class="title">
{{ popup_title }}
</div>
<div class="content" v-html="popup_content"></div>
<video-player ref="videoPlayer" style="width: 100%; height: 30vh; margin-top: 1rem;"
poster="https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100"
:src="video_src" class="video-player vjs-big-play-centered" controls :loop="true" :volume="0.6"></video-player>
</div>
</van-popup> -->
<van-dialog v-model:show="dialog_show" title="温馨提示">
<div style="padding: 1rem; text-align: center;">{{ dialog_text }}</div>
</van-dialog>
<!-- 自定义组件InfoWindow,初始时需要隐藏 -->
<!-- 隐藏不要使用v-if,因为我们需要渲染完成后的原生html结构作为信息框的dom对象使用 -->
<InfoWindow v-show="showInfoWindow" ref="infoWindow" :info-window="infoWindow" :info="itemInfo" :rect="rect"
@onLocation="infoWindowLocation" @onPlay="onPlay" @onPause="onPause"></InfoWindow>
<InfoWindowLite v-show="showInfoWindowLite" ref="infoWindowLite" :info-window="infoWindowLite" :info="itemInfo"
:rect="rect" @onLocation="infoWindowLocation"></InfoWindowLite>
<InfoWindowWarn v-show="showInfoWindowWarn" ref="infoWindowWarn" :info-window="infoWindowWarn" :info="itemInfo"
:rect="rect"></InfoWindowWarn>
<audioBackground></audioBackground>
<InfoPopup :show="showInfoPopup" :info-window="infoWindow" :info="itemInfo"
@onLocation="infoWindowLocation" @onPlay="onPlay" @onPause="onPause" @onClose="onClose"></InfoPopup>
<InfoPopupLite :show="showInfoLitePopup" :info="itemInfo" @onClose="onLiteClose"></InfoPopupLite>
<InfoPopupWarn :show="showInfoWarnPopup" :info="itemInfo" @onClose="onWarnClose"></InfoPopupWarn>
</div>
</template>
<script>
// import { mapState } from 'vuex'
import coord from '@/common/map_data'
import map_max from '@/common/max'
import map_alert from '@/common/alert'
import my_router from '@/common/my_router'
import _ from 'lodash';
import $ from 'jquery';
//引入定义的信息窗组件
import InfoWindow from '@/components/InfoWindow'
import InfoWindowLite from '@/components/InfoWindowLite'
import InfoWindowWarn from '@/components/InfoWindowWarn'
import audioBackground from '@/components/audioBackground'
import { useRect } from '@vant/use';
import { mapAPI } from '@/api/map.js'
import wx from 'weixin-js-sdk'
import InfoPopup from '@/components/InfoPopup'
import InfoPopupLite from '@/components/InfoPopupLite'
import InfoPopupWarn from '@/components/InfoPopupWarn'
const GPS = {
PI: 3.14159265358979324,
x_pi: 3.14159265358979324 * 3000.0 / 180.0,
delta: function (lat, lon) {
var a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
var ee = 0.00669342162296594323; // ee: 椭球的偏心率。
var dLat = this.transformLat(lon - 105.0, lat - 35.0);
var dLon = this.transformLon(lon - 105.0, lat - 35.0);
var radLat = lat / 180.0 * this.PI;
var magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
var sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * this.PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * this.PI);
return {
'lat': dLat,
'lon': dLon
};
},
//WGS-84 to GCJ-02
gcj_encrypt: function (wgsLat, wgsLon) {
if (this.outOfChina(wgsLat, wgsLon))
return {
'lat': wgsLat,
'lon': wgsLon
};
var d = this.delta(wgsLat, wgsLon);
return {
'lat': wgsLat + d.lat,
'lon': wgsLon + d.lon
};
},
outOfChina: function (lat, lon) {
if (lon < 72.004 || lon > 137.8347)
return true;
if (lat < 0.8293 || lat > 55.8271)
return true;
return false;
},
transformLat: function (x, y) {
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));
ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0;
return ret;
},
transformLon: function (x, y) {
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0;
return ret;
}
};
export default {
components: { InfoWindow, InfoWindowLite, InfoWindowWarn, audioBackground, InfoPopup, InfoPopupLite, InfoPopupWarn },
data() {
return {
map: '',
location_options: {
'showButton': true, // 是否显示定位按钮
'buttonPosition': 'LB', // 定位按钮的位置
/* LT LB RT RB */
'buttonOffset': new AMap.Pixel(10, 20), // 定位按钮距离对应角落的距离
'showMarker': true, // 是否显示定位点
'markerOptions': { // 自定义定位点样式,同Marker的Options
'offset': new AMap.Pixel(-18, -36),
'content': '<img src="https://a.amap.com/jsapi_demos/static/resource/img/user.png" style="width:36px;height:36px"/>'
},
'showCircle': true, // 是否显示定位精度圈
'circleOptions': { // 定位精度圈的样式
'strokeColor': '#0093FF',
'noSelect': true,
'strokeOpacity': 0.5,
'strokeWeight': 1,
'fillColor': '#02B0FF',
'fillOpacity': 0.25
},
enableHighAccuracy: true
},
geolocation: '',
current_lng: '',
current_lat: '',
current_route: '',
current_safe_route: [],
show_popup: false,
dialog_show: false,
dialog_text: '',
walk_route: '',
isActive: 0,
location_marker: '',
// route_marker: [],
route_safe_marker: [],
open_safe_route: true,
show_walk_route: true,
popup_title: '',
popup_content: '',
video_src: '',
show_nav_popup: false,
showInfoWindow: false,
showInfoWindowLite: false,
showInfoWindowWarn: false,
infoWindow: {},
infoWindowLite: {},
infoWindowWarn: {},
itemInfo: {},
navBarList: [],
rect: {},
navList: [],
navKey: '',
markerSum: [], // marker合集
// titleLayerSet: {
// 17: {
// x: [109439, 109441],
// y: [53519, 53521]
// },
// 18: {
// x: [218879, 218882],
// y: [107039, 107042]
// }
// },
defaultZoom: 18,
defaultCenter: [120.587382, 31.313900],
mapTiles: [],
open_current_location: true,
data_center: [], // 接口获取-地图中心点
data_zoom: '', // 接口获取-地图默认缩放
data_zooms: '', // 接口获取-地图默认缩放范围
data_rotation: 0, // 接口获取-地图旋转角度
data_paths: {}, // 接口获取-地图导航路径
data_path_list: [], // 接口获取-地图导航路径
showInfoPopup: false,
showInfoLitePopup: false,
showInfoWarnPopup: false,
data_layers: [],
}
},
async mounted() {
const code = this.$route.query.id;
const { data } = await mapAPI({ i: code });
// TAG:预警显示
// data.list = data.list.concat(map_alert);
this.navBarList = data.list; // 底部导航条
this.mapTiles = data.level; // 获取图层
this.navKey = data.list[0]['id']; // 默认选中 第一个 id
this.navList = data.list.filter(item => item.id === this.navKey)[0]['list']; // 返回默认选中项的实体信息
this.data_center = data.map.center.map(item => Number(item)); // 地图中心点
this.data_zoom = data.map.zoom; // 地图默认缩放
this.data_rotation = data.map.rotation; // 地图旋转角度
this.data_zooms = data.map.zooms.map(item => Number(item)); // 地图默认缩放范围
this.data_paths = data.map.path ? data.map.path : {}; // 地图默认导航路径
if (data.map.map_layers) { // 地图默认图层
if (data.map.map_layers === 'satellite') { // 卫星和路网
this.data_layers = [new AMap.TileLayer.Satellite(), new AMap.TileLayer.RoadNet()]
} else { // 平面图
this.data_layers = [];
}
}
if (data.map.path) {
for (const key in data.map.path) {
const element = data.map.path[key];
this.data_path_list.push({
name: key,
path: element,
status: true
})
}
}
// 初始化地图
this.initMap();
// this.setMapBoundary();
// 使用之前获取当前地址,判断当前是否能够获取经纬度
wx.getLocation({
type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
success: (res) => {
var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
var speed = res.speed; // 速度,以米/每秒计
var accuracy = res.accuracy; // 位置精度
this.current_lng = GPS.gcj_encrypt(latitude, longitude).lon;
this.current_lat = GPS.gcj_encrypt(latitude, longitude).lat;
},
});
// 设置贴片地图
this.setTitleLayer();
},
watch: {
// show_popup(val) {
// if (!val) {
// this.$nextTick(() => {
// // 弹框关闭时,暂停视频
// $('.vjs-tech')[0].pause();
// $('.vjs-tech')[0].currentTime = 0;
// })
// }
// },
showInfoWindow(val) {
if (val) {
// 元素的大小及其相对于视口的位置
const rect = useRect(this.$refs.root);
this.rect = rect;
}
},
showInfoWindowLite(val) {
if (val) {
// 元素的大小及其相对于视口的位置
const rect = useRect(this.$refs.root);
this.rect = rect;
}
},
showInfoWindowWarn(val) {
if (val) {
// 元素的大小及其相对于视口的位置
const rect = useRect(this.$refs.root);
this.rect = rect;
}
}
},
methods: {
initMap() {
// 初始化地图
this.map = new AMap.Map('container', {
viewMode: '2D', // 设置地图模式
turboMode: false,
showIndoorMap: false,
defaultCursor: 'pointer', // 地图默认鼠标样式
showBuildingBlock: false, // 是否展示地图 3D 楼块
zooms: this.data_zooms, // 地图显示的缩放级别范围, 默认为 [2, 20] ,取值范围 [2 ~ 30]
showLabel: true, // 是否展示地图文字和 POI 信息
zoom: this.data_zoom, // 设置地图显示的缩放级别
pitch: 0, // 俯仰角度,默认 0,最大值根据地图当前 zoom 级别不断增大,2D地图下无效 。
rotation: this.data_rotation, // 地图顺时针旋转角度,取值范围 [0-360] ,默认值:0
center: this.data_center, // 设置地图中心点坐标
forceVector: false,
// rotateEnable: true,
layers: this.data_layers,
features: ['bg', 'road'], // 设置地图上显示的元素种类
animateEnable: false, // 地图平移过程中是否使用动画
resizeEnable: true,
});
// 添加地图点击事件
this.map.on("click", this.showInfoClick);
// 加载景点图层
this.loadMaker(this.navKey);
//
this.map.setRotation(this.data_rotation, true);
},
setNavLayer({ id }, index) { // 选择地图图层显示
this.isActive = index;
this.navList = this.navBarList.filter(item => item.id === id)[0]['list']; // 返回默认选中项的实体信息
this.removeLayer();
this.loadMaker(id);
this.closeInfoWindow();
setTimeout(() => {
// 地图zooms调整
this.map.setZoom(this.defaultZoom);
this.map.setZoomAndCenter(this.defaultZoom, this.data_center);
}, 100);
this.removeNavRoute();
},
loadMaker(id) {
var zoomStyleMapping = { 14: 0, 15: 0, 16: 0, 17: 0, 18: 0, 19: 0, 20: 0 };
const entity_info = this.navBarList.filter(item => item.id === id)[0]['list'];
this.markerSum = [];
_.each(entity_info, (x, i) => {
let marker_icon = '';
if (entity_info[i].window_type === 'warn' && entity_info[i].details.length === 1) { // 如果是预警类型并且内部预警项目只有一个取details第一个icon
marker_icon = entity_info[i].details[0]['icon'];
} else {
marker_icon = entity_info[i].icon;
}
// var marker = new AMap.ElasticMarker({
// position: entity_info[i].position,
// zooms: [17, 19],
// styles: [{
// icon: {
// img: marker_icon, // 标记点图标
// size: [28, 28], // 可见区域的大小
// anchor: 'bottom-center', // 锚点
// fitZoom: 14, // 最合适的级别
// scaleFactor: 2, // 地图放大一级的缩放比例系数
// maxScale: 1.4, // 最大放大比例
// minScale: 0.8 // 最小放大比例
// },
// label: {
// content: entity_info[i].name,
// position: 'TM',
// // position: 'BM',
// // offset: new AMap.Pixel(0, 10),
// minZoom: 18
// }
// }, {
// icon: {},
// label: {}
// }],
// zoomStyleMapping: entity_info[i].zoom ? entity_info[i].zoom : zoomStyleMapping
// });
// if (clickListener) {
// marker.off('click', clickListener)
// }
// // 绑定景点的点击事件 - 文字出现才能触发
// var clickListener = marker.on('click', (e) => {
// // 不同弹框类型
// if (entity_info[i].window_type === 'normal') {
// this.positionMarker(entity_info[i])
// } else if(entity_info[i].window_type === 'lite') {
// this.positionLiteMarker(entity_info[i])
// } else if (entity_info[i].window_type === 'warn') {
// this.positionWarnMarker(entity_info[i])
// }
// })
// // marker合集
// this.markerSum.push(marker);
// 创建一个 文字 实例 - 适配竖型文字
// let writing_mode = 'vertical'; // 文字方向
if (entity_info[i]?.writing_mode === 'vertical') { // 标题文字垂直
var textMarker = new AMap.Text({
zooms: [18, 20], // 点标记显示的层级范围,超过范围不显示。
text: entity_info[i].name, //标记显示的文本内容
anchor: "center", //设置文本标记锚点位置
// draggable: true, //是否可拖拽
// cursor: "pointer", //指定鼠标悬停时的鼠标样式。
// angle: 10, //点标记的旋转角度
style: {
//设置文本样式,Object 同 css 样式表
"padding": ".5rem .2rem .5rem .2rem",
// "margin-bottom": "1rem",
// "border-radius": ".25rem",
"background-color": "#965f13",
// "width": "1rem",
// "border-width": 0,
// "box-shadow": "0 2px 6px 0 rgba(114, 124, 245, .5)",
// "text-align": "center",
"font-size": "0.8rem",
"color": "white",
"writing-mode": "vertical-rl",
"text-orientation": "mixed",
"display": "flex",
"justify-content": "center",
"align-items": "center",
},
position: entity_info[i].position, //点标记在地图上显示的位置
});
if (clickListener) {
textMarker.off('click', clickListener)
}
// 绑定景点的点击事件 - 文字出现才能触发
var clickListener = textMarker.on('click', (e) => {
// console.warn(e);
this.itemInfo = entity_info[i];
// 不同弹框类型
if (entity_info[i].window_type === 'normal') {
this.showInfoPopup = true;
} else if (entity_info[i].window_type === 'lite') {
this.showInfoLitePopup = true;
} else if (entity_info[i].window_type === 'warn') {
this.showInfoWarnPopup = true;
}
})
textMarker.setMap(this.map); //将文本标记设置到地图上
this.markerSum.push(textMarker);
} else { // 默认方向
// 创建一个 LabelMarker 实例
var labelMarker = new AMap.LabelMarker({
position: entity_info[i].position,
opacity: 1,
zIndex: 2,
opacity: 0.9,
// rank: 10,
icon: {
image: marker_icon,
anchor: 'bottom-center',
size: [36, 36],
},
text: {
zooms: [18, 20],
content: entity_info[i].name,
direction: 'top',
offset: [0, 5],
style: {
padding: [10, 10],
fontSize: 14,
fillColor: '#fff',
strokeWidth: 0,
backgroundColor: '#965f13',
borderWidth: 0,
}
}
});
if (clickListener) {
labelMarker.off('click', clickListener)
}
// 绑定景点的点击事件 - 文字出现才能触发
var clickListener = labelMarker.on('click', (e) => {
// 不同弹框类型
if (entity_info[i].window_type === 'normal') {
this.positionMarker(entity_info[i])
} else if (entity_info[i].window_type === 'lite') {
this.positionLiteMarker(entity_info[i])
} else if (entity_info[i].window_type === 'warn') {
this.positionWarnMarker(entity_info[i])
}
})
// 使用LabelsLayer包裹缩放地图时,图标不会自动显示和消失需要配置rank排序
// 创建一个 LabelsLayer 实例来承载 LabelMarker,[LabelsLayer 文档](https://lbs.amap.com/api/jsapi-v2/documentation#labelslayer)
var labelsLayer = new AMap.LabelsLayer({
collision: true,
});
// // 将 LabelMarker 实例添加到 LabelsLayer 上
labelsLayer.add(labelMarker);
// 将 LabelsLayer 添加到地图上
// this.map.add(labelsLayer);
this.markerSum.push(labelsLayer);
}
})
this.map.add(this.markerSum);
},
removeLayer() {
this.map.remove(this.markerSum);
},
// setMapBoundary() { // 地图描边
// new AMap.Polygon({
// cursor: 'pointer',
// bubble: true,
// 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]],
// map: this.map,
// fillOpacity: 0.5,
// strokeWeight: 1,
// fillColor: '#CFE7AA'
// });
// },
isPointInRing() { // 是否在景区范围
let isPointInRing = AMap.GeometryUtil.isPointInRing([this.current_lng, this.current_lat], [
[120.585111, 31.316084], [120.585111, 31.316084], [120.589488, 31.313197], [120.585422, 31.313005]
]);
return isPointInRing
},
setLocation() { // 开启定位服务
// 获取失败
if (!this.current_lng || !this.current_lat) {
this.dialog_show = true;
this.dialog_text = '获取经纬度失败';
}
this.getLocation();
// // this.map.addControl(this.geolocation); // 添加定位图标 自动跳转当前位置
// if (!this.current_lng || !this.current_lat) {
// this.getLocation()
// } else {
// // 判断是否在范围内
// if (!this.isPointInRing()) {
// this.dialog_show = true;
// this.dialog_text = '您不在景区范围内';
// } else {
// // this.current_lng = '120.587643'
// // this.current_lat = '31.314853'
// // 使用纠正偏移后的地址,打一个定位标记
// this.location_marker = new AMap.LabelMarker({
// icon: {
// image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A.png',
// anchor: 'bottom-center',
// size: [36, 36],
// },
// position: new AMap.LngLat(this.current_lng, this.current_lat), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
// });
// this.map.add(this.location_marker);
// // 定位到当前位置中心
// this.map.setZoomAndCenter(this.zoom, [this.current_lng, this.current_lat]);
// }
// }
},
handleLocation(status) { // 打开/关闭 当前定位
if (status) {
this.setLocation()
this.open_current_location = false;
} else {
this.removeLocation()
this.open_current_location = true;
}
},
removeLocation() { // 移除定位标记
this.current_lng = '';
this.current_lat = '';
this.map.remove(this.location_marker); // 删除当前定位标记
},
getLocation() { // 获取经纬度
// AMap.plugin(['AMap.Geolocation'], () => {
// this.geolocation = new AMap.Geolocation(this.location_options);
// this.geolocation.getCurrentPosition((status, result) => {
// if (status === 'complete') {
// let lat = result.position.lat;
// let lng = result.position.lng;
// // 行动路线
// if (lng && lat) {
// // this.current_lng = GPS.gcj_encrypt(lat, lng).lon;
// // this.current_lat = GPS.gcj_encrypt(lat, lng).lat;
// this.current_lng = lng;
// this.current_lat = lat;
// }
// } else {
// console.warn('获取失败');
// }
// })
// });
// PC端无法获取定位
// 微信获取地址
wx.getLocation({
type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
success: (res) => {
var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
var speed = res.speed; // 速度,以米/每秒计
var accuracy = res.accuracy; // 位置精度
this.current_lng = GPS.gcj_encrypt(latitude, longitude).lon;
this.current_lat = GPS.gcj_encrypt(latitude, longitude).lat;
// 判断是否在范围内
if (!this.isPointInRing()) {
this.dialog_show = true;
this.dialog_text = '您不在景区范围内';
} else {
// 使用纠正偏移后的地址,打一个定位标记
this.location_marker = new AMap.LabelMarker({
icon: {
image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A.png',
anchor: 'bottom-center',
size: [36, 36],
},
position: new AMap.LngLat(this.current_lng, this.current_lat), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
});
this.map.add(this.location_marker);
// 定位到当前位置中心
this.map.setZoomAndCenter(this.zoom, [this.current_lng, this.current_lat]);
}
},
complete: () => {
// 获取失败
if (!this.current_lng || !this.current_lat) {
this.dialog_show = true;
this.dialog_text = '获取经纬度失败';
}
},
});
},
setZoom(type) { // 设置放大缩小地图
const zoom = this.map.getZoom();
if (type === 'plus') {
this.map.setZoom(zoom + 1)
}
if (type === 'minus') {
this.map.setZoom(zoom - 1)
}
},
addSafeRoute({name, path}) { // 新增路径
// 获取对象的第一个键和值
// let firstKey = Object.keys(this.data_paths)[0];
// let firstValue = this.data_paths[firstKey];
// 行动路线
// var path = [
// [120.587645, 31.314833],
// [120.587709, 31.314338],
// [120.588211, 31.314377],
// ];
// console.warn(firstValue);
// var path = firstValue;
// 生成折线地图路径
let current_safe_route = new AMap.Polyline({
path,
isOutline: true,
outlineColor: '#fba601',
borderWeight: 1,
strokeColor: '#fba601',
strokeOpacity: 1,
strokeWeight: 3,
// 折线样式还支持 'dashed'
strokeStyle: 'solid',
// strokeStyle是dashed时有效
strokeDasharray: [10, 5],
lineJoin: 'round',
lineCap: 'round',
zIndex: 50
})
this.map.add([current_safe_route]);
this.current_safe_route.push({
key: name,
path: current_safe_route
})
// 设置起始点标记
var marker1 = new AMap.Marker({
icon: new AMap.Icon({
image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A.png',
size: new AMap.Size(40, 40),
// 图标所用图片大小
imageSize: new AMap.Size(40, 40),
// 图标取图偏移量
imageOffset: new AMap.Pixel(0, 0)
}),
position: new AMap.LngLat(path[0][0], path[0][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
anchor: 'bottom-center',
offset: new AMap.Pixel(0, 0)
});
marker1.setLabel({
direction: 'right',
offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
content: "<div class='info'>起点</div>", //设置文本标注内容
});
var marker2 = new AMap.Marker({
icon: new AMap.Icon({
image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A.png',
size: new AMap.Size(40, 40),
// 图标所用图片大小
imageSize: new AMap.Size(40, 40),
// 图标取图偏移量
imageOffset: new AMap.Pixel(0, 0)
}),
position: new AMap.LngLat(path[path.length - 1][0], path[path.length - 1][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
anchor: 'bottom-center',
offset: new AMap.Pixel(0, 0)
});
marker2.setLabel({
direction: 'right',
offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
content: "<div class='info'>终点</div>", //设置文本标注内容
});
// 新增逃生路线标记
// this.route_safe_marker = [marker1, marker2]
// this.map.add(this.route_safe_marker);
// 新增逃生路线标记
let route_safe_marker = [marker1, marker2]
this.map.add(route_safe_marker);
this.route_safe_marker.push({
key: name,
path: route_safe_marker
})
},
removeSafeRoute({name}) { // 移除地图路线
this.current_safe_route.forEach(item => {
if (item.key === name) {
this.map.remove([item.path]); // 删除地图折线
}
});
// this.map.remove(this.route_safe_marker); // 删除起始点标记
this.route_safe_marker.forEach(item => {
if (item.key === name) {
this.map.remove(item.path); // 删除起始点标记
}
});
},
// addNavRoute (path) { // 新增导航路径
// // 生成折线地图路径
// this.current_route = new AMap.Polyline({
// path,
// isOutline: true,
// outlineColor: '#42a5f5',
// borderWeight: 1,
// strokeColor: '#42a5f5',
// strokeOpacity: 1,
// strokeWeight: 2,
// // 折线样式还支持 'dashed'
// strokeStyle: 'dashed',
// // strokeStyle是dashed时有效
// strokeDasharray: [10, 5],
// lineJoin: 'round',
// lineCap: 'round',
// zIndex: 50
// })
// this.map.add([this.current_route]);
// // 设置起始点标记
// var marker1 = new AMap.Marker({
// icon: new AMap.Icon({
// image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8D-%E5%B0%8Fz@2x.png',
// size: new AMap.Size(40, 40),
// // 图标所用图片大小
// imageSize: new AMap.Size(40, 40),
// // 图标取图偏移量
// imageOffset: new AMap.Pixel(0, 0)
// }),
// position: new AMap.LngLat(path[0][0], path[0][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
// anchor: 'bottom-center',
// offset: new AMap.Pixel(0, 0)
// });
// marker1.setLabel({
// direction: 'right',
// offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
// content: "<div class='info'>终点</div>", //设置文本标注内容
// });
// var marker2 = new AMap.Marker({
// icon: new AMap.Icon({
// image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A%E4%BD%8D-%E5%B0%8Fz@2x.png',
// size: new AMap.Size(40, 40),
// // 图标所用图片大小
// imageSize: new AMap.Size(40, 40),
// // 图标取图偏移量
// imageOffset: new AMap.Pixel(0, 0)
// }),
// position: new AMap.LngLat(path[path.length - 1][0], path[path.length - 1][1]), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
// anchor: 'bottom-center',
// offset: new AMap.Pixel(0, 0)
// });
// marker2.setLabel({
// direction: 'right',
// offset: new AMap.Pixel(0, -10), //设置文本标注偏移量
// content: "<div class='info'>起点</div>", //设置文本标注内容
// });
// // 新增逃生路线标记
// this.route_marker = [marker1, marker2]
// this.map.add(this.route_marker);
// // 关闭导航提示
// this.show_walk_route = false;
// },
addNavRoute(position) { // 新增导航路径
function arraysEqual(a, b) {
// 如果两个数组的长度不等,则它们不相等
if (a.length !== b.length) {
return false;
}
// 比较每个元素是否相等
return a.every(function (element, index) {
return element == b[index];
});
}
let array = []; // 导航点所在路径集合
my_router.forEach((route) => {
route.path.forEach((item) => {
if (arraysEqual(position, item)) {
array.push(route);
return false;
}
})
});
let lngLat = [120.587234, 31.314147]; // 左边
// const lngLat = [120.588178, 31.314396]; // 右边
// console.log(this.current_lng ,this.current_lat);
// 构建路线结构
// 如果建筑不在导航路径上面需要单独处理, 直接查询建筑离哪个导航路径最近显示出来
const route_obj = array.length ? array: my_router;
if (!array.length) {
lngLat = position; // 当前导航建筑经纬度
}
// 计算距离最近的路径
route_obj.forEach((line) => {
line.distance = AMap.GeometryUtil.distanceToLine(lngLat, line.path);
});
let min = Math.min(...route_obj.map((line) => line.distance))
let result = route_obj.filter((line) => line.distance === min);
// 标记示例
this.current_route = new AMap.Polyline({
path: result[0]['path'],
isOutline: true,
outlineColor: '#42a5f5',
borderWeight: 1,
strokeColor: '#42a5f5',
strokeOpacity: 1,
strokeWeight: 2,
// 折线样式还支持 'dashed'
strokeStyle: 'dashed',
// strokeStyle是dashed时有效
strokeDasharray: [10, 5],
lineJoin: 'round',
lineCap: 'round',
zIndex: 50
})
this.map.add([this.current_route]);
// 获取定位打标记
this.setLocation();
// this.location_marker = new AMap.Marker({
// icon: new AMap.Icon({
// image: 'https://cdn.ipadbiz.cn/xys/map/%E5%AE%9A.png',
// size: new AMap.Size(40, 40),
// // 图标所用图片大小
// imageSize: new AMap.Size(40, 40),
// // 图标取图偏移量
// imageOffset: new AMap.Pixel(0, 0)
// }),
// position: new AMap.LngLat(...lngLat), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
// anchor: 'bottom-center',
// offset: new AMap.Pixel(0, 0)
// });
// this.map.add([this.location_marker]);
// 关闭导航提示
this.show_walk_route = false;
},
removeNavRoute() { // 移除地图路线
this.map.remove([this.current_route]); // 删除地图折线
// this.map.remove(this.route_marker); // 删除起始点标记
this.map.remove(this.location_marker); // 删除当前定位标记
// 关闭导航提示
this.show_walk_route = true;
},
computedMapSource(x, y, z) { // 根据图层信息生成图层实际地址
for (const id in this.mapTiles) {
if (z == id) {
const scope = this.mapTiles[id];
return scope[`${x}-${y}`]
}
}
},
setTitleLayer() { // 生成瓦片图
// 获取瓦片图渲染范围
function getFirstProperty(obj) {
for (var prop in obj) {
return prop;
}
}
function getLastProperty(obj) {
var props = [];
for (var prop in obj) {
props.push(prop);
}
return props[props.length - 1];
}
let obj_scope = {};
for (const key in this.mapTiles) {
const element = this.mapTiles[key];
let first = getFirstProperty(element).split('-');
let last = getLastProperty(element).split('-');
obj_scope[key] = {
x: [first[0], last[0]],
y: [first[1], last[1]]
}
}
const _this = this;
var layer = new AMap.TileLayer.Flexible({
cacheSize: 50,
opacity: 1,
zIndex: 100,
createTile: function (x, y, z, success, fail) {
// 控制地图等级显示图片范围-过滤不显示的图层渲染
for (const id in obj_scope) {
if (z == id) {
const scope = obj_scope[id];
if (x < scope.x[0] || x > scope.x[1]) {
fail()
return;
}
if (y < scope.y[0] || y > scope.y[1]) {
fail()
return;
}
}
}
var img = document.createElement('img');
img.onload = function () {
success(img)
};
img.crossOrigin = "anonymous";// 必须添加,同时图片要有跨域头
img.onerror = function () {
fail()
};
// img.src = `images/tiles/${z}/${x}_${y}.png`;
img.src = _this.computedMapSource(x, y, z);
},
});
this.map.addLayer(layer);
// Canvas作为切片
var layer1 = new AMap.TileLayer.Flexible({
// tileSize: 128,
cacheSize: 300,
zIndex: 200,
createTile: function (x, y, z, success, fail) {
var c = document.createElement('canvas');
c.width = c.height = 256;
var cxt = c.getContext("2d");
cxt.font = "15px Verdana";
cxt.fillStyle = "#ff0000";
cxt.strokeStyle = "#FF0000";
cxt.strokeRect(0, 0, 256, 256);
cxt.fillText('(' + [x, y, z].join(',') + ')', 10, 30);
// 通知API切片创建完成
success(c);
}
});
// layer1.setMap(this.map);
// TODO: 暂时屏蔽等待数据设置字段
// 只显示相应区域,移动会回到选定范围
const id = this.$route.query.id;
if (id === '1759271') { // 定制开发
// this.lockMapBounds()
}
},
// 限制地图范围
lockMapBounds() {
// var bounds = this.map.getBounds();
var myBounds = new AMap.Bounds(
[117.045587,26.838764],
[117.051081,26.8345723]
);
this.map.setLimitBounds(myBounds);
let list =[
[117.045598,26.838764],
[117.051075,26.838779],
[117.051075,26.835107],
[117.045598,26.835107]
]
// 隐藏边界以外的区域
let outer = [
new AMap.LngLat(-360, 90, true),
new AMap.LngLat(-360, -90, true),
new AMap.LngLat(360, -90, true),
new AMap.LngLat(360, 90, true),
] // 遮盖填充反向
let pathArray = [
outer,
list
]
var polygon = new AMap.Polygon({
pathL: pathArray,
strokeColor: "#fff",
strokeWeight: 2,
fillColor: "#fff",
fillOpacity: 1,
})
polygon.setPath(pathArray)
this.map.add(polygon)
},
showInfoClick(e) {
// console.log(e);
var zoom = this.map.getZoom(); //获取当前地图级别
// var text =
// "您在 [" +
// e.lnglat.getLng() +
// "," +
// e.lnglat.getLat() +
// "] 的位置单击了地图!当前层级" +
// zoom;
var text =
"[" +
e.lnglat.getLng() +
"," +
e.lnglat.getLat() +
"],"
console.log(text);
// 点击空白处 关闭弹框
this.closeInfoWindow();
// 关闭弹出底部导航弹框
this.show_nav_popup = false;
},
// setWalkRoute(start = { lng: 120.587799, lat: 31.313276 }, end = { lng: 120.587912, lat: 31.315169 }) {
// //步行导航
// let walking_CallBack = (result) => {
// if (result.type === 'complete') {
// console.warn(result);
// console.warn('绘制步行路线完成');
// } else {
// console.error('步行路线数据查询失败' + result);
// }
// }
// AMap.plugin(["AMap.Walking"], () => { //加载步行导航插件
// this.walk_route = new AMap.Walking({
// map: this.map,
// }); //构造步行导航类
// AMap.Event.addListener(this.walk_route, "complete", walking_CallBack); //返回导航查询结果
// //根据起、终点坐标规划步行路线
// this.walk_route.search(new AMap.LngLat(start.lng, start.lat), new AMap.LngLat(end.lng, end.lat));
// });
// this.show_walk_route = false;
// },
// removeWalkRoute() {
// this.walk_route.clear();
// this.show_walk_route = true;
// },
infoWindowLocation(path) { // 监听前往按钮回调
// TODO: 实际获取精确定位后导航到位置
// // 判断是否在范围内
// if (!this.isPointInRing()) {
// this.dialog_show = true;
// } else {
// this.setWalkRoute({ lng: this.current_lng, lat: this.current_lat }, { lng: position[0], lat: position[1] })
// }
// 测试
this.closeInfoWindow(); // 关闭弹框
this.onClose(); // 关闭弹框
// this.setWalkRoute({ lng: 120.59014, lat: 31.310306 }, { lng: position[0], lat: position[1] });
if (this.current_route) { // 清除前一条步行导航
this.map.remove([this.current_route]); // 删除地图折线
// this.map.remove(this.route_marker); // 删除起始点标记
this.map.remove(this.location_marker); // 删除当前定位标记
}
if (path.length) {
this.addNavRoute(path)
} else {
this.dialog_show = true;
this.dialog_text = '内部设施';
}
},
handleSafeRoute(status, item) { // 打开/关闭逃生路线线
if (status) {
this.addSafeRoute(item)
// this.open_safe_route = false;
} else {
this.removeSafeRoute(item)
// this.open_safe_route = true;
}
item.status = !status;
},
handleNavPopup() {
this.show_nav_popup = !this.show_nav_popup
},
handleNavMarker(item) { // 底部列表点击跳转弹框判断
if (item.window_type === 'normal') {
this.positionMarker(item)
} else if (item.window_type === 'lite') {
this.positionLiteMarker(item)
} else if (item.window_type === 'warn') {
this.positionWarnMarker(item)
}
},
positionMarker(item) {
// 点击后创建自定义信息窗口
this.setInfoWindows(item)
// 把地图中心点设置为当前点击的标记点
this.map.setZoomAndCenter(this.zoom, item.position);
//
this.show_nav_popup = false;
// 禁止缩放
this.map.setStatus({
zoomEnable: false
});
},
positionLiteMarker(item) {
// 点击后创建自定义信息窗口
this.setInfoWindowsLite(item)
// 把地图中心点设置为当前点击的标记点
this.map.setZoomAndCenter(this.zoom, item.position);
//
this.show_nav_popup = false;
// 禁止缩放
this.map.setStatus({
zoomEnable: false
});
},
positionWarnMarker(item) {
// 点击后创建自定义信息窗口
this.setInfoWindowsWarn(item)
// 把地图中心点设置为当前点击的标记点
this.map.setZoomAndCenter(this.zoom, [item.position[0], item.position[1] + 0.000300]);
//
this.show_nav_popup = false;
// 禁止缩放
this.map.setStatus({
zoomEnable: false
});
},
setInfoWindows(item) {
// 此时需要把组件的样式设置为可见
this.showInfoWindow = true
// 设置marker头部的标题信息窗口
const infoWindow = new AMap.InfoWindow({
// 使用自定义窗体
isCustom: true,
// 只有当组件渲染完毕后,通过$el才能拿到原生的dom对象
content: this.$refs['infoWindow'].$el,
// 设置定位偏移量
offset: new AMap.Pixel(0, -30),
})
this.infoWindow = infoWindow;
this.itemInfo = item;
this.itemInfo.map = this.map;
this.itemInfo.LngLat = {
lng: this.current_lng,
lat: this.current_lat,
}
// 信息窗口打开
infoWindow.open(this.map, item.position)
},
setInfoWindowsLite(item) {
// 此时需要把组件的样式设置为可见
this.showInfoWindowLite = true
// 设置marker头部的标题信息窗口
const infoWindowLite = new AMap.InfoWindow({
// 使用自定义窗体
isCustom: true,
// 只有当组件渲染完毕后,通过$el才能拿到原生的dom对象
content: this.$refs['infoWindowLite'].$el,
// 设置定位偏移量
offset: new AMap.Pixel(0, -30),
})
this.infoWindowLite = infoWindowLite;
this.itemInfo = item;
this.itemInfo.map = this.map;
this.itemInfo.LngLat = {
lng: this.current_lng,
lat: this.current_lat,
}
// 信息窗口打开
infoWindowLite.open(this.map, item.position)
},
setInfoWindowsWarn(item) {
// 此时需要把组件的样式设置为可见
this.showInfoWindowWarn = true
// 设置marker头部的标题信息窗口
const infoWindowWarn = new AMap.InfoWindow({
// 使用自定义窗体
isCustom: true,
// 只有当组件渲染完毕后,通过$el才能拿到原生的dom对象
content: this.$refs['infoWindowWarn'].$el,
// 设置定位偏移量
offset: new AMap.Pixel(0, -30),
})
this.infoWindowWarn = infoWindowWarn;
this.itemInfo = item;
this.itemInfo.map = this.map;
this.itemInfo.LngLat = {
lng: this.current_lng,
lat: this.current_lat,
}
// 信息窗口打开
infoWindowWarn.open(this.map, item.position);
},
closeInfoWindow() {
if (this.showInfoWindow) {
this.$refs['infoWindow'].close();
// 打开缩放
this.map.setStatus({
zoomEnable: true
});
}
if (this.showInfoWindowLite) {
this.$refs['infoWindowLite'].close();
// 打开缩放
this.map.setStatus({
zoomEnable: true
});
}
if (this.showInfoWindowWarn) {
this.$refs['infoWindowWarn'].close();
// 打开缩放
this.map.setStatus({
zoomEnable: true
});
}
},
onPlay(name) {
// var zoomStyleMapping = { 14: 0, 15: 0, 16: 0, 17: 0, 18: 0, 19: 0, 20: 0 };
// _.each(coord.spotInfo, (x, i) => {
// var marker = new AMap.ElasticMarker({
// position: coord.spotInfo[i].position,
// zooms: [17, 19],
// styles: [{
// icon: {
// 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',
// size: [28, 28], // 可见区域的大小
// anchor: 'bottom-center', // 锚点
// fitZoom: 14, // 最合适的级别
// scaleFactor: 2, // 地图放大一级的缩放比例系数
// maxScale: 1.4, // 最大放大比例
// minScale: 0.8 // 最小放大比例
// },
// label: {
// content: coord.spotInfo[i].name,
// position: 'TM',
// // position: 'BM',
// // offset: new AMap.Pixel(0, 10),
// minZoom: 18
// }
// }, {
// icon: {},
// label: {}
// }],
// zoomStyleMapping: coord.spotInfo[i].zoom ? coord.spotInfo[i].zoom : zoomStyleMapping
// });
// if (clickListener) {
// marker.off('click', clickListener)
// }
// // 绑定景点的点击事件 - 文字出现才能触发
// var clickListener = marker.on('click', (e) => {
// this.positionMarker(coord.spotInfo[i]);
// })
// this.spotInfo.push(marker);
// })
// this.map.add(this.spotInfo);
},
onPause(name) { },
onClose () {
this.showInfoPopup = false;
},
onLiteClose () {
this.showInfoLitePopup = false;
},
onWarnClose () {
this.showInfoWarnPopup = false;
},
}
}
</script>
<style lang="less">
#container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
/* 标记文字样式 */
.amap-marker-label {
padding: 0.25rem 0.5rem;
width: auto;
border: none;
border-radius: 2px;
background: rgba(86, 65, 23, 0.8);
color: white;
}
.amap-marker {
.amap-icon {
margin-top: 0.25rem;
}
}
.input-card {
display: flex;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
background-clip: border-box;
border-radius: .25rem;
width: 20rem;
border-width: 0;
border-radius: 0.4rem;
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
position: fixed;
top: 4rem;
right: 1rem;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
padding: 0.75rem 1.25rem;
}
.tool-bar-wrapper {
position: absolute;
left: 20px;
bottom: 8rem;
width: 20px;
}
.nav-bar-wrapper {
position: fixed;
bottom: 0;
left: 0;
height: 5.5rem;
width: 100%;
background-color: white;
text-align: center;
box-shadow: 0 -1px 0 rgba(80, 80, 80, 0.1);
z-index: 999;
// padding: 0.5rem 0;
padding-bottom: 0.5rem;
.nav-bar-content {
display: flex;
overflow-x: scroll;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
position: relative;
}
.item {
padding-top: 0.5rem;
color: #888;
width: 21.5%;
flex-shrink: 0;
padding-top: 1rem;
}
.checked {
color: #965f13;
}
}
.safe-route-wrapper {
position: absolute;
bottom: 2rem;
right: 1rem;
background-color: white;
}
.operate-bar-wrapper {
position: fixed;
left: 20px;
bottom: 6rem;
width: 20px;
height: auto;
z-index: 100;
.box-wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.item {
position: relative;
text-align: center;
font-size: 0.85rem;
width: 2rem;
height: 2rem;
background-color: white;
margin-bottom: 1rem;
border-radius: 50%;
padding: 2.5px;
line-height: 2rem;
}
}
}
.popup-wrapper {
margin-top: 1rem;
.title {
font-size: 1.25rem;
margin-bottom: 0.85rem;
}
.content {
line-height: 1.75;
font-size: 0.95rem;
}
}
.hideScrollBar::-webkit-scrollbar {
display: none;
}
.hideScrollBar {
-ms-overflow-style: none;
overflow: -moz-scrollbars-none;
}
.van-dialog__confirm,
.van-dialog__confirm:active {
color: #AB8F57;
}
.walk-nav-text {
position: fixed;
bottom: 6rem;
left: 50%;
transform: translate(-50%, -50%);
z-index: 999;
background: rgba(86, 65, 23, 0.8);
color: white;
border-radius: 10px;
padding: 5px 12px;
font-size: 0.8rem;
}
</style>
@/common/map_data