实现效果图

地图组件完整代码
// 天地图组件
<template>
<div class="map-container">
<div id="mapCon"></div>
</div>
</template>
<script>
import markerIcon from "@/assets/images/marker-icon.png";
let handler;
export default {
name: "MapEle",
props: {
mapData: {
type: Object,
default: () => {
return {
pointData: [],
lineData: {},
rangeData: {},
};
},
},
},
data() {
return {
map: null,
zoom: 14,
longitude: 117.119529,
latitude: 36.650396,
};
},
watch: {
// 只关注mapData数据源即可,监听mapData数据源改变清空地图,重新打点\线\面
mapData: {
handler(newV, oldV) {
this.map.clearOverLays();
this.drawMap();
},
deep: true,
},
},
methods: {
initMap() {
this.map = new T.Map("mapCon");
this.map.centerAndZoom(
new T.LngLat(this.longitude, this.latitude),
this.zoom
);
this.drawMap();
},
drawMap() {
this.drawPoint();
this.drawLine();
this.drawRange();
},
// 绘制点
drawPoint() {
this.mapData?.pointData?.forEach((item) => {
var marker = new T.Marker(new T.LngLat(item[0], item[1]));
this.map.addOverLay(marker);
var label = new T.Label({
text: item[2],
position: new T.LngLat(item[0], item[1]),
offset: new T.Point(0, 0),
style: {
fontSize: "14px",
color: "#000",
backgroundColor: "rgba(255, 255, 255, 0.8)",
padding: "5px",
borderRadius: "5px",
border: "1px solid #ccc",
},
});
this.map.addOverLay(label);
});
},
// 绘制线
drawLine() {
if (this.mapData.lineData) {
let coordinates;
if (
this.mapData.lineData.length > 0 &&
Array.isArray(this.mapData.lineData[0])
) {
coordinates = this.mapData.lineData[0].map(
(coord) => new T.LngLat(coord[0], coord[1])
);
} else {
coordinates = this.mapData.lineData.map(
(coord) => new T.LngLat(coord[0], coord[1])
);
}
if (coordinates.length > 1) {
const polyline = new T.Polyline(coordinates, {
color: "#ff0000",
weight: 3,
});
this.map.addOverLay(polyline);
this.map.panTo(coordinates[0]);
}
}
},
// 绘制面
drawRange() {
if (this.mapData.rangeData) {
let coordinates;
if (
this.mapData.rangeData.length > 0 &&
Array.isArray(this.mapData.rangeData[0])
) {
coordinates = this.mapData.rangeData[0].map(
(coord) => new T.LngLat(coord[0], coord[1])
);
} else {
coordinates = this.mapData.rangeData.map(
(coord) => new T.LngLat(coord[0], coord[1])
);
}
if (coordinates.length > 2) {
var polygon = new T.Polygon(coordinates, {
color: "green",
weight: 3,
opacity: 0.5,
fillColor: "red",
fillOpacity: 0.5,
strokeStyle: "solid",
});
this.map.addOverLay(polygon);
if (coordinates.length > 0) {
const center = this.getPolygonCenter(coordinates);
// this.map.panTo(center);
}
}
}
},
// 打点
async handlePoint(i) {
if (handler) handler.close(); // 如果已有打点工具打开,先关闭
handler = new T.MarkTool(this.map, {
follow: true,
icon: new T.Icon({
iconUrl: markerIcon,
iconSize: new T.Point(25, 41),
}),
});
handler.open(); // 打开打点工具
handler.addEventListener("mouseup", async (e) => {
try {
// 获取打点位置的名称
const locationName = await this.getLocationName(
e.currentLnglat.lng,
e.currentLnglat.lat
);
// 通知父组件打点完成
this.$emit(
"finishPoint",
[e.currentLnglat.lng, e.currentLnglat.lat, locationName],
i
);
} catch (error) {
console.error("获取位置名称失败:", error);
} finally {
handler.close(); // 关闭打点工具
}
});
},
// 画线
handleLine() {
if (this.handler) this.handler.close(); // 如果已有绘线工具打开,先关闭
this.handler = new T.PolylineTool(this.map, {
color: "#ff0000", // 线的颜色
weight: 3, // 线的宽度
});
this.handler.open(); // 打开绘线工具
this.handler.addEventListener("draw", (e) => {
// 将绘制的线的坐标转换为二维数组
const lineCoordinates = e.currentLnglats.map((item) => [
item.lng,
item.lat,
]);
// 发射事件,通知父组件绘线完成
this.$emit("finishLine", lineCoordinates);
});
},
// 画范围
handleRange() {
if (this.handler) this.handler.close(); // 如果已有绘图工具打开,先关闭
this.handler = new T.PolygonTool(this.map, {
color: "#ff0000", // 多边形边框颜色
weight: 3, // 多边形边框宽度
});
this.handler.open(); // 打开绘图工具
this.handler.addEventListener("draw", (e) => {
// 将绘制的多边形的坐标转换为二维数组
const polygonCoordinates = e.currentLnglats.map((item) => [
item.lng,
item.lat,
]);
// 通知父组件绘制完成
this.$emit("finishRange", polygonCoordinates);
});
},
// 计算多边形的中心点
getPolygonCenter(coordinates) {
let sumLng = 0;
let sumLat = 0;
coordinates.forEach((coord) => {
sumLng += coord.lng;
sumLat += coord.lat;
});
const centerLng = sumLng / coordinates.length;
const centerLat = sumLat / coordinates.length;
return new T.LngLat(centerLng, centerLat);
},
// 根据经纬度获取当前地名称
getLocationName(lng, lat) {
const geocoder = new T.Geocoder();
return new Promise((resolve, reject) => {
geocoder.getLocation(new T.LngLat(lng, lat), (result) => {
if (result.getStatus() === 0) {
const address = result.getAddress();
resolve(address); // address即为当前点名称
} else {
reject(result.getMsg());
}
});
});
},
},
mounted() {
this.initMap();
},
};
</script>
<style scoped lang="scss">
.map-container {
position: relative;
height: 70vh;
}
}
</style>
使用地图组件完整代码
// 编辑地图信息弹窗
<template>
<div class="container-box">
<el-dialog
v-if="visible"
:title="title"
:visible.sync="visible"
width="90vw"
custom-class="dialog-class"
>
<el-row :gutter="20">
<el-col :span="12">
<MapEle
ref="mapEleRef"
:mapData="mapData"
@finishPoint="finishPoint"
@finishLine="finishLine"
@finishRange="finishRange"
/>
</el-col>
<el-col :span="12">
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="名称" prop="name">
<el-input
v-model="form.name"
placeholder="请输入名称"
/> </el-form-item
></el-col>
<el-col :span="24">
<el-form-item label="起点位置" prop="startLocation">
<el-input
v-model="form.startLocation"
placeholder="请选择起点位置"
readonly
disabled
>
<template slot="append">
<el-button
icon="el-icon-location-outline"
@click="drawPoint(0)"
></el-button>
<el-divider
direction="vertical"
class="divider-class"
></el-divider>
<el-button
icon="el-icon-delete"
@click="delPoint(0)"
></el-button> </template
></el-input> </el-form-item
></el-col>
<el-col :span="24">
<el-form-item label="终点位置" prop="endLocation">
<el-input
v-model="form.endLocation"
placeholder="请选择终点位置"
readonly
disabled
>
<template slot="append">
<el-button
icon="el-icon-location-outline"
@click="drawPoint(1)"
></el-button>
<el-divider
direction="vertical"
class="divider-class"
></el-divider>
<el-button
icon="el-icon-delete"
@click="delPoint(1)"
></el-button>
</template> </el-input></el-form-item
></el-col>
<el-col :span="24">
<el-form-item label="线" prop="pipelayer">
<el-input
v-model="form.pipelayer"
disabled
placeholder="请绘制线"
>
<template slot="append">
<el-button
icon="el-icon-edit-outline"
@click="drawLine"
></el-button>
<el-divider
direction="vertical"
class="divider-class"
></el-divider>
<el-button
icon="el-icon-delete"
@click="delLine"
></el-button>
</template> </el-input></el-form-item
></el-col>
<el-col :span="24">
<el-form-item label="区域" prop="piperange">
<el-input
v-model="form.piperange"
disabled
placeholder="请绘制区域"
><template slot="append">
<el-button
icon="el-icon-edit-outline"
@click="drawRange"
></el-button>
<el-divider
direction="vertical"
class="divider-class"
></el-divider>
<el-button
icon="el-icon-delete"
@click="delRange"
></el-button>
</template>
</el-input> </el-form-item
></el-col>
<el-col :span="24">
<el-form-item label="高度差" prop="altitude">
<el-input
v-model="form.altitude"
placeholder="请输入高度差"
/> </el-form-item
></el-col>
<el-col :span="24">
<el-form-item label="面积(㎡)" prop="heatArea">
<el-input
v-model="form.heatArea"
placeholder="请输入面积"
/> </el-form-item
></el-col>
</el-row>
</el-form>
</el-col>
</el-row>
<div slot="footer" class="dialog-footer">
<el-button type="primary" v-if="!enterprise" @click="submitForm"
>确 定</el-button
>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { cloneDeep as _cloneDeep } from "lodash";
import { updateApi, getGateway } from "@/api/basicData/mainPipeNetwork.js";
import MapEle from "@/components/MapEle";
export default {
components: { MapEle },
data() {
return {
title: "",
visible: false,
defaultForm: {
name: undefined,
startLocation: undefined,
endLocation: undefined,
startLongitude: undefined,
startLatitude: undefined,
endLongitude: undefined,
endLatitude: undefined,
altitude: undefined,
heatArea: undefined,
pipelayer: undefined,
piperange: undefined,
},
rules: {
name: [
{ required: true, message: "主网名称不能为空", trigger: "blur" },
],
startLocation: [
{ required: true, message: "起点位置不能为空", trigger: "blur" },
],
endLocation: [
{ required: true, message: "终点位置不能为空", trigger: "blur" },
],
pipelayer: [{ required: true, message: "线不能为空", trigger: "blur" }],
piperange: [
{ required: true, message: "区域不能为空", trigger: "blur" },
],
altitude: [
{ required: true, message: "高度差不能为空", trigger: "blur" },
],
heatArea: [
{ required: true, message: "面积不能为空", trigger: "blur" },
],
},
form: {},
enterprise: false, // 企业标识
mapVisible: false,
mapFlag: 1,
mapData: {
pointData: [],
lineData: {
coordinates: [],
},
rangeData: {
coordinates: [],
},
},
};
},
created() {
// 判斷當前用戶是否有企业标识 qiye
if (
this.$store.state.user?.roles.length &&
this.$store.state.user?.roles.indexOf("qiye") >= 0
) {
this.enterprise = true;
}
},
methods: {
// 绘制点,这里的i标识起点(0)和终点(1)(根据个人情况使用)
drawPoint(i) {
// 绘制当前点时先将之前点清空,再绘制
this.delPoint(i);
this.$nextTick(() => {
this.$refs.mapEleRef.handlePoint(i);
});
},
// 绘制线
drawLine() {
this.delLine();
this.$nextTick(() => {
this.$refs.mapEleRef.handleLine();
});
},
// 绘制面
drawRange() {
this.delRange();
this.$nextTick(() => {
this.$refs.mapEleRef.handleRange();
});
},
// 删除点
delPoint(i) {
// 获取要删除的点的经纬度
const { longitude, latitude } =
i === 1
? {
longitude: this.form.endLongitude,
latitude: this.form.endLatitude,
}
: {
longitude: this.form.startLongitude,
latitude: this.form.startLatitude,
};
// 从 mapData.pointData 中移除对应的点
this.mapData.pointData = this.mapData.pointData.filter((item) => {
return !(item[0] === longitude && item[1] === latitude);
});
// 清空表单中的位置信息
if (i === 1) {
this.form.endLocation = "";
this.form.endLongitude = "";
this.form.endLatitude = "";
} else {
this.form.startLocation = "";
this.form.startLongitude = "";
this.form.startLatitude = "";
}
},
// 删除线
delLine() {
this.form.pipelayer = "";
this.$forceUpdate();
this.mapData.lineData = [];
},
// 删除面
delRange() {
this.form.piperange = "";
this.$forceUpdate();
this.mapData.rangeData = [];
},
// 绘制完点后触发的方法
finishPoint(arr, i) {
// 将点的坐标和名称保存到 mapData.pointData 中
this.mapData.pointData.push(arr);
// 根据索引 i 更新表单中的起点或终点信息
const updateForm = (location, longitude, latitude) => {
this.form[location] = arr[2];
this.form[longitude] = arr[0];
this.form[latitude] = arr[1];
};
if (i === 1) {
updateForm("endLocation", "endLongitude", "endLatitude");
} else {
updateForm("startLocation", "startLongitude", "startLatitude");
}
},
// 绘制完线后触发的方法
finishLine(arr) {
this.mapData.lineData = [arr];
this.form.pipelayer = JSON.stringify(arr);
this.$forceUpdate();
},
// 绘制完面后触发的方法
finishRange(arr) {
this.mapData.rangeData = [arr];
this.form.piperange = JSON.stringify(arr);
this.$forceUpdate();
},
// 打开编辑页面
async open(row) {
try {
// 获取地图数据
const res = await this.getMapData(row);
const tempData = JSON.parse(res?.data);
// 初始化地图数据
this.mapData = {
pointData: [
[row.startLongitude, row.startLatitude, row.startLocation],
[row.endLongitude, row.endLatitude, row.endLocation],
],
lineData: tempData?.features?.[0]?.pipelayer?.coordinates,
rangeData: tempData?.features?.[0]?.piperange?.coordinates,
};
// 初始化表单数据
this.form = _cloneDeep(this.defaultForm);
this.form = { ...row };
this.form.pipelayer = JSON.stringify(
tempData?.features?.[0]?.pipelayer?.coordinates[0] || []
);
this.form.piperange = JSON.stringify(
tempData?.features?.[0]?.piperange?.coordinates[0] || []
);
// 设置对话框标题和可见性
this.title = "修改";
this.visible = true;
} catch (error) {
console.error("打开对话框时出错:", error);
}
},
// 获取地图线和区域数据
getMapData(row) {
return new Promise((resolve, reject) => {
getGateway({ bh: row.code })
.then((response) => {
resolve(response);
})
.catch((error) => {
reject(error);
});
});
},
// 关闭弹窗页`
cancel() {
this.visible = false;
},
// 提交
submitForm() {
this.$refs["form"].validate((valid) => {
if (valid) {
// 构建提交参数
const params = {
...this.form,
layer: {
pipeLayerVar: this.form.pipelayer,
pipeRangeVar: `[${this.form.piperange}]`,
},
};
// 调用更新接口
updateApi(params)
.then((response) => {
this.$modal.msgSuccess("修改成功");
this.cancel();
this.$emit("refreshList");
})
.catch((error) => {
this.$modal.msgError("修改失败:" + error.message);
});
} else {
this.$modal.msgWarning("表单验证失败,请检查输入内容");
}
});
},
},
};
</script>
<style scoped lang="scss">
.container-box {
::v-deep .el-dialog.dialog-class {
height: auto;
height: 90vh;
overflow-y: auto;
}
::v-deep .el-dialog__body {
height: 75vh !important;
}
}
.divider-class {
margin: 0 20px;
}
</style>
文档:http://lbs.tianditu.gov.cn/api/js4.0/examples.html