<template>
<div class="village-info-container">
<div class="header-card">
<div class="header-content">
<h2>村庄信息管理</h2>
<p>管理系统中的村庄信息</p>
</div>
</div>
<el-card class="main-card">
<template #header>
<div class="card-header">
<span>村庄信息列表</span>
</div>
</template>
<!-- 搜索表单 -->
<el-form :model="searchForm" label-width="80px" class="search-form-inline" style="margin-bottom: 20px;">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="村庄名称">
<el-input v-model="searchForm.villageName" placeholder="请输入村庄名称" clearable />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="地理位置">
<el-input v-model="searchForm.location" placeholder="请输入地理位置" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<div class="search-buttons">
<el-button type="primary" @click="searchVillageInfos">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
<el-button type="primary" @click="showAddDialog">新增村庄</el-button>
</div>
</el-col>
</el-row>
</el-form>
<!-- 表格数据 -->
<el-table
:data="villageInfoList"
v-loading="loading"
border
style="width: 100%"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="villageName" label="村庄名称" width="150" />
<el-table-column prop="location" label="地理位置" width="200" />
<el-table-column prop="population" label="人口数量" width="100" />
<el-table-column prop="cultivatedArea" label="耕地面积(亩)" width="120" />
<el-table-column prop="contactInfo" label="联系方式" width="150" />
<el-table-column prop="establishedTime" label="建村时间" width="120">
<template #default="scope">
{{ scope.row.establishedTime }}
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template #default="scope">
<el-button size="small" @click="showDetail(scope.row)">详情</el-button>
<el-button size="small" @click="showEditDialog(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="deleteVillageInfo(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
class="pagination"
/>
</el-card>
<!-- 添加/编辑对话框 -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="800px"
:before-close="handleDialogClose"
>
<el-form
:model="dialogForm"
:rules="dialogRules"
ref="dialogFormRef"
label-width="100px"
>
<el-form-item label="村庄名称" prop="villageName">
<el-input v-model="dialogForm.villageName" placeholder="请输入村庄名称" />
</el-form-item>
<el-form-item label="地理位置" prop="location">
<div class="location-selector">
<el-input
v-model="dialogForm.location"
placeholder="请输入地理位置(如:四川省南充市XX县XX镇XX村)"
type="textarea"
:rows="3"
/>
<div class="location-hint">
<p>提示:可以手动输入详细地理位置,或通过地图选择位置</p>
</div>
<el-button type="primary" @click="showMapSelector" style="margin-top: 10px;">
通过地图选择位置
</el-button>
</div>
</el-form-item>
<el-form-item label="人口数量" prop="population">
<el-input-number v-model="dialogForm.population" :min="0" controls-position="right" style="width: 100%" />
</el-form-item>
<el-form-item label="耕地面积" prop="cultivatedArea">
<el-input-number v-model="dialogForm.cultivatedArea" :min="0" :precision="2" controls-position="right" style="width: 100%" />
</el-form-item>
<el-form-item label="联系方式" prop="contactInfo">
<el-input v-model="dialogForm.contactInfo" placeholder="请输入联系方式" />
</el-form-item>
<el-form-item label="建村时间" prop="establishedTime">
<el-date-picker
v-model="dialogForm.establishedTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择建村时间"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="村庄简介" prop="villageProfile">
<el-input v-model="dialogForm.villageProfile" placeholder="请输入村庄简介" type="textarea" :rows="3" />
</el-form-item>
<el-form-item label="发展规划" prop="developmentPlan">
<el-input v-model="dialogForm.developmentPlan" placeholder="请输入发展规划" type="textarea" :rows="3" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 地图选择对话框 -->
<el-dialog
v-model="mapDialogVisible"
title="选择地理位置"
width="800px"
@close="handleMapDialogClose"
>
<div class="map-container">
<div id="map-container" style="width: 100%; height: 400px;"></div>
<div class="map-controls">
<el-input
v-model="mapSearchKeyword"
placeholder="搜索地点(如:四川省南充市)"
style="width: 300px; margin-right: 10px;"
@keyup.enter="performSearch"
/>
<el-button type="primary" @click="performSearch">搜索</el-button>
<el-button @click="resetMapCenter">重置</el-button>
<el-button @click="forceReloadMap" type="warning">重载地图</el-button>
</div>
<div class="selected-location-info" v-if="selectedLocation">
<p><strong>已选择位置:</strong>{{ selectedLocation.address }}</p>
<p><strong>坐标:</strong>{{ selectedLocation.lng }}, {{ selectedLocation.lat }}</p>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="mapDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmLocationSelection" :disabled="!selectedLocation">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 详情对话框 -->
<el-dialog
v-model="detailDialogVisible"
title="村庄信息详情"
width="600px"
>
<el-form label-width="100px" class="detail-form">
<el-row>
<el-col :span="12">
<el-form-item label="村庄名称:">
<span>{{ detailData.villageName }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="人口数量:">
<span>{{ detailData.population }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="耕地面积:">
<span>{{ detailData.cultivatedArea }} 亩</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="建村时间:">
<span>{{ detailData.establishedTime }}</span>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="地理位置:">
<span>{{ detailData.location }}</span>
</el-form-item>
<el-form-item label="联系方式:">
<span>{{ detailData.contactInfo }}</span>
</el-form-item>
<el-form-item label="村庄简介:">
<span>{{ detailData.villageProfile }}</span>
</el-form-item>
<el-form-item label="发展规划:">
<span>{{ detailData.developmentPlan }}</span>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="detailDialogVisible = false">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import request from '@/utils/request'
// 搜索表单
const searchForm = ref({
villageName: '',
location: ''
})
// 表格数据
const villageInfoList = ref([])
const loading = ref(false)
const selectedVillageInfos = ref([])
// 分页参数
const pagination = ref({
pageNum: 1,
pageSize: 10,
total: 0
})
// 对话框相关
const dialogVisible = ref(false)
const dialogTitle = ref('')
const dialogForm = ref({
id: null,
villageName: '',
location: '',
population: 0,
cultivatedArea: 0,
villageProfile: '',
developmentPlan: '',
contactInfo: '',
establishedTime: ''
})
// 地图选择相关
const mapDialogVisible = ref(false)
const mapSearchKeyword = ref('')
const selectedLocation = ref(null)
let mapInstance = null
let marker = null
let currentMapType = 'tencent' // 只使用腾讯地图
let searchTimeout = null // 防抖定时器
// 动态加载地图API
const loadMapAPI = () => {
return new Promise((resolve, reject) => {
console.log('开始加载腾讯地图API')
// 检查是否已经存在腾讯地图API
if (isTencentMapReady()) {
console.log('腾讯地图API已存在且准备就绪')
resolve()
return
}
// 检查是否正在加载
const existingScript = document.querySelector('script[src*="map.qq.com"]')
if (existingScript) {
// 如果正在加载,等待加载完成
console.log('腾讯地图API正在加载中,等待加载完成...')
let checkCount = 0
const checkInterval = setInterval(() => {
checkCount++
if (isTencentMapReady()) {
clearInterval(checkInterval)
console.log('腾讯地图API加载完成并准备就绪')
resolve()
} else if (checkCount > 200) { // 增加超时时间到40秒
clearInterval(checkInterval)
console.error('腾讯地图API加载超时')
reject(new Error('腾讯地图API加载超时'))
}
}, 200)
return
}
// 创建script标签加载腾讯地图API,使用callback参数避免document.write问题
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = 'https://map.qq.com/api/js?v=2.exp&key=FLZBZ-3PLC3-Q4Z3P-RNIS6-EC5TK-UXBTT&callback=onTencentMapCallback'
script.async = true
script.defer = true
script.id = 'tencent-map-script'
// 定义回调函数
window.onTencentMapCallback = function() {
console.log('腾讯地图API回调执行')
// 等待API完全初始化
let checkCount = 0
const checkInterval = setInterval(() => {
checkCount++
if (isTencentMapReady()) {
clearInterval(checkInterval)
console.log('腾讯地图API初始化完成并准备就绪')
// 清理回调函数
if (window.onTencentMapCallback) {
delete window.onTencentMapCallback
}
resolve()
} else if (checkCount > 200) { // 增加超时时间到40秒
clearInterval(checkInterval)
console.error('腾讯地图API初始化超时')
// 清理回调函数
if (window.onTencentMapCallback) {
delete window.onTencentMapCallback
}
reject(new Error('腾讯地图API初始化超时'))
}
}, 200)
}
script.onload = () => {
console.log('腾讯地图API脚本加载成功')
}
script.onerror = (err) => {
console.log('腾讯地图API加载失败', err)
// 清理回调函数
if (window.onTencentMapCallback) {
delete window.onTencentMapCallback
}
reject(new Error('腾讯地图API加载失败: ' + (err.message || '未知错误')))
}
// 添加额外的安全检查,确保DOM已经准备好
if (document.head) {
document.head.appendChild(script)
} else {
// 如果document.head不存在,等待DOM准备好
const checkDOMInterval = setInterval(() => {
if (document.head) {
clearInterval(checkDOMInterval)
document.head.appendChild(script)
}
}, 100)
}
})
}
// 修改地图点击事件处理函数
const createTencentMap = () => {
try {
// 移除可能存在的旧地图实例
if (mapInstance) {
mapInstance = null
}
if (marker) {
marker.setMap(null)
marker = null
}
// 再次检查API是否就绪
if (!isTencentMapReady()) {
ElMessage.error('腾讯地图API未正确加载,请稍后重试')
return
}
// 检查地图容器
const mapContainer = document.getElementById('map-container')
if (!mapContainer) {
ElMessage.error('地图容器未找到')
return
}
console.log('创建腾讯地图实例')
// 使用更兼容的方式创建地图
const center = new qq.maps.LatLng(39.90923, 116.397428)
mapInstance = new qq.maps.Map(mapContainer, {
center: center,
zoom: 11,
disableDefaultUI: false
})
// 修复地图点击事件处理
qq.maps.event.addListener(mapInstance, 'click', function(e) {
try {
// 检查事件对象和坐标对象是否存在
if (!e || !e.latLng) {
console.error('地图点击事件缺少坐标信息')
return
}
// 关键修复:腾讯地图的LatLng使用属性而非方法获取经纬度
const lat = e.latLng.lat; // 不是e.latLng.lat()
const lng = e.latLng.lng; // 不是e.latLng.lng()
// 验证坐标是否为有效数字
if (typeof lat !== 'number' || typeof lng !== 'number' || isNaN(lat) || isNaN(lng)) {
console.error('获取的坐标不是有效数字:', e.latLng)
ElMessage.error('无法获取有效坐标')
return
}
updateTencentMarker({lat, lng})
// 反向地理编码获取地址
const geocoder = new qq.maps.Geocoder({
complete: (res) => {
if (res && res.detail) {
selectedLocation.value = {
lng,
lat,
address: res.detail.address || `${lng.toFixed(6)}, ${lat.toFixed(6)}`
}
} else {
selectedLocation.value = {
lng,
lat,
address: `${lng.toFixed(6)}, ${lat.toFixed(6)}`
}
}
},
error: () => {
selectedLocation.value = {
lng,
lat,
address: `${lng.toFixed(6)}, ${lat.toFixed(6)}`
}
}
})
geocoder.getAddress(e.latLng)
} catch (err) {
console.error('地图点击事件处理错误:', err)
ElMessage.error('处理地图点击事件失败')
}
})
console.log('腾讯地图实例创建完成')
} catch (error) {
ElMessage.error('创建腾讯地图实例失败: ' + error.message)
console.error('创建腾讯地图实例错误:', error)
}
}
// 增强API就绪检查函数
const isTencentMapReady = () => {
try {
// 更严格的API检查
if (!window.qq || typeof window.qq !== 'object') {
console.log('腾讯地图基础API未加载: window.qq 不存在或不是对象')
return false
}
if (!window.qq.maps || typeof window.qq.maps !== 'object') {
console.log('腾讯地图基础API未加载: window.qq.maps 不存在或不是对象')
return false
}
// 检查核心组件
const requiredConstructors = [
'Map', 'LatLng', 'Marker', 'Geocoder'
]
for (let ctor of requiredConstructors) {
if (typeof window.qq.maps[ctor] !== 'function') {
console.log(`腾讯地图组件 ${ctor} 未准备好: 不是函数`)
return false
}
}
// 检查事件系统
if (!window.qq.maps.event || typeof window.qq.maps.event.addListener !== 'function') {
console.log('腾讯地图事件系统未准备好')
return false
}
console.log('所有腾讯地图核心组件均已准备好')
return true
} catch (error) {
console.log('检查腾讯地图状态时出错:', error)
return false
}
}
// 详情对话框
const detailDialogVisible = ref(false)
const detailData = ref({
id: null,
villageName: '',
location: '',
population: 0,
cultivatedArea: 0,
villageProfile: '',
developmentPlan: '',
contactInfo: '',
establishedTime: ''
})
// 表单验证规则
const dialogRules = {
villageName: [
{ required: true, message: '请输入村庄名称', trigger: 'blur' }
],
location: [
{ required: true, message: '请输入地理位置', trigger: 'blur' }
]
}
// 表单引用
const dialogFormRef = ref(null)
// 防抖搜索函数
const debouncedSearch = () => {
// 清除之前的定时器
if (searchTimeout) {
clearTimeout(searchTimeout)
}
// 设置新的定时器
searchTimeout = setTimeout(() => {
searchMapLocation()
}, 500) // 500ms防抖延迟
}
// 获取村庄信息列表
const getVillageInfoList = () => {
loading.value = true
const params = {
pageNum: pagination.value.pageNum,
pageSize: pagination.value.pageSize,
villageName: searchForm.value.villageName,
location: searchForm.value.location
}
request.get('/villageInfo/selectPage', { params }).then(res => {
loading.value = false
if (res.code === '200') {
villageInfoList.value = res.data.list
pagination.value.total = res.data.total
} else {
ElMessage.error(res.msg || '获取村庄信息列表失败')
}
}).catch(err => {
loading.value = false
ElMessage.error('获取村庄信息列表失败,请稍后重试')
console.error('获取村庄信息列表错误:', err)
})
}
// 搜索村庄信息
const searchVillageInfos = () => {
pagination.value.pageNum = 1
getVillageInfoList()
}
// 重置搜索
const resetSearch = () => {
searchForm.value.villageName = ''
searchForm.value.location = ''
pagination.value.pageNum = 1
getVillageInfoList()
}
// 处理分页大小变化
const handleSizeChange = (val) => {
pagination.value.pageSize = val
pagination.value.pageNum = 1
getVillageInfoList()
}
// 处理当前页变化
const handleCurrentChange = (val) => {
pagination.value.pageNum = val
getVillageInfoList()
}
// 处理选中项变化
const handleSelectionChange = (val) => {
selectedVillageInfos.value = val
}
// 显示添加对话框
const showAddDialog = () => {
dialogTitle.value = '新增村庄信息'
dialogForm.value = {
id: null,
villageName: '',
location: '',
population: 0,
cultivatedArea: 0,
villageProfile: '',
developmentPlan: '',
contactInfo: '',
establishedTime: ''
}
dialogVisible.value = true
}
// 显示编辑对话框
const showEditDialog = (row) => {
dialogTitle.value = '编辑村庄信息'
dialogForm.value = { ...row }
dialogVisible.value = true
}
// 提交表单
const submitForm = () => {
try {
dialogFormRef.value.validate((valid) => {
if (valid) {
const formData = { ...dialogForm.value }
if (dialogForm.value.id) {
// 更新操作
request.put('/villageInfo/update', formData).then(res => {
if (res.code === '200') {
ElMessage.success('更新成功')
dialogVisible.value = false
getVillageInfoList()
} else {
ElMessage.error(res.msg || '更新失败')
}
}).catch(err => {
ElMessage.error('更新失败,请稍后重试')
console.error('更新村庄信息错误:', err)
})
} else {
// 添加操作
request.post('/villageInfo/add', formData).then(res => {
if (res.code === '200') {
ElMessage.success('添加成功')
dialogVisible.value = false
getVillageInfoList()
} else {
ElMessage.error(res.msg || '添加失败')
}
}).catch(err => {
ElMessage.error('添加失败,请稍后重试')
console.error('添加村庄信息错误:', err)
})
}
}
})
} catch (error) {
ElMessage.error('表单提交失败: ' + error.message)
console.error('表单提交错误:', error)
}
}
// 删除村庄信息
const deleteVillageInfo = (row) => {
try {
ElMessageBox.confirm(`确定要删除村庄"${row.villageName}"吗?`, '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
request.delete(`/villageInfo/delete/${row.id}`).then(res => {
if (res.code === '200') {
ElMessage.success('删除成功')
getVillageInfoList()
} else {
ElMessage.error(res.msg || '删除失败')
}
}).catch(err => {
ElMessage.error('删除失败,请稍后重试')
console.error('删除村庄信息错误:', err)
})
}).catch(() => {
// 取消删除
})
} catch (error) {
ElMessage.error('删除操作失败: ' + error.message)
console.error('删除操作错误:', error)
}
}
// 关闭对话框前的处理
const handleDialogClose = (done) => {
try {
ElMessageBox.confirm('确认关闭?修改的内容将不会保存', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
done()
}).catch(() => {
// 取消关闭
})
} catch (error) {
ElMessage.error('关闭对话框处理失败: ' + error.message)
console.error('关闭对话框错误:', error)
}
}
// 显示地图选择器
const showMapSelector = () => {
console.log('显示地图选择器')
mapDialogVisible.value = true
nextTick(() => {
console.log('nextTick回调执行')
try {
// 确保在DOM更新后再初始化地图
setTimeout(() => {
initMap()
}, 100)
} catch (error) {
ElMessage.error('初始化地图失败: ' + error.message)
console.error('初始化地图失败:', error)
}
})
}
// 初始化地图
const initMap = () => {
console.log('开始初始化地图')
// 检查地图容器是否存在
const mapContainer = document.getElementById('map-container')
if (!mapContainer) {
console.error('地图容器不存在')
ElMessage.error('地图容器未找到')
return
}
// 直接尝试创建地图,如果API未加载则加载API
createMap()
}
// 创建地图实例
const createMap = () => {
console.log('开始创建地图实例')
// 检查地图容器是否存在
const mapContainer = document.getElementById('map-container')
if (!mapContainer) {
console.error('地图容器不存在')
ElMessage.error('地图容器未找到')
return
}
if (mapInstance) {
console.log('地图实例已存在')
return
}
try {
// 检查腾讯地图API是否已加载并且组件可用
if (isTencentMapReady()) {
console.log('创建腾讯地图实例')
createTencentMap()
} else {
// 如果API未完全加载,则加载API
console.log('需要加载腾讯地图API')
ElMessage.info('正在加载地图服务...')
loadMapAPI().then(() => {
// 确保组件可用后再创建地图
if (isTencentMapReady()) {
createTencentMap()
} else {
ElMessage.error('腾讯地图API组件未正确加载')
}
}).catch(err => {
ElMessage.error('地图加载失败: ' + err.message)
console.error('地图加载错误:', err)
})
}
} catch (error) {
ElMessage.error('创建地图实例失败: ' + error.message)
console.error('地图初始化错误:', error)
}
}
// 执行搜索
const performSearch = () => {
console.log('搜索关键词:', mapSearchKeyword.value)
if (!mapSearchKeyword.value) {
ElMessage.warning('请输入搜索关键词')
return
}
console.log('使用腾讯地图进行搜索')
// 检查API是否已加载
if (isTencentMapReady()) {
searchTencentMapLocation()
} else {
// 如果API未加载,先加载API再搜索
ElMessage.info('正在加载地图服务...')
loadMapAPI().then(() => {
if (isTencentMapReady()) {
searchTencentMapLocation()
} else {
ElMessage.error('地图服务加载失败')
}
}).catch(err => {
ElMessage.error('地图服务加载失败: ' + err.message)
})
}
}
// 改进地理编码搜索函数
const searchTencentMapLocation = () => {
if (!mapSearchKeyword.value) {
ElMessage.warning('请输入搜索关键词')
return
}
if (!isTencentMapReady()) {
ElMessage.error('腾讯地图API未正确加载')
return
}
try {
console.log('开始腾讯地图搜索,关键词:', mapSearchKeyword.value)
if (typeof qq.maps.Geocoder !== 'function') {
ElMessage.error('地理编码服务不可用')
return
}
// 检查API密钥是否有效(简单验证)
const scriptTags = document.getElementsByTagName('script');
let apiKey = null;
for (let tag of scriptTags) {
if (tag.src && tag.src.includes('map.qq.com/api/js')) {
const match = tag.src.match(/key=([^&]+)/);
if (match && match[1]) {
apiKey = match[1];
break;
}
}
}
if (!apiKey) {
console.warn('未检测到腾讯地图API密钥,请检查加载地址');
ElMessage.warning('地图服务配置可能存在问题');
}
const geocoder = new qq.maps.Geocoder({
complete: (results) => {
try {
console.log('地理编码完整结果:', results);
if (!results || !results.detail) {
console.log('地理编码结果结构不完整');
ElMessage.warning('未找到匹配的位置信息');
return;
}
// 检查是否有错误代码
if (results.detail.error) {
console.error('地理编码返回错误代码:', results.detail.error);
handleGeocoderError(results.detail.error);
return;
}
if (!results.detail.location) {
console.log('地理编码结果中没有位置信息');
ElMessage.warning('未找到匹配的位置信息');
return;
}
// 获取位置信息
const location = results.detail.location;
if (typeof location.lat !== 'number' || typeof location.lng !== 'number') {
console.error('获取的坐标不是有效数字:', location);
ElMessage.error('获取位置坐标失败');
return;
}
const lat = location.lat;
const lng = location.lng;
if (isNaN(lat) || isNaN(lng)) {
ElMessage.error('获取的坐标无效');
return;
}
mapInstance.setCenter(location);
updateTencentMarker({lat, lng});
let address = '';
if (results.detail.address) {
address = results.detail.address;
} else if (results.detail.formattedAddress) {
address = results.detail.formattedAddress;
} else if (results.detail.pois && results.detail.pois.length > 0) {
address = results.detail.pois[0].name || mapSearchKeyword.value;
} else {
address = mapSearchKeyword.value;
}
selectedLocation.value = {
lng,
lat,
address: address
};
ElMessage.success('搜索成功');
} catch (e) {
console.error('处理地理编码结果错误:', e);
ElMessage.error('处理位置信息失败: ' + e.message);
}
},
error: (error) => {
try {
// 详细打印错误信息
console.error('地理编码错误详情:', {
error: error,
errorType: typeof error,
errorString: JSON.stringify(error),
keyword: mapSearchKeyword.value
});
// 尝试解析常见错误
if (error && error.status === 110) {
ElMessage.error('API密钥无效或未授权,请检查密钥配置');
} else if (error && error.status === 100) {
ElMessage.error('请求参数错误,请检查输入');
} else if (error && error.message) {
ElMessage.error('搜索失败: ' + error.message);
} else {
ElMessage.error('搜索失败,请尝试其他关键词或检查网络连接');
}
} catch (e) {
console.error('处理地理编码错误时出错:', e);
ElMessage.error('搜索过程发生错误');
}
}
});
console.log('发送地理编码请求:', mapSearchKeyword.value);
geocoder.getLocation(mapSearchKeyword.value);
} catch (error) {
console.error('地理编码初始化失败:', error);
ElMessage.warning('无法使用地图搜索,将直接使用输入的文本作为地址');
selectedLocation.value = {
lng: 0,
lat: 0,
address: mapSearchKeyword.value
};
}
};
// 处理地理编码错误代码
const handleGeocoderError = (errorCode) => {
const errorMessages = {
1: '服务器内部错误',
100: '请求参数非法',
101: 'AK不存在或非法',
102: '没有权限使用该服务',
103: '请求被限制',
104: '请求次数不足',
105: 'AK过期',
106: '服务当前不可用',
107: '不支持的接口',
108: '接口被禁用'
};
const message = errorMessages[errorCode] || `地理编码错误 (代码: ${errorCode})`;
ElMessage.error(`搜索失败: ${message}`);
// 针对常见错误给出解决方案
if (errorCode === 101 || errorCode === 105) {
console.warn('请检查腾讯地图API密钥是否有效,是否已添加当前域名到白名单');
} else if (errorCode === 102 || errorCode === 103) {
console.warn('当前API密钥可能没有地理编码服务权限或请求频率超限');
}
};
// 增加一个测试API连接的函数
const testMapApiConnection = () => {
if (!isTencentMapReady()) {
ElMessage.error('地图API未就绪');
return;
}
try {
// 创建一个简单的LatLng对象测试API是否正常工作
const testLatLng = new qq.maps.LatLng(39.90923, 116.397428);
console.log('API功能测试: 成功创建LatLng对象', testLatLng);
// 测试事件系统
const testObj = { test: true };
qq.maps.event.addListener(testObj, 'test', () => {
console.log('API功能测试: 事件系统工作正常');
});
qq.maps.event.trigger(testObj, 'test');
ElMessage.success('地图API连接测试成功');
} catch (error) {
console.error('API功能测试失败:', error);
ElMessage.error('地图API功能异常: ' + error.message);
}
}
// 处理搜索完成结果
const handleSearchComplete = (results) => {
try {
console.log('腾讯地图搜索结果:', results)
// 检查结果对象是否存在
if (!results) {
console.log('腾讯地图搜索无结果')
ElMessage.error('未找到指定位置,请检查地址是否正确')
return
}
// 检查详细信息是否存在
if (!results.detail) {
console.log('腾讯地图搜索详情不存在')
ElMessage.error('搜索结果格式不正确')
return
}
console.log('腾讯地图搜索详情:', results.detail)
// 检查POI数据
if (results.detail.pois && Array.isArray(results.detail.pois) && results.detail.pois.length > 0) {
// 使用第一个结果进行定位
const firstPoi = results.detail.pois[0]
console.log('第一个POI:', firstPoi)
// 检查POI的坐标信息
if (firstPoi && firstPoi.latLng) {
const location = firstPoi.latLng
console.log('定位坐标:', location)
// 确保坐标值有效
if (typeof location.lat === 'number' && typeof location.lng === 'number') {
mapInstance.setCenter(location)
updateTencentMarker({lat: location.lat, lng: location.lng})
// 获取地址信息,确保不访问未定义的属性
const name = firstPoi.name || ''
const address = firstPoi.address || ''
const title = firstPoi.title || ''
selectedLocation.value = {
lng: location.lng,
lat: location.lat,
address: name || address || title || mapSearchKeyword.value
}
ElMessage.success('搜索成功')
console.log('已选择位置:', selectedLocation.value)
return
}
}
}
// 如果没有搜索到结果或结果不完整
console.log('腾讯地图搜索无有效结果')
ElMessage.error('未找到指定位置或位置信息不完整,请检查地址是否正确')
} catch (e) {
console.error('处理搜索结果时发生错误:', e)
ElMessage.error('处理搜索结果失败: ' + (e.message || '未知错误'))
}
}
// 处理搜索错误
const handleSearchError = (error) => {
try {
console.error('腾讯地图搜索失败:', error)
// 检查错误对象是否存在及属性访问
const errorMessage = error ? (error.message || '未知错误') : '未知错误'
ElMessage.error('搜索失败: ' + errorMessage)
} catch (e) {
console.error('处理搜索错误时发生错误:', e)
ElMessage.error('处理搜索错误失败: ' + (e.message || '未知错误'))
}
}
// 使用地理编码器进行搜索(备选方案)
const performGeocoderSearch = () => {
try {
if (!mapSearchKeyword.value) {
ElMessage.warning('请输入搜索关键词')
return
}
// 检查地理编码器是否可用
if (typeof qq.maps.Geocoder !== 'function') {
ElMessage.error('腾讯地图地理编码服务不可用')
return
}
const geocoder = new qq.maps.Geocoder()
// 添加事件监听器
qq.maps.event.addListener(geocoder, 'complete', function(results) {
try {
console.log('地理编码结果:', results)
if (results && results.detail && results.detail.location) {
const location = results.detail.location
mapInstance.setCenter(location)
updateTencentMarker({lat: location.lat, lng: location.lng})
selectedLocation.value = {
lng: location.lng,
lat: location.lat,
address: mapSearchKeyword.value
}
ElMessage.success('搜索成功')
console.log('已选择位置:', selectedLocation.value)
} else {
ElMessage.error('未找到指定位置,请检查地址是否正确')
}
} catch (geocoderResultError) {
console.error('处理地理编码结果失败:', geocoderResultError)
ElMessage.error('处理搜索结果失败: ' + (geocoderResultError.message || '未知错误'))
}
})
qq.maps.event.addListener(geocoder, 'error', function(error) {
try {
console.error('地理编码失败:', error)
const errorMessage = error ? (error.message || '未知错误') : '未知错误'
ElMessage.error('搜索失败: ' + errorMessage)
} catch (e) {
console.error('处理地理编码错误时发生错误:', e)
ElMessage.error('处理搜索错误失败: ' + (e.message || '未知错误'))
}
})
// 执行地理编码
geocoder.getLocation(mapSearchKeyword.value)
} catch (geocoderError) {
console.error('初始化地理编码器失败:', geocoderError)
ElMessage.error('搜索初始化失败: ' + (geocoderError.message || '未知错误'))
}
}
// 处理地图对话框关闭
const handleMapDialogClose = () => {
try {
// 清理地图实例
if (mapInstance) {
mapInstance = null
}
if (marker) {
marker.setMap(null)
marker = null
}
selectedLocation.value = null
} catch (error) {
console.error('清理地图资源失败:', error)
}
}
// 强制重载地图
const forceReloadMap = () => {
try {
ElMessage.info('正在重载地图...')
// 清理现有地图实例和资源
if (mapInstance) {
mapInstance = null
}
if (marker) {
marker.setMap(null)
marker = null
}
selectedLocation.value = null
// 移除现有的腾讯地图脚本
const existingScript = document.querySelector('#tencent-map-script')
if (existingScript) {
document.head.removeChild(existingScript)
}
// 清理全局回调函数
if (window.onTencentMapCallback) {
delete window.onTencentMapCallback
}
// 重新初始化地图
setTimeout(() => {
initMap()
}, 500)
} catch (error) {
ElMessage.error('重载地图失败: ' + error.message)
console.error('重载地图失败:', error)
}
}
// 重置地图中心
const resetMapCenter = () => {
try {
if (mapInstance && window.qq && window.qq.maps && typeof qq.maps.LatLng === 'function') {
const center = new qq.maps.LatLng(39.90923, 116.397428)
mapInstance.setCenter(center)
} else {
ElMessage.error('地图未正确初始化')
return
}
if (marker) {
marker.setMap(null)
marker = null
}
selectedLocation.value = null
} catch (error) {
ElMessage.error('重置地图失败: ' + error.message)
console.error('重置地图错误:', error)
}
}
// 确认位置选择
const confirmLocationSelection = () => {
if (selectedLocation.value) {
dialogForm.value.location = selectedLocation.value.address
mapDialogVisible.value = false
} else {
ElMessage.warning('请先在地图上选择位置')
}
}
// 腾讯地图点击事件处理
const onTencentMapClick = (e) => {
try {
// 检查必要组件
if (!window.qq || !window.qq.maps) {
ElMessage.error('腾讯地图API未正确初始化')
return
}
const lat = e.latLng.getLat()
const lng = e.latLng.getLng()
const lnglat = { lat, lng }
// 更新标记位置
updateTencentMarker(lnglat)
// 设置选中位置
selectedLocation.value = {
lng: lng,
lat: lat,
address: `${lng.toFixed(6)}, ${lat.toFixed(6)}`
}
ElMessage.info('已选择地图位置')
} catch (error) {
ElMessage.error('处理地图点击事件失败: ' + error.message)
console.error('地图点击事件错误:', error)
}
}
// 更新腾讯地图标记
const updateTencentMarker = (lnglat) => {
try {
// 检查必要组件是否可用
if (!window.qq || !window.qq.maps) {
console.log('腾讯地图API未加载')
return
}
if (typeof qq.maps.Marker !== 'function' || typeof qq.maps.LatLng !== 'function') {
console.log('腾讯地图标记组件未准备好')
return
}
// 验证坐标有效性
if (typeof lnglat.lat !== 'number' || typeof lnglat.lng !== 'number' || isNaN(lnglat.lat) || isNaN(lnglat.lng)) {
console.error('无效的坐标值:', lnglat)
return
}
// 创建新的LatLng对象,注意这里的参数顺序是纬度(lat)在前,经度(lng)在后
const position = new qq.maps.LatLng(lnglat.lat, lnglat.lng)
if (!marker) {
marker = new qq.maps.Marker({
position: position,
map: mapInstance
})
} else {
marker.setPosition(position)
}
mapInstance.setCenter(position)
} catch (error) {
console.log('更新地图标记失败:', error.message)
}
}
// 页面加载时获取数据
onMounted(() => {
try {
getVillageInfoList()
} catch (error) {
ElMessage.error('页面初始化失败: ' + error.message)
console.error('页面初始化错误:', error)
}
})
</script>
<style scoped>
.village-info-container {
padding: 20px;
background-color: #f5f7fa;
min-height: calc(100vh - 60px);
}
.header-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
color: white;
}
.header-content h2 {
margin: 0 0 10px 0;
font-size: 24px;
font-weight: 600;
}
.header-content p {
margin: 0;
font-size: 14px;
opacity: 0.9;
}
.main-card {
margin-bottom: 20px;
}
.card-header {
font-weight: bold;
font-size: 16px;
}
.search-form-inline {
display: flex;
flex-wrap: wrap;
}
.search-buttons {
display: flex;
justify-content: flex-end;
align-items: flex-end;
height: 100%;
gap: 10px;
}
.pagination {
margin-top: 20px;
display: flex;
justify-content: center;
}
.detail-form .el-form-item {
margin-bottom: 15px;
}
.detail-form .el-form-item__label {
font-weight: bold;
}
.detail-form .el-form-item__content {
display: flex;
align-items: center;
}
.map-container {
width: 100%;
}
.map-controls {
margin: 15px 0;
display: flex;
align-items: center;
}
.selected-location-info {
margin-top: 15px;
padding: 10px;
background-color: #f5f7fa;
border-radius: 4px;
}
</style>出现搜索任何位置都报错搜索关键词: 四川省南充市高坪区
VillageInfo.vue:810 使用腾讯地图进行搜索
VillageInfo.vue:503 所有腾讯地图核心组件均已准备好
VillageInfo.vue:503 所有腾讯地图核心组件均已准备好
VillageInfo.vue:843 开始腾讯地图搜索,关键词: 四川省南充市高坪区
VillageInfo.vue:961 发送地理编码请求: 四川省南充市高坪区
VillageInfo.vue:937 地理编码错误详情: {error: 'ERROR', errorType: 'string', errorString: '"ERROR"', keyword: '四川省南充市高坪区'}
error @ VillageInfo.vue:937
De.onError @ VM5520:1
De.onLoad @ VM5520:1
eval @ VM5520:1
f.<computed> @ main.js:1
script
sk @ main.js:1
(匿名) @ main.js:1
sa.ready @ main.js:1
send @ main.js:1
De.send @ VM5520:1
De.doSend_changed @ VM5520:1
Lc @ main.js:1
mk @ main.js:1
jb.bindTo @ main.js:1
X @ VM5520:1
(匿名) @ main.js:1
$require @ main.js:1
(匿名) @ main.js:1
(匿名) @ main.js:1
(匿名) @ main.js:1
ha @ main.js:1
E.trigger @ main.js:1
Lc @ main.js:1
jb.set @ main.js:1
(匿名) @ main.js:1
Ui @ main.js:1
hh.send @ main.js:1
kh.getLocation @ main.js:1
searchTencentMapLocation @ VillageInfo.vue:962
performSearch @ VillageInfo.vue:814
callWithErrorHandling @ runtime-core.esm-bundler.js:199
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
emit @ runtime-core.esm-bundler.js:6386
(匿名) @ runtime-core.esm-bundler.js:8099
handleClick @ use-button.ts:61
callWithErrorHandling @ runtime-core.esm-bundler.js:199
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
invoker @ runtime-dom.esm-bundler.js:729