wangfukang 1 week ago
parent
commit
7b0c7f5ed4
  1. 4
      public/config.js
  2. 124
      src/libs/amapLoader.js
  3. 88
      src/libs/coordTransform.js
  4. 1
      src/views/app/business/logistics/logistics.vue
  5. 1
      src/views/app/shop/addEdit.vue
  6. 170
      src/views/app/shopArea/shopArea.vue
  7. 644
      src/views/my-components/hiver/map.vue
  8. 581
      src/views/my-components/hiver/mapLocate.vue

4
public/config.js

@ -1,5 +1,7 @@
// 打包后仍可修改的配置
const config = {
baseApi: "/hiver", // 请求路径统一前缀
mapboxToken: "pk.eyJ1IjoiZGVsaWNhY3lsZWUiLCJhIjoiY2o4b3U2eWx0MDh2ODJxcnNra2ZrM3l1dyJ9.z6CNmgaMEbC0Ks8AMM72Tw" // mapbox地图accessToken
mapboxToken: "pk.eyJ1IjoiZGVsaWNhY3lsZWUiLCJhIjoiY2o4b3U2eWx0MDh2ODJxcnNra2ZrM3l1dyJ9.z6CNmgaMEbC0Ks8AMM72Tw", // mapbox地图accessToken
amapKey: "4732ddeaa9ce280fa01864ff256426a8", // 高德地图 Web端(JS API) Key
amapSecurityJsCode: "05d7751de12e72a8e764a21e66d2e16f" // 高德安全密钥
}

124
src/libs/amapLoader.js

@ -0,0 +1,124 @@
let amapLoading = null;
function ensureSecurityConfig(securityJsCode) {
const code =
securityJsCode ||
(typeof config !== "undefined" && config.amapSecurityJsCode) ||
"";
if (code) {
window._AMapSecurityConfig = {
securityJsCode: code,
};
}
}
/**
* 项目里有 monaco AMD loader加载高德时需临时屏蔽 define
* 且不要用高德 loader.js会触发 Can only have one anonymous define
*/
function loadAmapScript(key) {
return new Promise((resolve, reject) => {
if (window.AMap) {
resolve(window.AMap);
return;
}
const previousDefine = window.define;
const hadAmd =
typeof previousDefine === "function" && previousDefine.amd;
if (hadAmd) {
window.define = undefined;
}
const restoreDefine = () => {
if (hadAmd) {
window.define = previousDefine;
}
};
const script = document.createElement("script");
script.type = "text/javascript";
script.charset = "utf-8";
// 不用 async/callback/plugin,避免 _cssload_ 与 AMD 冲突
script.src =
"https://webapi.amap.com/maps?v=2.0&key=" + encodeURIComponent(key);
script.onload = () => {
restoreDefine();
if (window.AMap) {
resolve(window.AMap);
} else {
reject(new Error("高德地图加载失败"));
}
};
script.onerror = () => {
restoreDefine();
reject(new Error("高德地图脚本加载失败"));
};
document.head.appendChild(script);
});
}
/**
* 按需加载高德 JS API
*/
export function loadAmap(key, securityJsCode) {
if (typeof window === "undefined") {
return Promise.reject(new Error("非浏览器环境"));
}
const finalKey = key || (typeof config !== "undefined" && config.amapKey) || "";
if (!finalKey) {
return Promise.reject(new Error("未配置高德地图 Key"));
}
if (window.AMap) {
return Promise.resolve(window.AMap);
}
if (amapLoading) {
return amapLoading;
}
ensureSecurityConfig(securityJsCode);
amapLoading = loadAmapScript(finalKey).catch((err) => {
amapLoading = null;
throw err;
});
return amapLoading;
}
/**
* 使用高德定位返回 GCJ-02 { lng, lat, accuracy, address }
*/
export function getAmapLocation(key, securityJsCode) {
return loadAmap(key, securityJsCode).then((AMap) => {
return new Promise((resolve, reject) => {
const runLocate = () => {
const geolocation = new AMap.Geolocation({
enableHighAccuracy: true,
timeout: 15000,
convert: true,
getCityWhenFail: false,
});
geolocation.getCurrentPosition((status, result) => {
if (status === "complete" && result && result.position) {
const position = result.position;
resolve({
lng:
typeof position.getLng === "function"
? position.getLng()
: position.lng,
lat:
typeof position.getLat === "function"
? position.getLat()
: position.lat,
accuracy: result.accuracy,
address: result.formattedAddress || "",
});
} else {
const message =
(result && (result.message || result.info)) || "定位失败";
reject(new Error(message));
}
});
};
AMap.plugin("AMap.Geolocation", runLocate);
});
});
}

88
src/libs/coordTransform.js

