20250417
This commit is contained in:
parent
5563ffb05c
commit
1d8fff443b
@ -73,6 +73,7 @@ INSTALLED_APPS = [
|
|||||||
'apps.lyTiktokUnion',
|
'apps.lyTiktokUnion',
|
||||||
'apps.lyworkflow',
|
'apps.lyworkflow',
|
||||||
'apps.mymaps',
|
'apps.mymaps',
|
||||||
|
'apps.map',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|||||||
@ -101,6 +101,7 @@ urlpatterns = [
|
|||||||
path('api/lyformbuilder/', include('apps.lyFormBuilder.urls')),
|
path('api/lyformbuilder/', include('apps.lyFormBuilder.urls')),
|
||||||
path('api/lytiktokunion/', include('apps.lyTiktokUnion.urls')),
|
path('api/lytiktokunion/', include('apps.lyTiktokUnion.urls')),
|
||||||
path('api/workflow/', include('apps.lyworkflow.urls')),
|
path('api/workflow/', include('apps.lyworkflow.urls')),
|
||||||
|
path('api/map/', include('apps.map.urls')), # 添加地图模块路由
|
||||||
|
|
||||||
#文件管理
|
#文件管理
|
||||||
path('api/fileMedia/', RYFileMediaView.as_view(), name='file_media'),
|
path('api/fileMedia/', RYFileMediaView.as_view(), name='file_media'),
|
||||||
@ -191,5 +192,7 @@ urlpatterns = [
|
|||||||
path('downloadapp/',downloadapp ,name='前端APP下载页'),
|
path('downloadapp/',downloadapp ,name='前端APP下载页'),
|
||||||
path('favicon.ico',RedirectView.as_view(url=r'static/favicon.ico')),
|
path('favicon.ico',RedirectView.as_view(url=r'static/favicon.ico')),
|
||||||
path('', TemplateView.as_view(template_name="index.html"),name='后台管理默认页面'),
|
path('', TemplateView.as_view(template_name="index.html"),name='后台管理默认页面'),
|
||||||
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
29
backend/apps/map/migrations/0001_initial.py
Normal file
29
backend/apps/map/migrations/0001_initial.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 4.1.8 on 2025-04-17 21:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Place',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=100, verbose_name='地点名称')),
|
||||||
|
('lng', models.FloatField(verbose_name='经度')),
|
||||||
|
('lat', models.FloatField(verbose_name='纬度')),
|
||||||
|
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='map.place', verbose_name='父节点')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': '地点标签',
|
||||||
|
'verbose_name_plural': '地点标签',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
0
backend/apps/map/migrations/__init__.py
Normal file
0
backend/apps/map/migrations/__init__.py
Normal file
14
backend/apps/map/models.py
Normal file
14
backend/apps/map/models.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Place(models.Model):
|
||||||
|
name = models.CharField(max_length=100, verbose_name="地点名称")
|
||||||
|
lng = models.FloatField(verbose_name="经度")
|
||||||
|
lat = models.FloatField(verbose_name="纬度")
|
||||||
|
parent = models.ForeignKey('self', null=True, blank=True, related_name='children', on_delete=models.CASCADE, verbose_name="父节点")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "地点标签"
|
||||||
|
verbose_name_plural = verbose_name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
13
backend/apps/map/serializers.py
Normal file
13
backend/apps/map/serializers.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from .models import Place
|
||||||
|
|
||||||
|
class PlaceSerializer(serializers.ModelSerializer):
|
||||||
|
children = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Place
|
||||||
|
fields = ['id', 'name', 'lng', 'lat', 'parent', 'children']
|
||||||
|
|
||||||
|
def get_children(self, obj):
|
||||||
|
queryset = obj.children.all()
|
||||||
|
return PlaceSerializer(queryset, many=True).data
|
||||||
15
backend/apps/map/urls.py
Normal file
15
backend/apps/map/urls.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
@Remark: 地点标签模块的路由文件
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.urls import path, re_path
|
||||||
|
from rest_framework import routers
|
||||||
|
from .views import PlaceViewSet
|
||||||
|
|
||||||
|
map_url = routers.SimpleRouter()
|
||||||
|
map_url.register(r'places', PlaceViewSet)
|
||||||
|
|
||||||
|
urlpatterns = []
|
||||||
|
urlpatterns += map_url.urls
|
||||||
29
backend/apps/map/views.py
Normal file
29
backend/apps/map/views.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from rest_framework import viewsets
|
||||||
|
from .models import Place
|
||||||
|
from .serializers import PlaceSerializer
|
||||||
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
|
from drf_yasg import openapi
|
||||||
|
|
||||||
|
class PlaceViewSet(viewsets.ModelViewSet):
|
||||||
|
"""
|
||||||
|
地点标签管理接口
|
||||||
|
"""
|
||||||
|
queryset = Place.objects.filter(parent=None) # 只获取顶级节点
|
||||||
|
serializer_class = PlaceSerializer
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
operation_summary='获取地点标签树',
|
||||||
|
operation_description='返回所有地点标签的树形结构数据',
|
||||||
|
responses={200: PlaceSerializer(many=True)}
|
||||||
|
)
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
return super().list(request, *args, **kwargs)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
operation_summary='创建地点标签',
|
||||||
|
operation_description='创建新的地点标签',
|
||||||
|
request_body=PlaceSerializer,
|
||||||
|
responses={201: PlaceSerializer()}
|
||||||
|
)
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
return super().create(request, *args, **kwargs)
|
||||||
@ -380,7 +380,7 @@
|
|||||||
transform: translateX(-0.5%);
|
transform: translateX(-0.5%);
|
||||||
}
|
}
|
||||||
.lyadmin-main-content{
|
.lyadmin-main-content{
|
||||||
padding: 10px !important;
|
padding: 1px !important;
|
||||||
}
|
}
|
||||||
.lyfadein-leave-from {
|
.lyfadein-leave-from {
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
|
|||||||
298
frontend/src/views/map/PlaceTagTree.vue
Normal file
298
frontend/src/views/map/PlaceTagTree.vue
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="place-tag-tree">
|
||||||
|
<el-tree
|
||||||
|
:data="treeData"
|
||||||
|
:props="defaultProps"
|
||||||
|
node-key="id"
|
||||||
|
default-expand-all
|
||||||
|
highlight-current
|
||||||
|
@node-click="handleNodeClick"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="add-place-panel">
|
||||||
|
<el-input v-model="newPlaceName" placeholder="新地点名称" size="small" />
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="startAddPlace"
|
||||||
|
:type="isAdding ? 'success' : 'primary'"
|
||||||
|
>
|
||||||
|
{{ isAdding ? '点击地图选点' : '添加地点' }}
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="tempMarker"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="savePlace"
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</el-button>
|
||||||
|
<!-- 添加刷新按钮 -->
|
||||||
|
<el-button
|
||||||
|
type="info"
|
||||||
|
size="small"
|
||||||
|
@click="refreshMap"
|
||||||
|
>
|
||||||
|
刷新地图
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
const treeData = ref([])
|
||||||
|
const newPlaceName = ref('')
|
||||||
|
const isAdding = ref(false)
|
||||||
|
const tempMarker = ref(null)
|
||||||
|
const tempLngLat = ref(null)
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
children: 'children',
|
||||||
|
label: 'name'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取父组件的地图实例
|
||||||
|
const props = defineProps({
|
||||||
|
map: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const showPlacesOnMap = () => {
|
||||||
|
// 清除地图上现有的标记
|
||||||
|
props.map.clearOverLays()
|
||||||
|
|
||||||
|
// 遍历所有点位并添加到地图上
|
||||||
|
treeData.value.forEach(place => {
|
||||||
|
const lnglat = new T.LngLat(place.lng, place.lat)
|
||||||
|
const marker = new T.Marker(lnglat)
|
||||||
|
props.map.addOverLay(marker)
|
||||||
|
|
||||||
|
const label = new T.Label({
|
||||||
|
text: place.name,
|
||||||
|
position: lnglat,
|
||||||
|
offset: new T.Point(-37, -45),
|
||||||
|
style: {
|
||||||
|
color: "#333",
|
||||||
|
fontSize: "14px",
|
||||||
|
height: "28px",
|
||||||
|
lineHeight: "28px",
|
||||||
|
padding: "0 12px",
|
||||||
|
minWidth: "100px",
|
||||||
|
maxWidth: "200px", // 增加最大宽度限制
|
||||||
|
textAlign: "center",
|
||||||
|
border: "none",
|
||||||
|
borderRadius: "999px", // 使用更大的值确保圆角效果
|
||||||
|
MozBorderRadius: "999px", // 添加浏览器前缀
|
||||||
|
WebkitBorderRadius: "999px",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.85)",
|
||||||
|
backdropFilter: "blur(4px)",
|
||||||
|
WebkitBackdropFilter: "blur(4px)",
|
||||||
|
boxShadow: "0 2px 6px rgba(0, 0, 0, 0.1)",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis" // 文字过长显示省略号
|
||||||
|
}
|
||||||
|
})
|
||||||
|
props.map.addOverLay(label)
|
||||||
|
|
||||||
|
// 添加点击事件显示信息窗口
|
||||||
|
marker.addEventListener('click', () => {
|
||||||
|
const infoWin = new T.InfoWindow(place.name)
|
||||||
|
marker.openInfoWindow(infoWin)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchPlaces = async () => {
|
||||||
|
try {
|
||||||
|
const res = await axios.get('/api/map/places/', {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('获取地点响应:', res.data) // 添加日志便于调试
|
||||||
|
|
||||||
|
if (res.data && res.data.data) {
|
||||||
|
// 兼容不同的数据结构
|
||||||
|
const placesData = Array.isArray(res.data.data) ? res.data.data :
|
||||||
|
(res.data.data.data || [])
|
||||||
|
|
||||||
|
treeData.value = placesData
|
||||||
|
showPlacesOnMap()
|
||||||
|
} else {
|
||||||
|
throw new Error(res.data?.msg || '数据格式错误')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('获取地点标签详细错误:', e)
|
||||||
|
if (e.response) {
|
||||||
|
ElMessage.error(`获取地点标签失败: ${e.response.data?.msg || e.response.statusText}`)
|
||||||
|
} else if (e.request) {
|
||||||
|
ElMessage.error('网络请求失败,请检查网络连接')
|
||||||
|
} else {
|
||||||
|
ElMessage.error(e.message || '获取地点标签失败')
|
||||||
|
}
|
||||||
|
treeData.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在保存成功后也需要更新地图显示
|
||||||
|
const startAddPlace = () => {
|
||||||
|
if (!newPlaceName.value) {
|
||||||
|
ElMessage.warning('请先输入地点名称')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isAdding.value = true
|
||||||
|
|
||||||
|
// 添加地图点击事件监听
|
||||||
|
props.map.addEventListener('click', handleMapClick)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMapClick = (e) => {
|
||||||
|
if (!isAdding.value) return
|
||||||
|
|
||||||
|
// 清除之前的临时标记
|
||||||
|
if (tempMarker.value) {
|
||||||
|
props.map.removeOverLay(tempMarker.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新的临时标记
|
||||||
|
tempLngLat.value = e.lnglat
|
||||||
|
tempMarker.value = new T.Marker(e.lnglat)
|
||||||
|
props.map.addOverLay(tempMarker.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const savePlace = async () => {
|
||||||
|
if (!tempLngLat.value || !newPlaceName.value) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const existingPlace = treeData.value.find(item => item.name === newPlaceName.value)
|
||||||
|
|
||||||
|
// 修改URL确保以斜杠结尾
|
||||||
|
const method = existingPlace ? 'put' : 'post'
|
||||||
|
const url = existingPlace ? `/api/map/places/${existingPlace.id}/` : '/api/map/places/'
|
||||||
|
|
||||||
|
const res = await axios({
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
data: {
|
||||||
|
name: newPlaceName.value,
|
||||||
|
lng: tempLngLat.value.lng,
|
||||||
|
lat: tempLngLat.value.lat
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
},
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.data && res.data.id) {
|
||||||
|
if (existingPlace) {
|
||||||
|
// 更新已有地点
|
||||||
|
const index = treeData.value.findIndex(item => item.id === existingPlace.id)
|
||||||
|
treeData.value[index] = res.data
|
||||||
|
ElMessage.success('地点已更新')
|
||||||
|
} else {
|
||||||
|
// 新增地点
|
||||||
|
treeData.value.push(res.data)
|
||||||
|
ElMessage.success('添加成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
showPlacesOnMap()
|
||||||
|
newPlaceName.value = ''
|
||||||
|
|
||||||
|
// 清理临时状态
|
||||||
|
props.map.removeEventListener('click', handleMapClick)
|
||||||
|
if (tempMarker.value) {
|
||||||
|
props.map.removeOverLay(tempMarker.value)
|
||||||
|
}
|
||||||
|
isAdding.value = false
|
||||||
|
tempMarker.value = null
|
||||||
|
tempLngLat.value = null
|
||||||
|
} else {
|
||||||
|
throw new Error('保存失败:返回数据格式错误')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('添加失败:', e)
|
||||||
|
if (e.response) {
|
||||||
|
if (e.response.status === 500) {
|
||||||
|
ElMessage.error('服务器内部错误,请稍后再试')
|
||||||
|
} else {
|
||||||
|
ElMessage.error(`添加失败: ${e.response.data?.msg || e.response.statusText}`)
|
||||||
|
}
|
||||||
|
} else if (e.code === 'ECONNABORTED') {
|
||||||
|
ElMessage.error('请求超时,请检查网络连接')
|
||||||
|
} else {
|
||||||
|
ElMessage.error(e.message || '未知错误')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNodeClick = (node) => {
|
||||||
|
ElMessage.info(`选中:${node.name}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(fetchPlaces)
|
||||||
|
|
||||||
|
// 添加刷新地图方法
|
||||||
|
const refreshMap = async () => {
|
||||||
|
try {
|
||||||
|
await fetchPlaces()
|
||||||
|
ElMessage.success('地图已刷新')
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('刷新失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
defineExpose({
|
||||||
|
refreshMap
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.place-tag-tree {
|
||||||
|
width: 300px;
|
||||||
|
padding: 10px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-place-panel {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 20px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 优化输入框和按钮的样式 */
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -25,26 +25,27 @@
|
|||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item @click="startMeasure">开始测距</el-dropdown-item>
|
<el-dropdown-item @click="startMeasure">开始测距</el-dropdown-item>
|
||||||
<el-dropdown-item @click="clearMeasure">清除测距</el-dropdown-item>
|
<el-dropdown-item @click="clearMeasure">清除测距</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="refreshMap" divided>刷新地图</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div id="map" style="width: 100%; height: 600px;"></div>
|
<div id="map"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 添加 v-if 条件 -->
|
||||||
|
<PlaceTagTree class="place-tag-tree-panel" :map="map" v-if="map" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { Close } from '@element-plus/icons-vue' // 添加这行
|
import { Close, ArrowDown } from '@element-plus/icons-vue'
|
||||||
import { ArrowDown } from '@element-plus/icons-vue' // 添加这行
|
import PlaceTagTree from './PlaceTagTree.vue'
|
||||||
|
import axios from 'axios' // 添加这行导入axios
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: { Close, ArrowDown, PlaceTagTree },
|
||||||
Close,
|
|
||||||
ArrowDown // 添加这个
|
|
||||||
},
|
|
||||||
setup() {
|
setup() {
|
||||||
const searchText = ref('白云路')
|
const searchText = ref('白云路')
|
||||||
const map = ref(null)
|
const map = ref(null)
|
||||||
@ -93,8 +94,6 @@ export default {
|
|||||||
const initMap = () => {
|
const initMap = () => {
|
||||||
map.value = new T.Map('map')
|
map.value = new T.Map('map')
|
||||||
map.value.centerAndZoom(new T.LngLat(108.320004, 22.82402), 12)
|
map.value.centerAndZoom(new T.LngLat(108.320004, 22.82402), 12)
|
||||||
// map.value.addControl(new T.Control.Zoom())
|
|
||||||
// map.value.addControl(new T.Control.Scale())
|
|
||||||
map.value.addControl(new T.Control.MapType())
|
map.value.addControl(new T.Control.MapType())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,38 +105,157 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 在setup函数中添加fetchPlaces方法
|
||||||
|
const fetchPlaces = async () => {
|
||||||
|
try {
|
||||||
|
const res = await axios.get('/api/map/places/', {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.data && res.data.data) {
|
||||||
|
const placesData = Array.isArray(res.data.data) ? res.data.data :
|
||||||
|
(res.data.data.data || [])
|
||||||
|
// 添加显示地点到地图的逻辑
|
||||||
|
showPlacesOnMap(placesData)
|
||||||
|
ElMessage.success('地点数据已刷新')
|
||||||
|
} else {
|
||||||
|
throw new Error(res.data?.msg || '数据格式错误')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('获取地点标签详细错误:', e)
|
||||||
|
ElMessage.error('获取地点标签失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改clearSearch方法
|
||||||
const clearSearch = () => {
|
const clearSearch = () => {
|
||||||
poiList.value = []
|
try {
|
||||||
map.value.clearOverLays()
|
console.log('开始清除搜索')
|
||||||
|
poiList.value = []
|
||||||
|
map.value.clearOverLays()
|
||||||
|
console.log('地图标记已清除')
|
||||||
|
|
||||||
|
// 直接调用fetchPlaces而不是通过子组件
|
||||||
|
fetchPlaces()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('清除搜索时出错:', e)
|
||||||
|
ElMessage.error('清除搜索失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isMeasuring = ref(false)
|
const isMeasuring = ref(false)
|
||||||
const lineTool = ref(null)
|
const lineTool = ref(null)
|
||||||
|
|
||||||
const startMeasure = () => {
|
const startMeasure = () => {
|
||||||
lineTool.value = new T.PolylineTool(map.value, {
|
if (lineTool.value) {
|
||||||
showLabel: true,
|
lineTool.value.clear()
|
||||||
color: "#FF0000", // 修改为大红色
|
}
|
||||||
weight: 3,
|
|
||||||
opacity: 1
|
try {
|
||||||
});
|
lineTool.value = new T.PolylineTool(map.value, {
|
||||||
lineTool.value.open();
|
showLabel: true,
|
||||||
|
color: "#FF0000",
|
||||||
|
weight: 3,
|
||||||
|
opacity: 1,
|
||||||
|
enableDrawingTool: true, // 启用绘制工具
|
||||||
|
enableEditing: false // 禁用编辑以避免事件冲突
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加测距完成事件监听
|
||||||
|
lineTool.value.addEventListener("draw", function(e) {
|
||||||
|
// 测距完成后的处理
|
||||||
|
console.log("测距完成", e)
|
||||||
|
})
|
||||||
|
|
||||||
|
lineTool.value.open()
|
||||||
|
ElMessage.success('测距工具已开启')
|
||||||
|
} catch (e) {
|
||||||
|
console.error('测距工具初始化失败:', e)
|
||||||
|
ElMessage.error('测距工具启动失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearMeasure = () => {
|
const clearMeasure = () => {
|
||||||
if (lineTool.value) {
|
try {
|
||||||
lineTool.value.clear(); // 清除测距线
|
if (lineTool.value) {
|
||||||
|
lineTool.value.clear() // 清除测距线
|
||||||
|
lineTool.value = null // 清空工具实例
|
||||||
|
ElMessage.success('测距已清除')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('清除测距失败:', e)
|
||||||
|
ElMessage.error('清除测距失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加刷新方法
|
||||||
|
const refreshMap = () => {
|
||||||
|
if (map.value) {
|
||||||
|
// 调用子组件的刷新方法
|
||||||
|
const placeTagTree = document.querySelector('.place-tag-tree-panel').__vueParentComponent.ctx
|
||||||
|
placeTagTree.refreshMap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const showPlacesOnMap = (places) => {
|
||||||
|
// 清除地图上现有的标记
|
||||||
|
map.value.clearOverLays()
|
||||||
|
|
||||||
|
// 遍历所有点位并添加到地图上
|
||||||
|
places.forEach(place => {
|
||||||
|
const lnglat = new T.LngLat(place.lng, place.lat)
|
||||||
|
const marker = new T.Marker(lnglat)
|
||||||
|
map.value.addOverLay(marker)
|
||||||
|
|
||||||
|
const label = new T.Label({
|
||||||
|
text: place.name,
|
||||||
|
position: lnglat,
|
||||||
|
offset: new T.Point(-37, -45),
|
||||||
|
style: {
|
||||||
|
color: "#333",
|
||||||
|
fontSize: "14px",
|
||||||
|
height: "28px",
|
||||||
|
lineHeight: "28px",
|
||||||
|
padding: "0 12px",
|
||||||
|
minWidth: "100px",
|
||||||
|
maxWidth: "200px",
|
||||||
|
textAlign: "center",
|
||||||
|
border: "none",
|
||||||
|
borderRadius: "999px",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.85)",
|
||||||
|
backdropFilter: "blur(4px)",
|
||||||
|
WebkitBackdropFilter: "blur(4px)",
|
||||||
|
boxShadow: "0 2px 6px rgba(0, 0, 0, 0.1)",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
overflow: "hidden"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
map.value.addOverLay(label)
|
||||||
|
|
||||||
|
marker.addEventListener('click', () => {
|
||||||
|
const infoWin = new T.InfoWindow(place.name)
|
||||||
|
marker.openInfoWindow(infoWin)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
searchText,
|
searchText,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
poiList,
|
poiList,
|
||||||
handlePoiClick,
|
handlePoiClick,
|
||||||
clearSearch,
|
clearSearch,
|
||||||
startMeasure, // 修改这里
|
startMeasure,
|
||||||
clearMeasure // 修改这里
|
clearMeasure,
|
||||||
|
refreshMap,
|
||||||
|
map,
|
||||||
|
showPlacesOnMap // 添加这行,如果需要模板中使用
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,8 +264,28 @@ export default {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.map-container {
|
.map-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
width: 100%; /* 改为100% */
|
||||||
|
height: 100%; /* 改为100% */
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 600px;
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#map {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poi-list {
|
.poi-list {
|
||||||
@ -247,4 +385,14 @@ export default {
|
|||||||
.search-box {
|
.search-box {
|
||||||
width: 400px; /* 调整宽度 */
|
width: 400px; /* 调整宽度 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 覆盖 el-main 的 padding */
|
||||||
|
:deep(.el-main) {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 覆盖 lyadmin-main-content 的 padding */
|
||||||
|
:deep(.lyadmin-main-content) {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user