448 lines
16 KiB
Vue
448 lines
16 KiB
Vue
<template>
|
||
<el-drawer
|
||
v-model="dialogVisible"
|
||
:title="title"
|
||
:size="GLOBAL.drawerSize"
|
||
append-to-body
|
||
:close-on-click-modal="false"
|
||
:destroy-on-close="true"
|
||
@open="open">
|
||
<div class="container">
|
||
<div
|
||
class="search-container"
|
||
v-if="!isReadonly">
|
||
<div class="search-input">
|
||
<el-input
|
||
v-model="searchAddress"
|
||
placeholder="请输入地址"></el-input>
|
||
<el-button
|
||
style="margin-left: 8px"
|
||
plain
|
||
type="primary"
|
||
@click="handleSearch">
|
||
查询
|
||
</el-button>
|
||
</div>
|
||
<el-card
|
||
class="search-panel"
|
||
v-if="searchPanelVisible">
|
||
<ul>
|
||
<li
|
||
v-for="item in list"
|
||
class="poibox search-item"
|
||
@click="renderMarker(item)">
|
||
<h3 class="poi-title">
|
||
<span class="poi-name amap-ellipsis">{{ item.name }}</span>
|
||
</h3>
|
||
<div
|
||
class="poi-info"
|
||
:title="item.address">
|
||
<p class="poi-addr amap-ellipsis">地址:{{ item.address }}</p>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</el-card>
|
||
</div>
|
||
<div
|
||
class="map-container"
|
||
ref="amapSearch"
|
||
:style="{ height: mapHeight }"></div>
|
||
|
||
<div class="detail-container">
|
||
<el-form
|
||
ref="form"
|
||
label-position="top"
|
||
:rules="formRules"
|
||
:model="form">
|
||
<el-form-item
|
||
label="详细地址信息"
|
||
prop="address">
|
||
<el-input
|
||
v-model="form.address"
|
||
placeholder="填写详细地址"></el-input>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
</div>
|
||
<template
|
||
v-if="!isReadonly"
|
||
#footer>
|
||
<span class="dialog-footer">
|
||
<el-button @click="close">取消</el-button>
|
||
<el-button
|
||
type="primary"
|
||
@click="handleConfirm">
|
||
确定
|
||
</el-button>
|
||
</span>
|
||
</template>
|
||
</el-drawer>
|
||
</template>
|
||
|
||
<script>
|
||
import AMapLoader from '@amap/amap-jsapi-loader';
|
||
|
||
let amapKey = '6fb9a8497a691e98c94b3365d26e89bc';
|
||
try {
|
||
amapKey = import.meta.env.VITE_APP_AMAP_KEY;
|
||
} catch (e) {}
|
||
|
||
export default {
|
||
name: 'GeocoderComponent',
|
||
props: {
|
||
modelValue: Boolean,
|
||
currentPosition: Object, //geometry格式
|
||
address: String,
|
||
title: {
|
||
type: String,
|
||
default: '详细位置',
|
||
},
|
||
isReadonly: {
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
isGetCurrentPosition: {
|
||
//是否自动获取当前定位,由高德提供获取当前定位功能,通常在数据创建时需要启用,在数据编辑时需要关闭
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
},
|
||
data() {
|
||
return {
|
||
searchAddress: '',
|
||
map: null, // 地图实例
|
||
placeSearch: null, //搜索地址
|
||
geocoder: null, //逆向地理编码
|
||
list: [],
|
||
location: null,
|
||
marker: null,
|
||
// 初始地图高度为100%
|
||
initialMapHeight: '100%',
|
||
//获取浏览器当前定位
|
||
geolocation: null,
|
||
searchPanelVisible: false,
|
||
form: {
|
||
address: '',
|
||
},
|
||
formRules: {
|
||
address: [{ required: true, message: '请输入详细地址!', trigger: 'blur' }],
|
||
},
|
||
};
|
||
},
|
||
computed: {
|
||
dialogVisible: {
|
||
get() {
|
||
return this.modelValue;
|
||
},
|
||
set(val) {
|
||
this.$emit('update:modelValue', val);
|
||
},
|
||
},
|
||
// 计算属性,根据list的长度来决定地图的高度
|
||
mapHeight() {
|
||
// 如果list有数据,则地图高度为50%,否则为100%
|
||
return this.list.length > 0 ? '100%' : this.initialMapHeight;
|
||
},
|
||
},
|
||
created() {
|
||
AMapLoader.load({
|
||
key: amapKey,
|
||
version: '2.0',
|
||
plugins: ['AMap.PlaceSearch', 'AMap.Geocoder'],
|
||
}).then((AMap) => {
|
||
this.geocoder = new AMap.Geocoder({
|
||
city: '全国',
|
||
extensions: 'all',
|
||
});
|
||
if (this.isGetCurrentPosition) {
|
||
//获取浏览器定位功能
|
||
AMap.plugin('AMap.Geolocation', () => {
|
||
this.geolocation = new AMap.Geolocation({
|
||
enableHighAccuracy: true, // 是否使用高精度定位,默认:true
|
||
timeout: 10000, // 设置定位超时时间,默认:无穷大
|
||
offset: [10, 20], // 定位按钮的停靠位置的偏移量
|
||
zoomToAccuracy: true, // 定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
|
||
panToLocation: true,
|
||
showMarker: true,
|
||
position: 'RB', // 定位按钮的排放位置, RB表示右下
|
||
convert: true, //转化为高德坐标
|
||
});
|
||
this.getCurrentPosition()
|
||
.then((res) => {
|
||
let { address, location } = res;
|
||
this.form.address = address;
|
||
let geometry = {
|
||
type: 'Point',
|
||
coordinates: [location.lng, location.lat],
|
||
};
|
||
this.coordinates = [location.lng, location.lat];
|
||
this.$emit('getAddress', geometry, this.form.address);
|
||
})
|
||
.catch((err) => {
|
||
this.$emit('error', err);
|
||
});
|
||
});
|
||
}
|
||
});
|
||
},
|
||
methods: {
|
||
open() {
|
||
AMapLoader.load({
|
||
key: amapKey,
|
||
version: '2.0',
|
||
plugins: ['AMap.PlaceSearch', 'AMap.Geocoder'],
|
||
}).then((AMap) => {
|
||
// 初始化地图
|
||
this.map = new AMap.Map(this.$refs.amapSearch, {
|
||
resizeEnable: true, // 是否监控地图容器尺寸变化
|
||
zoom: 18, // 初始化地图层级
|
||
animateEnable: false,
|
||
});
|
||
this.placeSearch = new AMap.PlaceSearch({
|
||
city: '全国',
|
||
map: this.map,
|
||
pageSize: 20, // 单页显示结果条数
|
||
autoFitView: true, // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见范围
|
||
});
|
||
|
||
//有定位时不获取默认定位和当前位置
|
||
if (this.currentPosition && this.currentPosition.coordinates.length > 0) {
|
||
let [lng, lat] = this.currentPosition.coordinates;
|
||
|
||
this.renderMarker({
|
||
address: this.address || '未知定位',
|
||
location: {
|
||
lng: lng,
|
||
lat: lat,
|
||
},
|
||
});
|
||
this.form.address = this.address;
|
||
/*this.coordinateToAddress(this.currentPosition.coordinates);*/
|
||
}
|
||
});
|
||
},
|
||
handleConfirm() {
|
||
this.$refs.form.validate().then((valid) => {
|
||
if (valid) {
|
||
let geometry = { type: 'Point', coordinates: this.coordinates };
|
||
this.$emit('getAddress', geometry, this.form.address);
|
||
this.close();
|
||
}
|
||
});
|
||
},
|
||
|
||
handleSearch() {
|
||
if (this.searchAddress) {
|
||
this.placeSearch.search(this.searchAddress, (status, result) => {
|
||
this.searchPanelVisible = true;
|
||
if (status === 'complete' && result.info === 'OK') {
|
||
if (result.poiList && result.poiList.pois.length > 0) {
|
||
this.list = result.poiList.pois;
|
||
this.location = this.list[0].id;
|
||
/*this.renderMarker(this.list[0]);*/
|
||
} else {
|
||
this.map.clearMap();
|
||
this.list = [];
|
||
this.$message.error('无法找到该地址!');
|
||
}
|
||
} else {
|
||
this.$message.warning('无法找到该地址!');
|
||
this.list = [];
|
||
this.clearMarker();
|
||
}
|
||
});
|
||
}
|
||
},
|
||
coordinateToAddress(lnglat) {
|
||
let that = this;
|
||
this.list = [];
|
||
this.geocoder.getAddress(lnglat, function (status, result) {
|
||
console.log(result.regeocode.pois);
|
||
if (status === 'complete' && result.info === 'OK') {
|
||
// result中对应详细地理坐标信息
|
||
that.list = result.regeocode.pois;
|
||
let details = that.list.filter((item) => {
|
||
let location = new AMap.LngLat(
|
||
Number(item.location.lng),
|
||
Number(item.location.lat),
|
||
);
|
||
if (location.lng === lnglat[0] && location.lat === lnglat[1]) {
|
||
return item;
|
||
}
|
||
});
|
||
if (details.length > 0) {
|
||
that.location = details[0].id;
|
||
that.searchAddress = details[0].name;
|
||
that.renderMarker(details[0]);
|
||
} else {
|
||
that.location = that.list[0].id;
|
||
that.searchAddress = that.list[0].name;
|
||
that.renderMarker(that.list[0]);
|
||
}
|
||
}
|
||
});
|
||
},
|
||
renderMarker(marker) {
|
||
this.clearMarker(); // 清除之前的标记
|
||
let location = marker.location;
|
||
this.map.setCenter([location.lng, location.lat]);
|
||
// 移动地图视野到标记位置
|
||
// this.map.setZoomAndCenter(16, [location.lng, location.lat]);
|
||
this.coordinates = [location.lng, location.lat];
|
||
// 创建标记
|
||
this.marker = new AMap.Marker({
|
||
position: [location.lng, location.lat],
|
||
map: this.map,
|
||
});
|
||
// 创建信息窗口
|
||
let infoWindowContent = `<div style="width: 200px">`;
|
||
if (marker.name) {
|
||
infoWindowContent += `<h3>${marker.name}</h3>`;
|
||
}
|
||
if (marker.address) {
|
||
infoWindowContent += `<p>地址:${marker.address}</p>`;
|
||
}
|
||
infoWindowContent += '</div>';
|
||
let infoWindow = new AMap.InfoWindow({
|
||
content: infoWindowContent, // 使用 HTML 设置信息窗口内容
|
||
offset: new AMap.Pixel(0, -10),
|
||
});
|
||
setTimeout(() => {
|
||
// 打开信息窗口
|
||
infoWindow.open(
|
||
this.map,
|
||
new AMap.LngLat(Number(location.lng), Number(location.lat)),
|
||
);
|
||
});
|
||
this.searchPanelVisible = false;
|
||
if (marker.address) {
|
||
this.form.address = marker.address;
|
||
}
|
||
},
|
||
close() {
|
||
this.dialogVisible = false;
|
||
},
|
||
clearMarker() {
|
||
if (this.marker) {
|
||
this.marker.setMap(null);
|
||
this.marker = null;
|
||
}
|
||
},
|
||
getCurrentPosition() {
|
||
return new Promise((resolve, reject) => {
|
||
if (!this.geolocation) reject();
|
||
this.geolocation.getCurrentPosition((status, result) => {
|
||
if (status === 'complete') {
|
||
let position = result.position;
|
||
this.geocoder.getAddress([position.lng, position.lat], (status, result) => {
|
||
if (status === 'complete' && result.info === 'OK') {
|
||
let pois = result.regeocode.pois;
|
||
if (pois.length > 0) {
|
||
let { address, location } = pois[0];
|
||
resolve({
|
||
address,
|
||
location,
|
||
});
|
||
/* this.renderMarker({
|
||
name: '当前定位',
|
||
address: address,
|
||
location: {
|
||
lng: location.lng,
|
||
lat: location.lat,
|
||
},
|
||
});*/
|
||
}
|
||
} else {
|
||
resolve({
|
||
address: '',
|
||
location: position,
|
||
});
|
||
/*this.renderMarker({
|
||
name: '当前定位',
|
||
location: {
|
||
lng: position.lng,
|
||
lat: position.lat,
|
||
},
|
||
});*/
|
||
}
|
||
});
|
||
} else {
|
||
console.error(result);
|
||
reject(result);
|
||
/* this.$emit('error', '无法获取定位信息:' + result.message);*/
|
||
}
|
||
});
|
||
});
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.search-container {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 40px;
|
||
background-color: #fff;
|
||
}
|
||
.search-input {
|
||
display: flex;
|
||
}
|
||
.search-panel {
|
||
position: absolute;
|
||
width: 100%;
|
||
background-color: #fff;
|
||
z-index: 2;
|
||
max-height: 50vh;
|
||
overflow: hidden;
|
||
}
|
||
.search-item {
|
||
}
|
||
.poibox {
|
||
border-bottom: 2px solid #e8e8e8;
|
||
cursor: pointer;
|
||
padding: 10px 0;
|
||
position: relative;
|
||
min-height: 5.5rem;
|
||
width: 100%;
|
||
.poi-title {
|
||
margin-left: 10px;
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
overflow: hidden;
|
||
margin-top: 0;
|
||
}
|
||
.poi-info {
|
||
word-break: break-all;
|
||
margin-left: 10px;
|
||
overflow: hidden;
|
||
}
|
||
}
|
||
/* 默认样式,适用于web端 */
|
||
.container {
|
||
display: flex;
|
||
height: 100%;
|
||
flex-direction: column; /* 改为上下布局 */
|
||
}
|
||
|
||
.map-container {
|
||
position: relative;
|
||
flex: 1; /* 占据剩余空间 */
|
||
min-height: 30vh;
|
||
margin-bottom: 10px;
|
||
z-index: 1;
|
||
}
|
||
|
||
.detail-container {
|
||
margin-top: 10px;
|
||
}
|
||
/* 移动端样式 */
|
||
@media (max-width: 768px) {
|
||
.search-container {
|
||
margin-left: 0; /* 移除间距 */
|
||
margin-top: 10px; /* 添加上下间距 */
|
||
width: 100%;
|
||
}
|
||
}
|
||
</style>
|