You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
386 lines
11 KiB
386 lines
11 KiB
7 months ago
|
<!-- @format -->
|
||
|
|
||
|
<template>
|
||
|
<div style="display: flex; width: 100%">
|
||
|
<a-form-item-rest>
|
||
|
<div class="rightBox">
|
||
|
<div class="inputArea">
|
||
|
<div style="margin-right: 10px">经度</div>
|
||
|
<div class="longitude">
|
||
|
<ns-input
|
||
|
v-model:value="longLatTudeobj.longitude"
|
||
|
type="number"
|
||
|
class="input"
|
||
|
placeholder="请输入经度"
|
||
|
:disabled="viewOnly"
|
||
|
@change="inputChange('longitude', $event)" /> </div
|
||
|
><div style="margin: 0 10px">纬度</div>
|
||
|
<div class="latitude">
|
||
|
<ns-input
|
||
|
v-model:value="longLatTudeobj.latitude"
|
||
|
type="number"
|
||
|
class="input"
|
||
|
placeholder="请输入纬度"
|
||
|
:disabled="viewOnly"
|
||
|
@change="inputChange('latitude', $event)" />
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="inputSearch" style="margin: 15px 0 15px 0">
|
||
|
<label style="margin-right: 10px">地址</label>
|
||
|
<ns-input
|
||
|
v-model:value="longLatTudeobj.address"
|
||
|
class="inputAdress"
|
||
|
:disabled="viewOnly"
|
||
|
placeholder="请输入地址" />
|
||
|
<ns-button type="primary" @click="onSearch" :disabled="viewOnly">查询</ns-button>
|
||
|
</div>
|
||
|
<div class="mapArea">
|
||
|
<div id="map-container"></div>
|
||
|
<div v-if="isShowResult" id="map-result" style="overflow: auto"></div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</a-form-item-rest>
|
||
|
</div>
|
||
|
</template>
|
||
|
<script lang="ts">
|
||
|
import { defineComponent, watch, ref, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
||
|
import AMapLoader from '@amap/amap-jsapi-loader';
|
||
|
import { shallowRef } from 'vue';
|
||
|
import { isArray, isEqual, isEmpty } from 'lodash-es';
|
||
|
|
||
|
export default defineComponent({
|
||
|
name: 'NsMapV2',
|
||
|
props: {
|
||
|
defaultValue: {
|
||
|
type: Array,
|
||
|
default: () => {
|
||
|
return [];
|
||
|
},
|
||
|
},
|
||
|
viewOnly: {
|
||
|
type: Boolean,
|
||
|
default: false,
|
||
|
},
|
||
|
value: {
|
||
|
type: Array,
|
||
|
default: () => {
|
||
|
return [];
|
||
|
},
|
||
|
},
|
||
|
fieldMap: {
|
||
|
type: Object || Array,
|
||
|
default: () => {
|
||
|
return [];
|
||
|
},
|
||
|
},
|
||
|
formModel: {
|
||
|
type: Object || Array,
|
||
|
},
|
||
|
//兼容v1写法
|
||
|
longitude: {
|
||
|
type: String,
|
||
|
default: '',
|
||
|
},
|
||
|
latitude: {
|
||
|
type: String,
|
||
|
default: '',
|
||
|
},
|
||
|
defaultAddress: {
|
||
|
type: String,
|
||
|
default: '',
|
||
|
},
|
||
|
//兼容v1写法end
|
||
|
},
|
||
|
emits: ['change', 'updata:longLat'],
|
||
|
setup(props, { emit }) {
|
||
|
let map: Recordable;
|
||
|
let geoc: any, localSearch: any, Amap: any;
|
||
|
const isShowResult = ref(false);
|
||
|
const hasload = ref(false);
|
||
|
const longLatTudeobj = ref({
|
||
|
longitude: '',
|
||
|
latitude: '',
|
||
|
address: '',
|
||
|
});
|
||
|
const delay = (time) => {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
setTimeout(() => {
|
||
|
resolve(map);
|
||
|
}, time);
|
||
|
});
|
||
|
};
|
||
|
const circleDelay = async () => {
|
||
|
let res = await delay(100);
|
||
|
if (res) {
|
||
|
return res;
|
||
|
} else {
|
||
|
return circleDelay();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//更新值
|
||
|
const updateValue = async (event) => {
|
||
|
if (!map) {
|
||
|
//循环等待map初始化
|
||
|
await circleDelay();
|
||
|
}
|
||
|
if (isArray(event)) {
|
||
|
if (!event.length) return;
|
||
|
longLatTudeobj.value['latitude'] = event[0] || '';
|
||
|
longLatTudeobj.value['longitude'] = event[1] || '';
|
||
|
longLatTudeobj.value['address'] = event[2] || '';
|
||
|
|
||
|
if (longLatTudeobj.value['latitude'] && longLatTudeobj.value['longitude']) {
|
||
|
map.clearMap();
|
||
|
getAddressBylnglat(longLatTudeobj.value, (res) => {
|
||
|
longLatTudeobj.value.address = res;
|
||
|
});
|
||
|
addMark({ position: [event[1], event[0]] });
|
||
|
map.setFitView();
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const resultPanelClick = (data: { [x: string]: string }) => {
|
||
|
if (data && data['location']) {
|
||
|
if (data['location']) {
|
||
|
const location = data['location']; // 当前marker的经纬度信息
|
||
|
longLatTudeobj.value.longitude = location['lng'].toFixed(6);
|
||
|
longLatTudeobj.value.latitude = location['lat'].toFixed(6);
|
||
|
}
|
||
|
if (data['address']) {
|
||
|
longLatTudeobj.value.address = data['address'];
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
//搜索
|
||
|
const onSearch = () => {
|
||
|
isShowResult.value = true;
|
||
|
map.clearMap();
|
||
|
if (!localSearch) return;
|
||
|
localSearch.search(longLatTudeobj.value.address, (status: string, result: Recordable) => {
|
||
|
if (status == 'complete' && result.info == 'OK') {
|
||
|
const resultPoi = result['poiList']['pois'];
|
||
|
if (resultPoi && resultPoi instanceof Array && resultPoi.length > 0) {
|
||
|
const pois = result.poiList.pois;
|
||
|
for (let i = 0; i < pois.length; i++) {
|
||
|
const poi = pois[i];
|
||
|
const marker = [];
|
||
|
marker[i] = addMark({
|
||
|
position: poi.location, // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
|
||
|
title: poi.name,
|
||
|
});
|
||
|
}
|
||
|
map.setFitView();
|
||
|
}
|
||
|
} else {
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
//初始化
|
||
|
const initMap = async () => {
|
||
|
AMapLoader.load({
|
||
|
key: '2694972059c3b978de5d7e073efdfc64', // 申请好的Web端开发者Key,首次调用 load 时必填
|
||
|
version: '1.4.15',
|
||
|
plugins: ['AMap.Geocoder', 'AMap.PlaceSearch'],
|
||
|
})
|
||
|
.then((AMap) => {
|
||
|
Amap = AMap;
|
||
|
// 需要传入AMap ,否则方法无法调用
|
||
|
map = new AMap.Map('map-container', {
|
||
|
// center: [117.000923, 36.675807],
|
||
|
resizeEnable: true,
|
||
|
zoom: 12,
|
||
|
});
|
||
|
//精准搜索插件
|
||
|
geoc = new AMap.Geocoder({
|
||
|
city: '021',
|
||
|
});
|
||
|
|
||
|
//模糊匹配插件
|
||
|
AMap.service('AMap.PlaceSearch', function () {
|
||
|
// 回调函数
|
||
|
// 实例化PlaceSearch
|
||
|
localSearch = new AMap.PlaceSearch({
|
||
|
pageSize: 5, // 每页显示多少行
|
||
|
pageIndex: 1, // 显示的下标从那个开始
|
||
|
type: '', // 类别,可以以|后面加其他类
|
||
|
city: '021',
|
||
|
panel: 'map-result', // 服务显示 的面板
|
||
|
autoFitView: true,
|
||
|
});
|
||
|
});
|
||
|
|
||
|
if (AMap.event) {
|
||
|
// 搜索结果列表的点标记
|
||
|
AMap.event.addListener(localSearch, 'listElementClick', (e) => {
|
||
|
resultPanelClick(e['data']);
|
||
|
});
|
||
|
// 标记物点击
|
||
|
AMap.event.addListener(localSearch, 'markerClick', (e) => {
|
||
|
// console.log('搜索结果标记物点击');
|
||
|
resultPanelClick(e['data']);
|
||
|
});
|
||
|
}
|
||
|
//不可编辑状态
|
||
|
if (!props.viewOnly) {
|
||
|
map.on('click', (e) => {
|
||
|
map.clearMap();
|
||
|
getAddressByaddress(e, (res) => {
|
||
|
longLatTudeobj.value.address = res;
|
||
|
longLatTudeobj.value.latitude = e.lnglat['lat'].toFixed(6);
|
||
|
longLatTudeobj.value.longitude = e.lnglat['lng'].toFixed(6);
|
||
|
});
|
||
|
addMark({
|
||
|
position: e.lnglat,
|
||
|
});
|
||
|
map.setFitView();
|
||
|
});
|
||
|
}
|
||
|
})
|
||
|
.catch((e) => {});
|
||
|
};
|
||
|
//根据经纬度搜索
|
||
|
const getAddressBylnglat = (event, callBack) => {
|
||
|
geoc.getAddress(event.lnglat, (status, result) => {
|
||
|
if (status == 'complete' && result.info == 'OK') {
|
||
|
const addComp = result['regeocode']['addressComponent'];
|
||
|
let address =
|
||
|
addComp.province +
|
||
|
addComp.city +
|
||
|
addComp.district +
|
||
|
addComp.street +
|
||
|
addComp.streetNumber;
|
||
|
callBack(address, result);
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
//根据关键字搜索
|
||
|
const getAddressByaddress = (event, callBack) => {
|
||
|
geoc.getAddress(event.lnglat, function (status, result) {
|
||
|
if (status == 'complete' && result.info == 'OK') {
|
||
|
const addComp = result['regeocode']['addressComponent'];
|
||
|
let address =
|
||
|
addComp.province +
|
||
|
addComp.city +
|
||
|
addComp.district +
|
||
|
addComp.street +
|
||
|
addComp.streetNumber;
|
||
|
callBack(address, result);
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
//添加点标记
|
||
|
const addMark = (data: Recordable) => {
|
||
|
let marker = new Amap.Marker(data);
|
||
|
map.add(marker);
|
||
|
};
|
||
|
onMounted(async () => {
|
||
|
await initMap();
|
||
|
if (props.value.length) {
|
||
|
updateValue(props.value);
|
||
|
}
|
||
|
if (props.defaultValue.length) {
|
||
|
updateValue(props.defaultValue);
|
||
|
}
|
||
|
});
|
||
|
onBeforeUnmount(() => {
|
||
|
map.destroy();
|
||
|
// localSearch.render.markerList.clear();
|
||
|
isShowResult.value = false;
|
||
|
});
|
||
|
|
||
|
watch(
|
||
|
() => props.value,
|
||
|
(newv, oldv) => {
|
||
|
if (!isEqual(newv, oldv) && !isEqual(newv, longLatTudeobj.value)) {
|
||
|
updateValue(newv);
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
immediate: true,
|
||
|
},
|
||
|
);
|
||
|
|
||
|
watch(
|
||
|
() => longLatTudeobj.value,
|
||
|
(newv, oldv) => {
|
||
|
if (newv) {
|
||
|
emit('change', [newv['latitude'], newv['longitude'], newv['address']]);
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
deep: true,
|
||
|
},
|
||
|
);
|
||
|
|
||
|
watch(
|
||
|
() => props.formModel,
|
||
|
(newv, oldv) => {
|
||
|
if (!isEmpty(newv) && props.fieldMap.length && !hasload.value) {
|
||
|
if (isArray(props.value) && !isEmpty(props.value)) {
|
||
|
return;
|
||
|
}
|
||
|
let longitude = [];
|
||
|
props.fieldMap.forEach((el, i) => {
|
||
|
longitude[i] = newv[el];
|
||
|
});
|
||
|
updateValue(longitude);
|
||
|
hasload.value = true;
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
immediate: true,
|
||
|
},
|
||
|
);
|
||
|
|
||
|
//兼容老的处理方法
|
||
|
watch(
|
||
|
//
|
||
|
() => [props.latitude, props.longitude, props.defaultAddress],
|
||
|
//
|
||
|
([latitude, longitude, defaultAddress]) => {
|
||
|
console.log(latitude, longitude, defaultAddress);
|
||
|
if (latitude && longitude) {
|
||
|
updateValue([latitude, longitude, defaultAddress]);
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
deep: true,
|
||
|
immediate: true,
|
||
|
},
|
||
|
);
|
||
|
|
||
|
return {
|
||
|
map,
|
||
|
isShowResult,
|
||
|
longLatTudeobj,
|
||
|
onSearch,
|
||
|
};
|
||
|
},
|
||
|
});
|
||
|
</script>
|
||
|
<style lang="less" scoped>
|
||
|
input::-webkit-outer-spin-button,
|
||
|
input::-webkit-inner-spin-button {
|
||
|
-webkit-appearance: none;
|
||
|
}
|
||
|
#map-container {
|
||
|
height: 400px;
|
||
|
}
|
||
|
.leftBox,
|
||
|
.rightBox {
|
||
|
flex: 1;
|
||
|
}
|
||
|
.inputArea {
|
||
|
display: flex;
|
||
|
line-height: 30px;
|
||
|
}
|
||
|
.inputSearch {
|
||
|
.inputAdress {
|
||
|
width: 344px;
|
||
|
margin-right: 10px;
|
||
|
}
|
||
|
}
|
||
|
</style>
|