@ -0,0 +1,88 @@
/**
* 国内坐标系转换高德 GCJ-02 GPS WGS84
* Mapbox 使用 WGS84高德定位返回 GCJ-02
*/
const PI = Math.PI;
const A = 6378245.0;
const EE = 0.00669342162296594323;
function outOfChina(lng, lat) {
return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271;
}
function transformLat(lng, lat) {
let ret =
-100.0 +
2.0 * lng +
3.0 * lat +
0.2 * lat * lat +
0.1 * lng * lat +
0.2 * Math.sqrt(Math.abs(lng));
ret +=
((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
2.0) /
3.0;
ret +=
((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) /
3.0;
ret +=
((160.0 * Math.sin((lat / 12.0) * PI) +
320 * Math.sin((lat * PI) / 30.0)) *
2.0) /
3.0;
return ret;
}
function transformLng(lng, lat) {
let ret =
300.0 +
lng +
2.0 * lat +
0.1 * lng * lng +
0.1 * lng * lat +
0.1 * Math.sqrt(Math.abs(lng));
ret +=
((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
2.0) /
3.0;
ret +=
((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) /
3.0;
ret +=
((150.0 * Math.sin((lng / 12.0) * PI) +
300.0 * Math.sin((lng / 30.0) * PI)) *
2.0) /
3.0;
return ret;
}
function delta(lng, lat) {
let dLat = transformLat(lng - 105.0, lat - 35.0);
let dLng = transformLng(lng - 105.0, lat - 35.0);
const radLat = (lat / 180.0) * PI;
let magic = Math.sin(radLat);
magic = 1 - EE * magic * magic;
const sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / (((A * (1 - EE)) / (magic * sqrtMagic)) * PI);
dLng = (dLng * 180.0) / ((A / sqrtMagic) * Math.cos(radLat) * PI);
return { lat: dLat, lng: dLng };
}
/** GCJ-02 -> WGS84 */
export function gcj02ToWgs84(lng, lat) {
if (outOfChina(lng, lat)) {
return [lng, lat];
}
const d = delta(lng, lat);
return [lng - d.lng, lat - d.lat];
}
/** WGS84 -> GCJ-02 */
export function wgs84ToGcj02(lng, lat) {
if (outOfChina(lng, lat)) {
return [lng, lat];
}
const d = delta(lng, lat);
return [lng + d.lng, lat + d.lat];
}

1
src/views/app/business/logistics/logistics.vue

@ -323,6 +323,7 @@
xiangtong: false,
sortField: "avgTime",
regionId: regionId,
fromAdmin: true,
pageNum: 1,
pageSize: 10
},

1
src/views/app/shop/addEdit.vue

@ -218,6 +218,7 @@
<FormItem label="中转地点" prop="transferAddressId" class="form-noheight">
<shopArea-tree-choose @on-change="handleSelectTransferAddress"
@on-clear="handleClearTransferAddress" ref="transferAddressTree"
:root-id="shopAreaRootId"
text="选择中转地点" placeholder="点击选择中转地点"
placement="top" tree-max-height="260px"></shopArea-tree-choose>
</FormItem>

170
src/views/app/shopArea/shopArea.vue

@ -7,6 +7,13 @@
white-space: normal;
line-height: 1.5;
}
.geo-tip {
margin-top: 6px;
color: #808695;
font-size: 12px;
line-height: 1.4;
}
</style>
<template>
<div class="search">
@ -158,6 +165,30 @@
></InputNumber>
<span style="margin-left: 8px">%</span>
</FormItem>
<FormItem v-if="showEditParentAgent" label="区域类型" prop="areaKind">
<RadioGroup v-model="form.areaKind">
<Radio :label="1">校园区域</Radio>
<Radio :label="2">社会区域</Radio>
</RadioGroup>
<div class="geo-tip">社会区域首页展示待成团并隐藏配送入口</div>
</FormItem>
<FormItem v-if="showEditParentAgent" label="区域中心">
<Map
id="shopAreaEditMap"
v-model="form.geoPosition"
text="地图选点"
placeholder="经度, 纬度"
/>
<div class="geo-tip">用于小程序定位自动绑定校区请用高德地图选点GCJ-02未设置则不参与自动匹配</div>
</FormItem>
<FormItem v-if="showEditParentAgent" label="匹配半径">
<Input
v-model="form.radiusMeters"
placeholder="请输入匹配半径"
style="width: 200px"
/>
<span style="margin-left: 8px"></span>
</FormItem>
<FormItem label="排序值" prop="sortOrder">
<Tooltip
@ -235,7 +266,7 @@
:title="modalTitle"
v-model="modalVisible"
:mask-closable="false"
:width="500"
:width="showAddParentAgent ? 620 : 500"
>
<Form
ref="formAdd"
@ -281,6 +312,30 @@
></InputNumber>
<span style="margin-left: 8px">%</span>
</FormItem>
<FormItem v-if="showAddParentAgent" label="区域类型" prop="areaKind">
<RadioGroup v-model="formAdd.areaKind">
<Radio :label="1">校园区域</Radio>
<Radio :label="2">社会区域</Radio>
</RadioGroup>
<div class="geo-tip">社会区域首页展示待成团并隐藏配送入口</div>
</FormItem>
<FormItem v-if="showAddParentAgent" label="区域中心">
<Map
id="shopAreaAddMap"
v-model="formAdd.geoPosition"
text="地图选点"
placeholder="经度, 纬度"
/>
<div class="geo-tip">用于小程序定位自动绑定校区请用高德地图选点GCJ-02未设置则不参与自动匹配</div>
</FormItem>
<FormItem v-if="showAddParentAgent" label="匹配半径">
<Input
v-model="formAdd.radiusMeters"
placeholder="请输入匹配半径"
style="width: 200px"
/>
<span style="margin-left: 8px"></span>
</FormItem>
<FormItem label="排序值" prop="sortOrder">
<Tooltip
trigger="hover"
@ -397,8 +452,12 @@ import {
searchShopArea,
} from "@/api/app";
import { getUserListData } from "@/api/index";
import Map from "@/views/my-components/hiver/map";
export default {
name: "shopArea",
components: {
Map,
},
data() {
const validateCommissionRatio = (rule, value, callback) => {
if (value === "" || value == null || value === "null") {
@ -436,6 +495,11 @@ export default {
parentAgentId: "",
parentAgentName: "",
commissionRatio: 0,
areaKind: 1,
centerLng: null,
centerLat: null,
radiusMeters: "",
geoPosition: "",
},
formAdd: {},
agentModalVisible: false,
@ -643,11 +707,61 @@ export default {
this.$set(target, "parentAgentId", "");
this.$set(target, "parentAgentName", "");
},
clearGeoValue(target) {
this.$set(target, "centerLng", null);
this.$set(target, "centerLat", null);
this.$set(target, "radiusMeters", "");
this.$set(target, "geoPosition", "");
},
buildGeoPosition(lng, lat) {
if (lng === "" || lng == null || lat === "" || lat == null) {
return "";
}
return `${lng}, ${lat}`;
},
applyGeoPosition(target) {
if (!this.isRootArea(target.parentId)) {
this.clearGeoValue(target);
return true;
}
const position = (target.geoPosition || "").trim();
if (!position) {
this.clearGeoValue(target);
return true;
}
const parts = position.split(",");
if (parts.length < 2) {
this.$Message.warning("区域中心坐标格式应为:经度, 纬度");
return false;
}
const lng = Number(parts[0].trim());
const lat = Number(parts[1].trim());
if (!Number.isFinite(lng) || !Number.isFinite(lat)) {
this.$Message.warning("区域中心坐标不合法");
return false;
}
if (lng < -180 || lng > 180 || lat < -90 || lat > 90) {
this.$Message.warning("区域中心坐标超出范围");
return false;
}
const radius = Number(target.radiusMeters);
if (!Number.isInteger(radius) || radius <= 0) {
this.$Message.warning("请设置有效的匹配半径(米)");
return false;
}
this.$set(target, "centerLng", lng);
this.$set(target, "centerLat", lat);
this.$set(target, "radiusMeters", radius);
return true;
},
normalizeParentAgent(target) {
if (!this.isRootArea(target.parentId)) {
this.clearParentAgentValue(target);
this.$delete(target, "commissionRatio");
} else if (
this.clearGeoValue(target);
this.$set(target, "areaKind", 1);
} else {
if (
target.commissionRatio === "" ||
target.commissionRatio == null ||
target.commissionRatio === "null"
@ -656,6 +770,25 @@ export default {
} else {
this.$set(target, "commissionRatio", Number(target.commissionRatio));
}
const areaKind = Number(target.areaKind);
this.$set(target, "areaKind", areaKind === 2 ? 2 : 1);
}
},
syncGeoFromEntity(target) {
this.$set(
target,
"geoPosition",
this.buildGeoPosition(target.centerLng, target.centerLat)
);
if (
target.radiusMeters === "" ||
target.radiusMeters === "null" ||
target.radiusMeters == null
) {
this.$set(target, "radiusMeters", "");
} else {
this.$set(target, "radiusMeters", String(target.radiusMeters));
}
},
init() {
this.getParentList();
@ -783,6 +916,9 @@ export default {
) {
this.$set(this.form, "commissionRatio", 0);
}
const areaKind = Number(this.form.areaKind);
this.$set(this.form, "areaKind", areaKind === 2 ? 2 : 1);
this.syncGeoFromEntity(this.form);
} else {
this.cancelEdit();
}
@ -804,6 +940,11 @@ export default {
parentAgentId: "",
parentAgentName: "",
commissionRatio: 0,
areaKind: 1,
centerLng: null,
centerLat: null,
radiusMeters: "",
geoPosition: "",
};
this.editTitle = "";
},
@ -843,11 +984,19 @@ export default {
this.$Message.warning("请先点击选择要修改的区域");
return;
}
if (!this.applyGeoPosition(this.form)) {
return;
}
this.submitLoading = true;
delete this.form.mainHeaders;
delete this.form.viceHeaders;
this.normalizeParentAgent(this.form);
editShopArea(this.form).then((res) => {
const payload = { ...this.form };
delete payload.geoPosition;
if (payload.centerLng == null) payload.centerLng = "";
if (payload.centerLat == null) payload.centerLat = "";
if (payload.radiusMeters == null) payload.radiusMeters = "";
editShopArea(payload).then((res) => {
this.submitLoading = false;
if (res.success) {
this.editTitle = this.form.title;
@ -863,9 +1012,17 @@ export default {
this.normalizeParentAgent(this.formAdd);
this.$refs.formAdd.validate((valid) => {
if (valid) {
if (!this.applyGeoPosition(this.formAdd)) {
return;
}
this.submitLoading = true;
this.normalizeParentAgent(this.formAdd);
addShopArea(this.formAdd).then((res) => {
const payload = { ...this.formAdd };
delete payload.geoPosition;
if (payload.centerLng == null) payload.centerLng = "";
if (payload.centerLat == null) payload.centerLat = "";
if (payload.radiusMeters == null) payload.radiusMeters = "";
addShopArea(payload).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("添加成功");
@ -927,6 +1084,11 @@ export default {
parentAgentId: "",
parentAgentName: "",
commissionRatio: 0,
areaKind: 1,
centerLng: null,
centerLat: null,
radiusMeters: "",
geoPosition: "",
};
this.modalVisible = true;
},

644
src/views/my-components/hiver/map.vue

@ -50,12 +50,61 @@
<Icon type="ios-close" class="ivu-icon-ios-close" />
</a>
</div>
<div :id="id" :style="{ width: '100%', height: modalHeight }"></div>
<div class="amap-panel">
<div v-if="searchable" class="amap-search">
<Input
v-model="keyword"
search
enter-button="搜索"
placeholder="搜索校区/地点名称"
@on-search="searchPlace"
/>
<Button
v-if="locate"
class="amap-locate-btn"
size="small"
icon="md-locate"
:loading="locating"
@click="locateByAmap"
>定位到当前位置</Button
>
<ul v-if="searchResults.length" class="amap-search-list">
<li
v-for="(item, index) in searchResults"
:key="index"
@click="selectSearchResult(item)"
>
<div class="name">{{ item.name }}</div>
<div class="addr">{{ item.address || item.district || "" }}</div>
</li>
</ul>
</div>
<div
v-else-if="locate"
class="amap-locate-only"
>
<Button
size="small"
icon="md-locate"
:loading="locating"
@click="locateByAmap"
>定位到当前位置</Button
>
</div>
<div
:id="id"
class="amap-container"
:style="{ width: '100%', height: modalHeight }"
></div>
</div>
<div slot="footer">
<Row align="middle" justify="space-between">
<div style="flex: 1; margin-right: 12px">
<Alert show-icon style="margin-bottom: 0"
>当前选点坐标{{ currentValue }}</Alert
>当前选点坐标{{ currentValue || "未选择" }}</Alert
>
<div class="locate-tip">坐标为高德 GCJ-02可搜索定位或点击地图选点</div>
</div>
<div>
<Button type="text" @click="showModal = false">取消</Button>
<Button type="primary" @click="handelSubmit">确认提交</Button>
@ -65,9 +114,10 @@
</Modal>
</div>
</template>
<script>
import mapboxgl from "mapbox-gl";
import MapboxGeocoder from "@mapbox/mapbox-gl-geocoder";
import { loadAmap, getAmapLocation } from "@/libs/amapLoader";
export default {
name: "map",
props: {
@ -91,10 +141,6 @@ export default {
type: Number,
default: 900,
},
pitch: {
type: Number,
default: 0
},
decimal: {
type: Number,
default: 6,
@ -131,367 +177,266 @@ export default {
type: String,
default: "md-locate",
},
style: {
type: String,
default: "mapbox://styles/mapbox/streets-v11",
},
center: {
type: Array,
default: [116.35, 39.85],
default: () => [116.397428, 39.90923],
},
zoom: {
type: Number,
default: 9,
},
compact: {
type: Boolean,
default: true,
},
customAttribution: {
type: String,
default: "",
default: 12,
},
searchable: {
type: Boolean,
default: true,
},
changeStyle: {
type: Boolean,
default: true,
},
navigation: {
type: Boolean,
default: true,
},
locate: {
type: Boolean,
default: true,
},
fullscreen: {
type: Boolean,
default: false,
},
building3D: {
type: Boolean,
default: true,
},
},
data() {
return {
data: this.value,
mapbox: null,
map: null,
marker: null,
placeSearch: null,
currentValue: "",
showModal: false,
full: false,
modalHeight: "500px",
marker: null,
keyword: "",
searchResults: [],
locating: false,
mapReady: false,
};
},
methods: {
init() {
if (!config.mapboxToken) {
return;
}
mapboxgl.accessToken = config.mapboxToken;
this.mapbox = new mapboxgl.Map({
container: this.id,
style: this.style,
center: this.center,
zoom: this.zoom,
pitch: this.pitch,
attributionControl: false,
getAmapKey() {
return (typeof config !== "undefined" && config.amapKey) || "";
},
getSecurityCode() {
return (typeof config !== "undefined" && config.amapSecurityJsCode) || "";
},
formatLngLat(lng, lat) {
let decimal = this.decimal;
if (decimal < 0) decimal = 0;
if (decimal > 14) decimal = 14;
return Number(lng).toFixed(decimal) + ", " + Number(lat).toFixed(decimal);
},
parsePosition(value) {
if (!value || !String(value).trim()) return null;
const parts = String(value).split(",");
if (parts.length < 2) return null;
const lng = Number(parts[0].trim());
const lat = Number(parts[1].trim());
if (!Number.isFinite(lng) || !Number.isFinite(lat)) return null;
return { lng, lat };
},
ensureMap() {
if (this.map) {
return Promise.resolve(this.map);
}
const key = this.getAmapKey();
if (!key) {
return Promise.reject(new Error("未配置高德地图 Key"));
}
return loadAmap(key, this.getSecurityCode()).then((AMap) => {
const centerPos = this.parsePosition(this.data) || {
lng: this.center[0],
lat: this.center[1],
};
this.map = new AMap.Map(this.id, {
zoom: this.parsePosition(this.data) ? 16 : this.zoom,
center: [centerPos.lng, centerPos.lat],
resizeEnable: true,
mapStyle: "amap://styles/normal",
});
//
this.mapbox.addControl(
new mapboxgl.AttributionControl({
compact: this.compact,
customAttribution: this.customAttribution,
})
);
//
if (this.searchable) {
this.mapbox.addControl(
new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
mapboxgl: mapboxgl,
this.map.on("click", (e) => {
if (!e || !e.lnglat) return;
this.placeMarker(e.lnglat.getLng(), e.lnglat.getLat());
});
return new Promise((resolve) => {
AMap.plugin(
["AMap.ToolBar", "AMap.Scale", "AMap.PlaceSearch", "AMap.Geolocation"],
() => {
this.map.addControl(
new AMap.ToolBar({
position: "RB",
locate: false,
})
);
}
//
class ChangeStyleControl {
getDefaultPosition() {
const defaultPosition = "top-right";
return defaultPosition;
}
onAdd(map) {
this.map = map;
this.controlContainer = document.createElement("div");
this.controlContainer.classList.add("mapboxgl-ctrl");
this.controlContainer.classList.add("mapboxgl-ctrl-group");
this.mapStyleContainer = document.createElement("div");
this.styleButton = document.createElement("button");
this.styleButton.type = "button";
this.mapStyleContainer.classList.add("mapboxgl-style-list");
const defaultStyle = "街道地图";
const styles = [
{ title: "街道地图", uri: "mapbox://styles/mapbox/streets-v11" },
{
title: "卫星街道",
uri: "mapbox://styles/mapbox/satellite-streets-v11",
},
{ title: "暗黑风格", uri: "mapbox://styles/mapbox/dark-v10" },
{ title: "明亮风格", uri: "mapbox://styles/mapbox/light-v10" },
{ title: "户外地图", uri: "mapbox://styles/mapbox/outdoors-v11" },
{ title: "卫星地图", uri: "mapbox://styles/mapbox/satellite-v9" },
];
for (const style of styles) {
const styleElement = document.createElement("button");
styleElement.type = "button";
styleElement.innerText = style.title;
styleElement.classList.add(
style.title.replace(/[^a-z0-9-]/gi, "_")
);
styleElement.dataset.uri = JSON.stringify(style.uri);
styleElement.addEventListener("click", (event) => {
const srcElement = event.srcElement;
if (srcElement.classList.contains("active")) {
return;
}
this.map.setStyle(JSON.parse(srcElement.dataset.uri));
setTimeout(() => {
const labelList = this.map.getStyle().layers.filter((layer) => {
return /-label/.test(layer.id);
this.map.addControl(new AMap.Scale({ position: "LB" }));
this.placeSearch = new AMap.PlaceSearch({
pageSize: 8,
pageIndex: 1,
citylimit: false,
});
for (let labelLayer of labelList) {
this.map.setLayoutProperty(labelLayer.id, "text-field", [
"coalesce",
["get", "name_zh-Hans"],
["get", "name"],
]);
this.mapReady = true;
resolve(this.map);
}
}, 500);
this.mapStyleContainer.style.display = "none";
this.styleButton.style.display = "block";
const elms = this.mapStyleContainer.getElementsByClassName(
"active"
);
while (elms[0]) {
elms[0].classList.remove("active");
}
srcElement.classList.add("active");
});
if (style.title === defaultStyle) {
styleElement.classList.add("active");
}
this.mapStyleContainer.appendChild(styleElement);
}
this.styleButton.classList.add("mapboxgl-ctrl-icon");
this.styleButton.classList.add("mapboxgl-style-switcher");
this.styleButton.addEventListener("click", () => {
this.styleButton.style.display = "none";
this.mapStyleContainer.style.display = "block";
});
document.addEventListener("click", this.onDocumentClick);
this.controlContainer.appendChild(this.styleButton);
this.controlContainer.appendChild(this.mapStyleContainer);
return this.controlContainer;
}
onRemove() {
if (
!this.controlContainer ||
!this.controlContainer.parentNode ||
!this.map ||
!this.styleButton
) {
return;
}
this.styleButton.removeEventListener("click", this.onDocumentClick);
this.controlContainer.parentNode.removeChild(this.controlContainer);
document.removeEventListener("click", this.onDocumentClick);
this.map = undefined;
}
onDocumentClick(event) {
if (
this.controlContainer &&
!this.controlContainer.contains(event.target) &&
this.mapStyleContainer &&
this.styleButton
) {
this.mapStyleContainer.style.display = "none";
this.styleButton.style.display = "block";
},
placeMarker(lng, lat, zoom) {
if (!this.map || typeof window.AMap === "undefined") return;
if (this.marker) {
this.marker.setMap(null);
this.marker = null;
}
this.marker = new window.AMap.Marker({
position: [lng, lat],
draggable: this.draggable,
cursor: "move",
});
this.marker.setMap(this.map);
if (this.draggable) {
this.marker.on("dragend", (e) => {
const pos = e.lnglat || (e.target && e.target.getPosition());
if (!pos) return;
const nextLng = typeof pos.getLng === "function" ? pos.getLng() : pos.lng;
const nextLat = typeof pos.getLat === "function" ? pos.getLat() : pos.lat;
this.currentValue = this.formatLngLat(nextLng, nextLat);
this.$emit("on-click", this.currentValue);
});
}
this.currentValue = this.formatLngLat(lng, lat);
this.$emit("on-click", this.currentValue);
this.map.setZoomAndCenter(zoom || Math.max(this.map.getZoom(), 16), [
lng,
lat,
]);
},
syncMarkerFromValue() {
const pos = this.parsePosition(this.data);
if (pos) {
this.currentValue = this.data;
this.placeMarker(pos.lng, pos.lat, 16);
} else {
this.currentValue = "";
if (this.marker) {
this.marker.setMap(null);
this.marker = null;
}
if (this.changeStyle) {
this.mapbox.addControl(new ChangeStyleControl());
if (this.map) {
this.map.setZoomAndCenter(this.zoom, this.center);
}
//
if (this.navigation) {
this.mapbox.addControl(new mapboxgl.NavigationControl());
}
//
if (this.locate) {
this.mapbox.addControl(
new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true,
},
trackUserLocation: true,
})
);
},
searchPlace() {
const keyword = (this.keyword || "").trim();
if (!keyword) {
this.searchResults = [];
return;
}
//
if (this.fullscreen) {
this.mapbox.addControl(
new mapboxgl.FullscreenControl({
container: document.querySelector("body"),
})
);
this.ensureMap()
.then(() => {
if (!this.placeSearch) {
this.$Message.warning("搜索组件未就绪,请稍后重试");
return;
}
this.mapbox.on("load", () => {
//
const labelList = this.mapbox.getStyle().layers.filter((layer) => {
return /-label/.test(layer.id);
});
for (let labelLayer of labelList) {
this.mapbox.setLayoutProperty(labelLayer.id, "text-field", [
"coalesce",
["get", "name_zh-Hans"],
["get", "name"],
]);
this.placeSearch.search(keyword, (status, result) => {
if (status === "complete" && result && result.poiList) {
this.searchResults = result.poiList.pois || [];
if (!this.searchResults.length) {
this.$Message.info("未找到相关地点");
}
// 3D
if (this.building3D) {
this.mapbox.addLayer({
id: "3d-buildings",
source: "composite",
"source-layer": "building",
filter: ["==", "extrude", "true"],
type: "fill-extrusion",
minzoom: 15,
paint: {
"fill-extrusion-color": "#aaa",
"fill-extrusion-height": [
"interpolate",
["linear"],
["zoom"],
15,
0,
15.05,
["get", "height"],
],
"fill-extrusion-base": [
"interpolate",
["linear"],
["zoom"],
15,
0,
15.05,
["get", "min_height"],
],
"fill-extrusion-opacity": 0.6,
},
});
//
this.flyTo();
} else {
this.searchResults = [];
this.$Message.warning("搜索失败,请换个关键词试试");
}
});
//
this.mapbox.on("click", (e) => {
if (this.marker) {
this.marker.remove();
}
this.marker = new mapboxgl.Marker({
offset: [0, -25],
draggable: this.draggable,
})
.setLngLat([e.lngLat.lng, e.lngLat.lat])
.addTo(this.mapbox);
if (this.decimal < 0) {
this.decimal = 0;
}
if (this.decimal > 14) {
this.decimal = 14;
}
let lng = e.lngLat.lng.toFixed(this.decimal);
let lat = e.lngLat.lat.toFixed(this.decimal);
this.currentValue = lng + ", " + lat;
this.$emit("on-click", this.currentValue);
.catch((err) => {
this.$Message.error((err && err.message) || "地图初始化失败");
});
},
flyTo() {
if (this.data) {
try {
let lngLat = this.data.split(",");
let lng = lngLat[0].trim();
let lat = lngLat[1].trim();
this.currentValue = this.data;
if (this.marker) {
this.marker.remove();
selectSearchResult(item) {
if (!item || !item.location) return;
const lng = item.location.lng;
const lat = item.location.lat;
this.placeMarker(lng, lat, 17);
this.searchResults = [];
this.keyword = item.name || this.keyword;
},
locateByAmap() {
if (!this.getAmapKey()) {
this.$Message.warning("请先在 config.js 配置高德地图 Key");
return;
}
this.marker = new mapboxgl.Marker({
offset: [0, -25],
draggable: true,
})
.setLngLat([lng, lat])
.addTo(this.mapbox);
this.mapbox.flyTo({
center: [lng, lat],
essential: true,
this.locating = true;
const loading = this.$Message.loading({
content: "正在定位...",
duration: 0,
});
} catch (error) {
this.$Message.error("您输入的坐标不合法");
}
this.ensureMap()
.then(() => getAmapLocation(this.getAmapKey(), this.getSecurityCode()))
.then((pos) => {
loading();
this.locating = false;
this.placeMarker(pos.lng, pos.lat, 17);
if (pos.accuracy && pos.accuracy > 1000) {
this.$Message.warning(
`定位成功,精度约 ${Math.round(pos.accuracy)} 米,请核对后确认`
);
} else {
this.currentValue = "";
if (this.marker) {
this.marker.remove();
this.$Message.success(
pos.address ? `定位成功:${pos.address}` : "定位成功"
);
}
this.mapbox.flyTo({
center: this.center,
zoom: 9,
essential: true,
})
.catch((err) => {
loading();
this.locating = false;
this.$Message.error(
(err && err.message) || "定位失败,请搜索或手动点击地图选点"
);
});
}
},
handleChange(v) {
handleChange() {
this.$emit("input", this.data);
this.$emit("on-change", this.data);
},
setData(value) {
if (!this.mapbox) {
this.init();
}
if (value != this.data) {
this.data = value;
this.$emit("input", this.data);
this.$emit("on-change", this.data);
}
if (this.showModal && this.map) {
this.syncMarkerFromValue();
}
},
handleFull() {
this.full = !this.full;
if (this.full) {
this.modalHeight = "100%";
} else {
this.modalHeight = "500px";
this.modalHeight = this.full ? "100%" : "500px";
this.$nextTick(() => {
if (this.map) {
this.map.resize();
}
setTimeout(() => {
this.mapbox.resize();
}, 10);
});
},
handleShow() {
if (!config.mapboxToken) {
this.$Message.warning("请先配置Mapbox地图的accessToken");
if (!this.getAmapKey()) {
this.$Message.warning("请先在 config.js 配置高德地图 Key");
return;
}
this.showModal = true;
},
changeModal(v) {
if (v) {
setTimeout(() => {
this.mapbox.resize();
this.flyTo();
}, 10);
this.$nextTick(() => {
this.ensureMap()
.then(() => {
this.map.resize();
this.syncMarkerFromValue();
})
.catch((err) => {
this.$Message.error((err && err.message) || "地图初始化失败");
});
});
} else {
this.searchResults = [];
}
},
handelSubmit() {
@ -502,9 +447,13 @@ export default {
},
},
beforeDestroy() {
// API
if (this.mapbox != null) {
this.mapbox.remove();
if (this.marker) {
this.marker.setMap(null);
this.marker = null;
}
if (this.map) {
this.map.destroy();
this.map = null;
}
},
watch: {
@ -512,49 +461,76 @@ export default {
this.setData(val);
},
},
mounted() {
this.init();
},
};
</script>
<style lang="less">
.mapboxgl-style-list {
display: none;
}
.mapboxgl-ctrl-group .mapboxgl-style-list button {
background: none;
border: none;
cursor: pointer;
display: block;
font-size: 14px;
padding: 8px 8px 6px;
text-align: right;
width: 100%;
height: auto;
<style lang="less">
.map-modal {
.ivu-modal-body {
padding: 0;
}
}
.mapboxgl-style-list button.active {
font-weight: bold;
.amap-panel {
position: relative;
}
.mapboxgl-style-list button:hover {
background-color: rgba(0, 0, 0, 0.05);
.amap-search {
position: absolute;
top: 12px;
left: 12px;
z-index: 120;
width: 320px;
}
.mapboxgl-style-list button + button {
border-top: 1px solid #ddd;
.amap-locate-btn {
margin-top: 8px;
background: #fff;
}
.mapboxgl-style-switcher {
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNTQuODQ5cHgiIGhlaWdodD0iNTQuODQ5cHgiIHZpZXdCb3g9IjAgMCA1NC44NDkgNTQuODQ5IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1NC44NDkgNTQuODQ5OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PGc+PGc+PGc+PHBhdGggZD0iTTU0LjQ5NywzOS42MTRsLTEwLjM2My00LjQ5bC0xNC45MTcsNS45NjhjLTAuNTM3LDAuMjE0LTEuMTY1LDAuMzE5LTEuNzkzLDAuMzE5Yy0wLjYyNywwLTEuMjU0LTAuMTA0LTEuNzktMC4zMThsLTE0LjkyMS01Ljk2OEwwLjM1MSwzOS42MTRjLTAuNDcyLDAuMjAzLTAuNDY3LDAuNTI0LDAuMDEsMC43MTZMMjYuNTYsNTAuODFjMC40NzcsMC4xOTEsMS4yNTEsMC4xOTEsMS43MjksMEw1NC40ODgsNDAuMzNDNTQuOTY0LDQwLjEzOSw1NC45NjksMzkuODE3LDU0LjQ5NywzOS42MTR6Ii8+PHBhdGggZD0iTTU0LjQ5NywyNy41MTJsLTEwLjM2NC00LjQ5MWwtMTQuOTE2LDUuOTY2Yy0wLjUzNiwwLjIxNS0xLjE2NSwwLjMyMS0xLjc5MiwwLjMyMWMtMC42MjgsMC0xLjI1Ni0wLjEwNi0xLjc5My0wLjMyMWwtMTQuOTE4LTUuOTY2TDAuMzUxLDI3LjUxMmMtMC40NzIsMC4yMDMtMC40NjcsMC41MjMsMC4wMSwwLjcxNkwyNi41NiwzOC43MDZjMC40NzcsMC4xOSwxLjI1MSwwLjE5LDEuNzI5LDBsMjYuMTk5LTEwLjQ3OUM1NC45NjQsMjguMDM2LDU0Ljk2OSwyNy43MTYsNTQuNDk3LDI3LjUxMnoiLz48cGF0aCBkPSJNMC4zNjEsMTYuMTI1bDEzLjY2Miw1LjQ2NWwxMi41MzcsNS4wMTVjMC40NzcsMC4xOTEsMS4yNTEsMC4xOTEsMS43MjksMGwxMi41NDEtNS4wMTZsMTMuNjU4LTUuNDYzYzAuNDc3LTAuMTkxLDAuNDgtMC41MTEsMC4wMS0wLjcxNkwyOC4yNzcsNC4wNDhjLTAuNDcxLTAuMjA0LTEuMjM2LTAuMjA0LTEuNzA4LDBMMC4zNTEsMTUuNDFDLTAuMTIxLDE1LjYxNC0wLjExNiwxNS45MzUsMC4zNjEsMTYuMTI1eiIvPjwvZz48L2c+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjwvc3ZnPg==);
background-position: center;
background-repeat: no-repeat;
background-size: 70%;
.amap-locate-only {
position: absolute;
top: 12px;
left: 12px;
z-index: 120;
}
.map-modal {
.ivu-modal-body {
padding: 0px;
.amap-search-list {
margin: 8px 0 0;
padding: 0;
list-style: none;
max-height: 240px;
overflow: auto;
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
li {
padding: 8px 12px;
cursor: pointer;
border-bottom: 1px solid #f0f0f0;
&:hover {
background: #f5f7fa;
}
.name {
color: #17233d;
font-size: 13px;
}
.addr {
margin-top: 2px;
color: #808695;
font-size: 12px;
}
}
}
.amap-container {
min-height: 360px;
}
.locate-tip {
margin-top: 6px;
color: #808695;
font-size: 12px;
line-height: 1.4;
}
.modal-fullscreen {
position: absolute;
right: 42px;
top: 10px;
font-size: 18px;
color: #999;
}
</style>

581
src/views/my-components/hiver/mapLocate.vue

@ -1,583 +1,12 @@
<template>
<div>
<div style="display: flex">
<Input
v-model="data"
@on-change="handleChange"
:placeholder="placeholder"
:size="size"
:disabled="disabled"
:readonly="readonly"
:maxlength="maxlength"
:clearable="clearable"
v-if="showInput"
style="margin-right: 10px"
/>
<Button
:size="size"
:type="type"
:shape="shape"
:ghost="ghost"
:disabled="disabled"
:icon="icon"
@click="handleShow"
>{{ text }}</Button
>
</div>
<Modal
v-model="showModal"
:mask-closable="false"
:width="width"
:fullscreen="full"
@on-visible-change="changeModal"
class="map-modal"
>
<div slot="header">
<div class="ivu-modal-header-inner">{{ text }}</div>
<a @click="handleFull" class="modal-fullscreen">
<Icon
v-show="!full"
type="ios-expand"
class="model-fullscreen-icon"
/>
<Icon
v-show="full"
type="ios-contract"
class="model-fullscreen-icon"
/>
</a>
<a @click="showModal = false" class="ivu-modal-close">
<Icon type="ios-close" class="ivu-icon-ios-close" />
</a>
</div>
<div :id="id" :style="{ width: '100%', height: modalHeight }"></div>
<div slot="footer">
<Row align="middle" justify="space-between">
<Alert show-icon style="margin-bottom: 0" v-show="currentValue"
>当前坐标{{ currentValue }}</Alert
>
<div v-show="!currentValue"></div>
<div>
<Button type="text" @click="showModal = false" v-if="!preview"
>取消</Button
>
<Button @click="showModal = false" v-if="preview">关闭</Button>
<Button type="primary" @click="handelSubmit" v-if="!preview"
>确认提交</Button
>
</div>
</Row>
</div>
</Modal>
</div>
<Map v-bind="$attrs" v-on="$listeners" />
</template>
<script>
import mapboxgl from "mapbox-gl";
import MapboxGeocoder from "@mapbox/mapbox-gl-geocoder";
import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css';
import Map from "@/views/my-components/hiver/map";
export default {
name: "mapLocate",
props: {
id: {
type: String,
default: "map",
},
value: {
type: String,
default: "",
},
showInput: {
type: Boolean,
default: true,
},
preview: {
type: Boolean,
default: false,
},
text: {
type: String,
default: "地图选点",
},
width: {
type: Number,
default: 900,
},
pitch: {
type: Number,
default: 0,
},
decimal: {
type: Number,
default: 6,
},
draggable: {
type: Boolean,
default: true,
},
size: String,
type: String,
shape: String,
ghost: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
clearable: {
type: Boolean,
default: true,
},
placeholder: {
type: String,
default: "输入坐标或选择地点",
},
maxlength: Number,
readonly: {
type: Boolean,
default: false,
},
icon: {
type: String,
default: "md-locate",
},
styles: {
type: String,
default: "mapbox://styles/mapbox/streets-v11",
},
center: {
type: Array,
default: function () {
return [116.35, 39.85];
},
},
zoom: {
type: Number,
default: 9,
},
compact: {
type: Boolean,
default: true,
},
customAttribution: {
type: String,
default: "",
},
searchable: {
type: Boolean,
default: true,
},
changeStyle: {
type: Boolean,
default: true,
},
navigation: {
type: Boolean,
default: true,
},
locate: {
type: Boolean,
default: true,
},
fullscreen: {
type: Boolean,
default: false,
},
building3D: {
type: Boolean,
default: true,
},
},
data() {
return {
data: this.value,
mapbox: null,
currentValue: "",
showModal: false,
full: false,
modalHeight: "500px",
marker: null,
};
},
methods: {
init() {
if (!config.mapboxToken) {
return;
}
mapboxgl.accessToken = config.mapboxToken;
this.mapbox = new mapboxgl.Map({
container: this.id,
style: this.styles,
center: this.center,
zoom: this.zoom,
pitch: this.pitch,
attributionControl: false,
});
//
this.mapbox.addControl(
new mapboxgl.AttributionControl({
compact: this.compact,
customAttribution: this.customAttribution,
})
);
//
if (this.searchable) {
this.mapbox.addControl(
new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
mapboxgl: mapboxgl,
})
);
}
//
class ChangeStyleControl {
getDefaultPosition() {
const defaultPosition = "top-right";
return defaultPosition;
}
onAdd(map) {
this.map = map;
this.controlContainer = document.createElement("div");
this.controlContainer.classList.add("mapboxgl-ctrl");
this.controlContainer.classList.add("mapboxgl-ctrl-group");
this.mapStyleContainer = document.createElement("div");
this.styleButton = document.createElement("button");
this.styleButton.type = "button";
this.mapStyleContainer.classList.add("mapboxgl-style-list");
const defaultStyle = "街道地图";
const styles = [
{ title: "街道地图", uri: "mapbox://styles/mapbox/streets-v11" },
{
title: "卫星街道",
uri: "mapbox://styles/mapbox/satellite-streets-v11",
},
{ title: "暗黑风格", uri: "mapbox://styles/mapbox/dark-v10" },
{ title: "明亮风格", uri: "mapbox://styles/mapbox/light-v10" },
{ title: "户外地图", uri: "mapbox://styles/mapbox/outdoors-v11" },
{ title: "卫星地图", uri: "mapbox://styles/mapbox/satellite-v9" },
];
for (const style of styles) {
const styleElement = document.createElement("button");
styleElement.type = "button";
styleElement.innerText = style.title;
styleElement.classList.add(
style.title.replace(/[^a-z0-9-]/gi, "_")
);
styleElement.dataset.uri = JSON.stringify(style.uri);
styleElement.addEventListener("click", (event) => {
const srcElement = event.srcElement;
if (srcElement.classList.contains("active")) {
return;
}
this.map.setStyle(JSON.parse(srcElement.dataset.uri));
this.map.on("data", (e) => {
if (e.isSourceLoaded) {
const labelList = this.map
.getStyle()
.layers.filter((layer) => {
return /-label/.test(layer.id);
});
for (let labelLayer of labelList) {
this.map.setLayoutProperty(labelLayer.id, "text-field", [
"coalesce",
["get", "name_zh-Hans"],
["get", "name"],
]);
}
}
});
this.mapStyleContainer.style.display = "none";
this.styleButton.style.display = "block";
const elms =
this.mapStyleContainer.getElementsByClassName("active");
while (elms[0]) {
elms[0].classList.remove("active");
}
srcElement.classList.add("active");
});
if (style.title === defaultStyle) {
styleElement.classList.add("active");
}
this.mapStyleContainer.appendChild(styleElement);
}
this.styleButton.classList.add("mapboxgl-ctrl-icon");
this.styleButton.classList.add("mapboxgl-style-switcher");
this.styleButton.addEventListener("click", () => {
this.styleButton.style.display = "none";
this.mapStyleContainer.style.display = "block";
});
document.addEventListener("click", this.onDocumentClick);
this.controlContainer.appendChild(this.styleButton);
this.controlContainer.appendChild(this.mapStyleContainer);
return this.controlContainer;
}
onRemove() {
if (
!this.controlContainer ||
!this.controlContainer.parentNode ||
!this.map ||
!this.styleButton
) {
return;
}
this.styleButton.removeEventListener("click", this.onDocumentClick);
this.controlContainer.parentNode.removeChild(this.controlContainer);
document.removeEventListener("click", this.onDocumentClick);
this.map = undefined;
}
onDocumentClick(event) {
if (
this.controlContainer &&
!this.controlContainer.contains(event.target) &&
this.mapStyleContainer &&
this.styleButton
) {
this.mapStyleContainer.style.display = "none";
this.styleButton.style.display = "block";
}
}
}
if (this.changeStyle) {
this.mapbox.addControl(new ChangeStyleControl());
}
//
if (this.navigation) {
this.mapbox.addControl(new mapboxgl.NavigationControl());
}
//
if (this.locate) {
this.mapbox.addControl(
new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true,
},
trackUserLocation: true,
})
);
}
//
if (this.fullscreen) {
this.mapbox.addControl(
new mapboxgl.FullscreenControl({
container: document.querySelector("body"),
})
);
}
this.mapbox.on("load", () => {
//
const labelList = this.mapbox.getStyle().layers.filter((layer) => {
return /-label/.test(layer.id);
});
for (let labelLayer of labelList) {
this.mapbox.setLayoutProperty(labelLayer.id, "text-field", [
"coalesce",
["get", "name_zh-Hans"],
["get", "name"],
]);
}
// 3D
if (this.building3D) {
this.mapbox.addLayer({
id: "3d-buildings",
source: "composite",
"source-layer": "building",
filter: ["==", "extrude", "true"],
type: "fill-extrusion",
minzoom: 15,
paint: {
"fill-extrusion-color": "#aaa",
"fill-extrusion-height": [
"interpolate",
["linear"],
["zoom"],
15,
0,
15.05,
["get", "height"],
],
"fill-extrusion-base": [
"interpolate",
["linear"],
["zoom"],
15,
0,
15.05,
["get", "min_height"],
],
"fill-extrusion-opacity": 0.6,
},
});
//
this.flyTo();
}
});
//
this.mapbox.on("click", (e) => {
if (this.preview) {
return;
}
if (this.marker) {
this.marker.remove();
}
this.marker = new mapboxgl.Marker({
offset: [0, -25],
draggable: this.draggable,
})
.setLngLat([e.lngLat.lng, e.lngLat.lat])
.addTo(this.mapbox);
if (this.decimal < 0) {
this.decimal = 0;
}
if (this.decimal > 14) {
this.decimal = 14;
}
let lng = e.lngLat.lng.toFixed(this.decimal);
let lat = e.lngLat.lat.toFixed(this.decimal);
this.currentValue = lng + ", " + lat;
this.$emit("on-click", this.currentValue);
});
},
flyTo() {
if (this.data) {
try {
let lngLat = this.data.split(",");
let lng = lngLat[0].trim();
let lat = lngLat[1].trim();
this.currentValue = this.data;
if (this.marker) {
this.marker.remove();
}
this.marker = new mapboxgl.Marker({
offset: [0, -25],
draggable: true,
})
.setLngLat([lng, lat])
.addTo(this.mapbox);
this.mapbox.flyTo({
center: [lng, lat],
essential: true,
});
} catch (error) {
this.$Message.error("您输入的坐标不合法");
}
} else {
this.currentValue = "";
if (this.marker) {
this.marker.remove();
}
this.mapbox.flyTo({
center: this.center,
zoom: 9,
essential: true,
});
}
},
handleChange(v) {
this.$emit("input", this.data);
this.$emit("on-change", this.data);
},
setData(value) {
if (!this.mapbox) {
this.init();
}
if (value != this.data) {
this.data = value;
this.$emit("input", this.data);
this.$emit("on-change", this.data);
}
},
handleFull() {
this.full = !this.full;
if (this.full) {
this.modalHeight = "100%";
} else {
this.modalHeight = "500px";
}
setTimeout(() => {
this.mapbox.resize();
}, 10);
},
handleShow() {
if (!config.mapboxToken) {
this.$Message.warning("请先配置Mapbox地图的accessToken");
return;
}
this.showModal = true;
},
changeModal(v) {
if (v) {
setTimeout(() => {
this.mapbox.resize();
this.flyTo();
}, 10);
}
},
handelSubmit() {
if (!this.currentValue) {
this.$Message.warning("请在地图中点击鼠标选择一个地点");
return;
}
this.data = this.currentValue;
this.$emit("input", this.data);
this.$emit("on-change", this.data);
this.showModal = false;
},
},
beforeDestroy() {
// API
if (this.mapbox != null) {
this.mapbox.remove();
}
},
watch: {
value(val) {
this.setData(val);
},
},
mounted() {
this.init();
},
components: { Map },
inheritAttrs: false,
};
</script>
<style lang="less">
.mapboxgl-style-list {
display: none;
}
.mapboxgl-ctrl-group .mapboxgl-style-list button {
background: none;
border: none;
cursor: pointer;
display: block;
font-size: 14px;
padding: 8px 8px 6px;
text-align: right;
width: 100%;
height: auto;
}
.mapboxgl-style-list button.active {
font-weight: bold;
}
.mapboxgl-style-list button:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.mapboxgl-style-list button + button {
border-top: 1px solid #ddd;
}
.mapboxgl-style-switcher {
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNTQuODQ5cHgiIGhlaWdodD0iNTQuODQ5cHgiIHZpZXdCb3g9IjAgMCA1NC44NDkgNTQuODQ5IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1NC44NDkgNTQuODQ5OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PGc+PGc+PGc+PHBhdGggZD0iTTU0LjQ5NywzOS42MTRsLTEwLjM2My00LjQ5bC0xNC45MTcsNS45NjhjLTAuNTM3LDAuMjE0LTEuMTY1LDAuMzE5LTEuNzkzLDAuMzE5Yy0wLjYyNywwLTEuMjU0LTAuMTA0LTEuNzktMC4zMThsLTE0LjkyMS01Ljk2OEwwLjM1MSwzOS42MTRjLTAuNDcyLDAuMjAzLTAuNDY3LDAuNTI0LDAuMDEsMC43MTZMMjYuNTYsNTAuODFjMC40NzcsMC4xOTEsMS4yNTEsMC4xOTEsMS43MjksMEw1NC40ODgsNDAuMzNDNTQuOTY0LDQwLjEzOSw1NC45NjksMzkuODE3LDU0LjQ5NywzOS42MTR6Ii8+PHBhdGggZD0iTTU0LjQ5NywyNy41MTJsLTEwLjM2NC00LjQ5MWwtMTQuOTE2LDUuOTY2Yy0wLjUzNiwwLjIxNS0xLjE2NSwwLjMyMS0xLjc5MiwwLjMyMWMtMC42MjgsMC0xLjI1Ni0wLjEwNi0xLjc5My0wLjMyMWwtMTQuOTE4LTUuOTY2TDAuMzUxLDI3LjUxMmMtMC40NzIsMC4yMDMtMC40NjcsMC41MjMsMC4wMSwwLjcxNkwyNi41NiwzOC43MDZjMC40NzcsMC4xOSwxLjI1MSwwLjE5LDEuNzI5LDBsMjYuMTk5LTEwLjQ3OUM1NC45NjQsMjguMDM2LDU0Ljk2OSwyNy43MTYsNTQuNDk3LDI3LjUxMnoiLz48cGF0aCBkPSJNMC4zNjEsMTYuMTI1bDEzLjY2Miw1LjQ2NWwxMi41MzcsNS4wMTVjMC40NzcsMC4xOTEsMS4yNTEsMC4xOTEsMS43MjksMGwxMi41NDEtNS4wMTZsMTMuNjU4LTUuNDYzYzAuNDc3LTAuMTkxLDAuNDgtMC41MTEsMC4wMS0wLjcxNkwyOC4yNzcsNC4wNDhjLTAuNDcxLTAuMjA0LTEuMjM2LTAuMjA0LTEuNzA4LDBMMC4zNTEsMTUuNDFDLTAuMTIxLDE1LjYxNC0wLjExNiwxNS45MzUsMC4zNjEsMTYuMTI1eiIvPjwvZz48L2c+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjwvc3ZnPg==);
background-position: center;
background-repeat: no-repeat;
background-size: 70%;
}
.map-modal {
.ivu-modal-body {
padding: 0px;
}
}
</style>
Loading…
Cancel
Save