chore: initialize frontend miniapp repository
This commit is contained in:
401
src/components/map/TencentMap.vue
Normal file
401
src/components/map/TencentMap.vue
Normal file
@@ -0,0 +1,401 @@
|
||||
<template>
|
||||
<view class="map-component">
|
||||
<map
|
||||
id="museum-map"
|
||||
class="map"
|
||||
:latitude="mapCenter.latitude"
|
||||
:longitude="mapCenter.longitude"
|
||||
:scale="mapScale"
|
||||
:markers="markers"
|
||||
:polygons="polygons"
|
||||
:show-location="!isH5"
|
||||
:enable-3D="false"
|
||||
:enable-overlooking="false"
|
||||
:enable-zoom="true"
|
||||
:enable-scroll="true"
|
||||
:enable-rotate="false"
|
||||
@markertap="handleMarkerTap"
|
||||
@regionchange="handleRegionChange"
|
||||
@tap="handleMapTap"
|
||||
>
|
||||
<!-- 自定义控件 -->
|
||||
<cover-view class="map-controls">
|
||||
<!-- 楼层选择器 -->
|
||||
<cover-view class="floor-selector-wrapper">
|
||||
<cover-view class="floor-selector">
|
||||
<cover-view
|
||||
v-for="floor in floors"
|
||||
:key="floor.id"
|
||||
class="floor-item"
|
||||
:class="{ active: currentFloor === floor.id }"
|
||||
@tap="handleFloorClick(floor.id)"
|
||||
>
|
||||
<cover-view class="floor-label">{{ floor.label }}</cover-view>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
|
||||
<!-- 功能按钮 -->
|
||||
<cover-view class="action-buttons">
|
||||
<!-- 进入 3D 室内模式按钮 -->
|
||||
<cover-view class="action-btn indoor-mode-btn" @tap="handleEnter3D">
|
||||
<cover-view class="btn-text">🏛</cover-view>
|
||||
<cover-view class="btn-sub-text">室内</cover-view>
|
||||
</cover-view>
|
||||
<cover-view class="action-btn" @tap="handleLocation">
|
||||
<cover-view class="btn-text">📍</cover-view>
|
||||
</cover-view>
|
||||
<cover-view class="action-btn" @tap="handleZoomIn">
|
||||
<cover-view class="btn-text">+</cover-view>
|
||||
</cover-view>
|
||||
<cover-view class="action-btn" @tap="handleZoomOut">
|
||||
<cover-view class="btn-text">-</cover-view>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
</map>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const isH5 = typeof window !== 'undefined'
|
||||
|
||||
interface MapCenter {
|
||||
latitude: number
|
||||
longitude: number
|
||||
}
|
||||
|
||||
interface Marker {
|
||||
id: number
|
||||
latitude: number
|
||||
longitude: number
|
||||
iconPath: string
|
||||
width: number
|
||||
height: number
|
||||
title?: string
|
||||
callout?: {
|
||||
content: string
|
||||
display: 'ALWAYS' | 'BYCLICK'
|
||||
padding: number
|
||||
borderRadius: number
|
||||
bgColor: string
|
||||
color: string
|
||||
fontSize: number
|
||||
}
|
||||
}
|
||||
|
||||
interface Polygon {
|
||||
points: Array<{ latitude: number; longitude: number }>
|
||||
strokeWidth: number
|
||||
strokeColor: string
|
||||
fillColor: string
|
||||
zIndex: number
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
markerClick: [markerId: number]
|
||||
floorChange: [floor: string]
|
||||
enter3DMode: []
|
||||
}>()
|
||||
|
||||
// 深圳自然博物馆实际坐标
|
||||
// POI ID: 7043042949572197975
|
||||
const mapCenter = ref<MapCenter>({
|
||||
latitude: 22.692763, // 深圳自然博物馆纬度
|
||||
longitude: 114.363987 // 深圳自然博物馆经度(向右偏移,让建筑显示在左侧)
|
||||
})
|
||||
|
||||
// 地图缩放级别(调整为适中比例,既能看到建筑全貌又能看清细节)
|
||||
const mapScale = ref(17)
|
||||
|
||||
// 当前楼层
|
||||
const currentFloor = ref('1F')
|
||||
|
||||
// 楼层列表
|
||||
const floors = [
|
||||
{ id: '3F', label: '3F' },
|
||||
{ id: '2F', label: '2F' },
|
||||
{ id: '1F', label: '1F' },
|
||||
{ id: 'B1', label: 'B1' }
|
||||
]
|
||||
|
||||
// 博物馆建筑围栏(多边形)
|
||||
// 深圳自然博物馆实际建筑轮廓坐标
|
||||
const polygons = ref<Polygon[]>([
|
||||
{
|
||||
points: [
|
||||
{ latitude: 22.693738, longitude: 114.362331 },
|
||||
{ latitude: 22.69358, longitude: 114.362239 },
|
||||
{ latitude: 22.693513, longitude: 114.362239 },
|
||||
{ latitude: 22.691953, longitude: 114.362252 },
|
||||
{ latitude: 22.692106, longitude: 114.365058 },
|
||||
{ latitude: 22.692167, longitude: 114.365051 },
|
||||
{ latitude: 22.693129, longitude: 114.365018 },
|
||||
{ latitude: 22.693135, longitude: 114.364655 },
|
||||
{ latitude: 22.693196, longitude: 114.363982 },
|
||||
{ latitude: 22.693232, longitude: 114.363764 },
|
||||
{ latitude: 22.693348, longitude: 114.363473 },
|
||||
{ latitude: 22.693476, longitude: 114.36319 },
|
||||
{ latitude: 22.693653, longitude: 114.362714 },
|
||||
{ latitude: 22.693701, longitude: 114.362523 },
|
||||
{ latitude: 22.693738, longitude: 114.362351 },
|
||||
],
|
||||
strokeWidth: 3,
|
||||
strokeColor: '#E0E100', // 黄色边框
|
||||
fillColor: '#E0E10020', // 半透明黄色填充
|
||||
zIndex: 1
|
||||
}
|
||||
])
|
||||
|
||||
// 地图标记点(博物馆入口和主要位置)
|
||||
const markers = ref<Marker[]>([
|
||||
{
|
||||
id: 0,
|
||||
latitude: 22.692763,
|
||||
longitude: 114.363487,
|
||||
iconPath: '/static/icons/marker-museum.svg',
|
||||
width: 40,
|
||||
height: 40,
|
||||
title: '深圳自然博物馆',
|
||||
callout: {
|
||||
content: '深圳自然博物馆',
|
||||
display: 'ALWAYS',
|
||||
padding: 12,
|
||||
borderRadius: 6,
|
||||
bgColor: '#E0E100',
|
||||
color: '#000000',
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
latitude: 22.692363,
|
||||
longitude: 114.363487,
|
||||
iconPath: '/static/icons/marker-entrance.svg',
|
||||
width: 30,
|
||||
height: 30,
|
||||
title: '主入口',
|
||||
callout: {
|
||||
content: '主入口',
|
||||
display: 'BYCLICK',
|
||||
padding: 10,
|
||||
borderRadius: 5,
|
||||
bgColor: '#000000',
|
||||
color: '#FFFFFF',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
])
|
||||
|
||||
// 处理标记点点击
|
||||
const handleMarkerTap = (e: any) => {
|
||||
const markerId = e.detail.markerId || e.markerId
|
||||
console.log('点击标记点:', markerId)
|
||||
emit('markerClick', markerId)
|
||||
}
|
||||
|
||||
// 处理地图区域变化
|
||||
const handleRegionChange = (e: any) => {
|
||||
if (e.type === 'end') {
|
||||
console.log('地图区域变化:', e.detail)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理地图点击
|
||||
const handleMapTap = (e: any) => {
|
||||
console.log('点击地图:', e.detail)
|
||||
}
|
||||
|
||||
// 处理楼层点击
|
||||
const handleFloorClick = (floorId: string) => {
|
||||
currentFloor.value = floorId
|
||||
emit('floorChange', floorId)
|
||||
console.log('切换楼层:', floorId)
|
||||
}
|
||||
|
||||
// 定位到当前位置
|
||||
const handleLocation = () => {
|
||||
uni.getLocation({
|
||||
type: 'gcj02',
|
||||
success: (res) => {
|
||||
mapCenter.value = {
|
||||
latitude: res.latitude,
|
||||
longitude: res.longitude
|
||||
}
|
||||
|
||||
// 添加或更新用户位置标记
|
||||
const userMarkerIndex = markers.value.findIndex(m => m.id === 999)
|
||||
const userMarker: Marker = {
|
||||
id: 999,
|
||||
latitude: res.latitude,
|
||||
longitude: res.longitude,
|
||||
iconPath: '/static/icons/marker-user-location.svg',
|
||||
width: 40,
|
||||
height: 40,
|
||||
title: '我的位置',
|
||||
callout: {
|
||||
content: '您在这里',
|
||||
display: 'BYCLICK',
|
||||
padding: 10,
|
||||
borderRadius: 5,
|
||||
bgColor: '#000000',
|
||||
color: '#FFFFFF',
|
||||
fontSize: 12
|
||||
}
|
||||
}
|
||||
|
||||
if (userMarkerIndex >= 0) {
|
||||
markers.value[userMarkerIndex] = userMarker
|
||||
} else {
|
||||
markers.value.push(userMarker)
|
||||
}
|
||||
|
||||
console.log('定位成功:', res)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('定位失败:', err)
|
||||
uni.showToast({
|
||||
title: '定位失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 放大地图
|
||||
const handleZoomIn = () => {
|
||||
if (mapScale.value < 20) {
|
||||
mapScale.value += 1
|
||||
console.log('放大地图,当前缩放级别:', mapScale.value)
|
||||
}
|
||||
}
|
||||
|
||||
// 缩小地图
|
||||
const handleZoomOut = () => {
|
||||
if (mapScale.value > 3) {
|
||||
mapScale.value -= 1
|
||||
console.log('缩小地图,当前缩放级别:', mapScale.value)
|
||||
}
|
||||
}
|
||||
|
||||
// 进入 3D 室内模式
|
||||
const handleEnter3D = () => {
|
||||
console.log('进入 3D 室内模式')
|
||||
emit('enter3DMode')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
console.log('地图组件已挂载')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.map-component {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: var(--museum-bg-map);
|
||||
}
|
||||
|
||||
.map {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--museum-bg-map);
|
||||
}
|
||||
|
||||
.map-controls {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.floor-selector-wrapper {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.floor-selector {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.floor-item {
|
||||
width: 46px;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: transparent;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.floor-item.active {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
.floor-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #424754;
|
||||
}
|
||||
|
||||
.floor-item.active .floor-label {
|
||||
color: #E0E100;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 12px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
// 室内模式按钮
|
||||
.indoor-mode-btn {
|
||||
width: 46px;
|
||||
height: 64px !important;
|
||||
background-color: #E0E100;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
|
||||
.btn-text {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.btn-sub-text {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user