路线规划
This commit is contained in:
47
src/api/line/line.js
Normal file
47
src/api/line/line.js
Normal file
@ -0,0 +1,47 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询线路规划列表
|
||||
export function listLine(query) {
|
||||
return request({
|
||||
url: '/line/line/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询线路规划详细
|
||||
export function getLine(lineId,uuid) {
|
||||
return request({
|
||||
url: '/line/line/' + lineId + '/' + uuid,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增线路规划
|
||||
export function addLine(data) {
|
||||
return request({
|
||||
url: '/line/line',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改线路规划
|
||||
export function updateLine(data) {
|
||||
return request({
|
||||
url: '/line/line',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除线路规划
|
||||
export function delLine(lineId) {
|
||||
return request({
|
||||
url: '/line/line',
|
||||
method: 'delete',
|
||||
data: {
|
||||
ids: lineId
|
||||
}
|
||||
})
|
||||
}
|
BIN
src/assets/images/map-pin.png
Normal file
BIN
src/assets/images/map-pin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 102 KiB |
29
src/utils/map.js
Normal file
29
src/utils/map.js
Normal file
@ -0,0 +1,29 @@
|
||||
import AMapLoader from "@amap/amap-jsapi-loader";
|
||||
|
||||
// 初始化地图
|
||||
export const loadAMap = async (plugins) => {
|
||||
try {
|
||||
const AMap = await AMapLoader.load({
|
||||
key: "377d7c36dd385e2a722f29d4c6e1ffbf", // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||
plugins, // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||
AMapUI: {
|
||||
// 是否加载 AMapUI,缺省不加载
|
||||
version: "1.1", // AMapUI 版本
|
||||
plugins: ["overlay/SimpleMarker"], // 需要加载的 AMapUI ui插件
|
||||
},
|
||||
Loca: {
|
||||
// 是否加载 Loca, 缺省不加载
|
||||
version: "2.0", // Loca 版本
|
||||
},
|
||||
});
|
||||
// const map = new AMap.Map("map-container", {
|
||||
// zoom: 16, // 缩放级别
|
||||
// center: [117.290345, 31.797813], // 中心点
|
||||
// });
|
||||
return AMap;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new Error("地图创建失败");
|
||||
}
|
||||
};
|
@ -10,16 +10,28 @@
|
||||
<div class="search-bar">
|
||||
<el-row :gutter="10">
|
||||
<!-- <el-col :span="7"> -->
|
||||
<el-button type="info" size="small">返回上层</el-button>
|
||||
<el-button type="info" size="small" @click="back"
|
||||
>返回上层</el-button
|
||||
>
|
||||
<!-- </el-col> -->
|
||||
<el-col :span="10">
|
||||
<el-input size="small"></el-input>
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
size="small"
|
||||
@keypress.enter="searchPoint"
|
||||
placeholder="请输入地名或点击地图选点"
|
||||
></el-input>
|
||||
</el-col>
|
||||
<!-- <el-col :span="7"> -->
|
||||
<el-button type="primary" size="small" icon="search"
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="search"
|
||||
@click="searchPoint"
|
||||
>搜索</el-button
|
||||
>
|
||||
<!-- </el-col> -->
|
||||
<el-button type="primary" plain size="small">卫星模式</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
<div id="map-container"></div>
|
||||
@ -76,13 +88,13 @@
|
||||
placeholder="请输入报警时段"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="围栏地图数据" prop="fencePath">
|
||||
<!-- <el-form-item label="围栏地图数据" prop="fencePath">
|
||||
<el-input
|
||||
v-model="form.fencePath"
|
||||
type="textarea"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-button
|
||||
@ -113,16 +125,22 @@
|
||||
import tab from "@/plugins/tab";
|
||||
import { onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import AMapLoader from "@amap/amap-jsapi-loader";
|
||||
// import AMapLoader from "@amap/amap-jsapi-loader";
|
||||
import { addFence, updateFence, getFence } from "@/api/fence";
|
||||
import markBsPng from "@/assets/images/mark_bs.png";
|
||||
import mapPinPng from "@/assets/images/map-pin.png";
|
||||
import { loadAMap } from "@/utils/map";
|
||||
|
||||
const route = useRoute();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const AMap = shallowRef(null);
|
||||
const map = shallowRef(null);
|
||||
const placeSearch = shallowRef(null);
|
||||
// const fencePaths = ref([]);
|
||||
const searchResultMarkers = shallowRef([]); // 搜索结果表笔列表
|
||||
const turningPointMarker = shallowRef([]);
|
||||
const resultInfoWindow = shallowRef(null);
|
||||
const fencePolygon = shallowRef(null);
|
||||
const searchKeyword = ref("");
|
||||
const data = reactive({
|
||||
form: {},
|
||||
rules: {
|
||||
@ -145,60 +163,44 @@ const data = reactive({
|
||||
|
||||
const { form, rules } = toRefs(data);
|
||||
|
||||
// 初始化地图
|
||||
const initMap = async () => {
|
||||
try {
|
||||
AMap.value = await AMapLoader.load({
|
||||
key: "377d7c36dd385e2a722f29d4c6e1ffbf", // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
// key: "7891f1238368a895ff1967c79643102d", // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||
plugins: [
|
||||
// "AMap.Driving",
|
||||
"AMap.Scale",
|
||||
"AMap.PlaceSearch",
|
||||
// "AMap.AutoComplete",
|
||||
// "AMap.Geocoder",
|
||||
// "AMap.TruckDriving",
|
||||
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||
AMapUI: {
|
||||
// 是否加载 AMapUI,缺省不加载
|
||||
version: "1.1", // AMapUI 版本
|
||||
plugins: ["overlay/SimpleMarker"], // 需要加载的 AMapUI ui插件
|
||||
},
|
||||
Loca: {
|
||||
// 是否加载 Loca, 缺省不加载
|
||||
version: "2.0", // Loca 版本
|
||||
},
|
||||
});
|
||||
// geocoder.value = new AMap.value.Geocoder();
|
||||
map.value = new AMap.value.Map("map-container", {
|
||||
zoom: 16, // 缩放级别
|
||||
center: [117.290345, 31.797813], // 中心点
|
||||
city: "合肥",
|
||||
});
|
||||
map.value.on("click", handleMapClick);
|
||||
// 地点搜索插件
|
||||
placeSearch.value = new AMap.value.PlaceSearch({
|
||||
// map: map.value,
|
||||
city: "0551",
|
||||
});
|
||||
fencePolygon.value = new AMap.value.Polygon({});
|
||||
map.value.add(fencePolygon.value);
|
||||
|
||||
map.value.addControl(new AMap.value.Scale());
|
||||
return "success";
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMapClick = (ev) => {
|
||||
console.log(ev);
|
||||
if (map.value.getAllOverlays("polygon").length === 0) {
|
||||
map.value.add(fencePolygon.value);
|
||||
}
|
||||
|
||||
form.value.fencePath.push(ev.lnglat);
|
||||
console.log(JSON.stringify(form.value.fencePath));
|
||||
|
||||
turningPointMarker.value.push(
|
||||
new AMap.value.Marker({
|
||||
map: map.value,
|
||||
position: ev.lnglat,
|
||||
content: `
|
||||
<div
|
||||
style="
|
||||
background-image: url(${mapPinPng});
|
||||
width: 21px;
|
||||
height: 22.5px;
|
||||
background-size: cover;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
padding-top: 3px;
|
||||
"
|
||||
>
|
||||
</div>
|
||||
`,
|
||||
anchor: "bottom-right",
|
||||
})
|
||||
);
|
||||
if (form.value.fencePath.length > 2) {
|
||||
turningPointMarker.value.forEach((el) => {
|
||||
el.hide();
|
||||
});
|
||||
} else {
|
||||
turningPointMarker.value.forEach((el) => {
|
||||
el.show();
|
||||
});
|
||||
}
|
||||
fencePolygon.value.setPath(
|
||||
// AMap.value.GeometryUtil.makesureClockwise(
|
||||
// form.value.fencePath.map((el) => new AMap.value.LngLat(el.lng, el.lat))
|
||||
@ -206,6 +208,55 @@ const handleMapClick = (ev) => {
|
||||
// )
|
||||
);
|
||||
};
|
||||
const handleResultClick = (ev) => {
|
||||
resultInfoWindow.value.setContent(`
|
||||
<div>
|
||||
${ev.target.getExtData().name}</div>
|
||||
`);
|
||||
resultInfoWindow.value.open(map.value, [ev.lnglat.lng, ev.lnglat.lat]);
|
||||
};
|
||||
// 根据名称搜索地图标点
|
||||
const searchPoint = (val /** 搜索建议选项 */, type) => {
|
||||
map.value.clearMap();
|
||||
form.value.fencePath = [];
|
||||
|
||||
placeSearch.value.search(searchKeyword.value, (status, result) => {
|
||||
console.log(status, result);
|
||||
// TODO: 添加搜索结果标记
|
||||
if (status === "complete") {
|
||||
searchResultMarkers.value = result.poiList.pois.map((el, index) => {
|
||||
const marker = new AMap.value.Marker({
|
||||
map: map.value,
|
||||
position: new AMap.value.LngLat(el.location.lng, el.location.lat),
|
||||
extData: el,
|
||||
content: `
|
||||
<div
|
||||
style="
|
||||
background-image: url(${markBsPng});
|
||||
width: 19px;
|
||||
height: 31px;
|
||||
background-size: cover;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
padding-top: 3px;
|
||||
"
|
||||
>
|
||||
${index + 1}
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
marker.on("mouseover", handleResultClick);
|
||||
marker.on("click", handleMapClick);
|
||||
marker.on("mouseout", (ev) => {
|
||||
resultInfoWindow.value.close();
|
||||
});
|
||||
return marker;
|
||||
});
|
||||
map.value.setFitView(searchResultMarkers.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs["fenceRef"].validate((valid) => {
|
||||
@ -254,7 +305,25 @@ function back() {
|
||||
|
||||
reset();
|
||||
onMounted(() => {
|
||||
initMap().then(() => {
|
||||
loadAMap(["AMap.Scale", "AMap.PlaceSearch"]).then((_AMap) => {
|
||||
AMap.value = _AMap;
|
||||
map.value = new AMap.value.Map("map-container", {
|
||||
zoom: 16, // 缩放级别
|
||||
center: [117.290345, 31.797813], // 中心点
|
||||
city: "合肥",
|
||||
});
|
||||
map.value.on("click", handleMapClick);
|
||||
// 地点搜索插件
|
||||
placeSearch.value = new AMap.value.PlaceSearch({
|
||||
// map: map.value,
|
||||
city: "0551",
|
||||
});
|
||||
resultInfoWindow.value = new AMap.value.InfoWindow({
|
||||
anchor: "bottom-center",
|
||||
});
|
||||
fencePolygon.value = new AMap.value.Polygon({});
|
||||
map.value.add(fencePolygon.value);
|
||||
map.value.addControl(new AMap.value.Scale());
|
||||
if (route.query.fenceId && route.query.uuid) {
|
||||
getFence(route.query.fenceId, route.query.uuid).then((resp) => {
|
||||
form.value = {
|
||||
|
@ -94,11 +94,7 @@
|
||||
<el-table-column label="围栏ID" align="center" prop="fenceId" />
|
||||
<el-table-column label="围栏名称" align="center" prop="fenceName" />
|
||||
<el-table-column label="限速(km/h)" align="center" prop="speed" />
|
||||
<el-table-column
|
||||
label="报警类型(0不1进2出3都)"
|
||||
align="center"
|
||||
prop="${field}"
|
||||
>
|
||||
<el-table-column label="报警类型" align="center" prop="${field}">
|
||||
<template #default="{ row }">
|
||||
{{ reportTypeMap.get(row.reportType) ?? "未知" }}
|
||||
</template>
|
||||
|
166
src/views/basic/line/RouteStepList.vue
Normal file
166
src/views/basic/line/RouteStepList.vue
Normal file
@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="description">
|
||||
<el-scrollbar height="20">
|
||||
<div class="roads">
|
||||
<div class="road-item" v-for="(road, index) in roadsList" :key="road">
|
||||
{{ road }}
|
||||
<ElIcon v-if="index != roadsList.length - 1">
|
||||
<ArrowRight />
|
||||
</ElIcon>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div class="time-distance">
|
||||
<span class="time"
|
||||
>{{ (routeData.routes[0].time / 60).toFixed(1) }}分钟</span
|
||||
>
|
||||
(<span class="distance">{{ routeData.routes[0].distance }}米</span>)
|
||||
<span class="seg">|</span>
|
||||
<span class="policy">
|
||||
{{ routeData.routes[0].policy }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@click="emit('start-click')"
|
||||
class="steps-item start"
|
||||
v-if="routeData.routes[0].steps.length"
|
||||
>
|
||||
<div
|
||||
class="icon"
|
||||
:style="{
|
||||
backgroundPosition: stepIconMap.get('起点'),
|
||||
}"
|
||||
></div>
|
||||
<div class="content">起点</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(item, index) in routeData.routes[0].steps"
|
||||
:key="item.start_location.lng"
|
||||
class="steps-item"
|
||||
@click="handleRouteClick(item, index)"
|
||||
>
|
||||
<div
|
||||
class="icon"
|
||||
:style="{
|
||||
backgroundPosition: stepIconMap.get(item.action) ?? '-46px -23px',
|
||||
}"
|
||||
></div>
|
||||
<div class="content">
|
||||
{{ item.instruction }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@click="emit('end-click')"
|
||||
class="steps-item start"
|
||||
v-if="routeData.routes[0].steps.length"
|
||||
>
|
||||
<div
|
||||
class="icon"
|
||||
:style="{
|
||||
backgroundPosition: stepIconMap.get('终点'),
|
||||
}"
|
||||
></div>
|
||||
<div class="content">终点</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup name="StepsList">
|
||||
import { ArrowRight } from "@element-plus/icons-vue";
|
||||
import { computed, toRefs } from "vue";
|
||||
|
||||
const emit = defineEmits(["step-click", "start-click", "end-click"]);
|
||||
const props = defineProps({
|
||||
routeData: { type: Object, required: true },
|
||||
// routeExtData: {
|
||||
// type: Object,
|
||||
// required: true,
|
||||
// },
|
||||
});
|
||||
const { routeData } = toRefs(props);
|
||||
const roadsList = computed(() =>
|
||||
routeData.value.routes[0].steps
|
||||
? [
|
||||
...new Set(
|
||||
routeData.value.routes[0].steps
|
||||
.map((el) => el.road)
|
||||
.filter((el) => !!el)
|
||||
),
|
||||
]
|
||||
: []
|
||||
);
|
||||
const stepIconMap = new Map([
|
||||
["靠左", "-406px -23px"],
|
||||
["靠右", "-445px -23px"],
|
||||
["左转", "-87px -23px"],
|
||||
["右转", "-124px -23px"],
|
||||
["左转调头", "-327px -23px"],
|
||||
["向左前方行驶", "-165px -23px"],
|
||||
["向右前方行驶", "-206px -23px"],
|
||||
["向左后方行驶", "-246px -23px"],
|
||||
["减速行驶", "-524px -23px"],
|
||||
["终点", "-126px -104px"],
|
||||
["起点", "-47px -104px"],
|
||||
]);
|
||||
|
||||
const handleRouteClick = (data, index) => {
|
||||
emit("step-click", data, index);
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.description {
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
padding: 8px 8px;
|
||||
.roads {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
.road-item {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
.time-distance {
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
font-size: 13px;
|
||||
.seg {
|
||||
margin: 0px 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.steps-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 5px;
|
||||
&:not(:last-child) {
|
||||
// margin-bottom: 10px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
.icon {
|
||||
margin-right: 8px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: #fff;
|
||||
line-height: 20px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #ddd;
|
||||
background-color: #fff;
|
||||
// background-position: -47px -104px;
|
||||
background-image: url(https://webapi.amap.com/theme/v1.3/images/newpc/diricon.png);
|
||||
}
|
||||
.content {
|
||||
font-size: 12px;
|
||||
}
|
||||
&.start .content {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
</style>
|
666
src/views/basic/line/add.vue
Normal file
666
src/views/basic/line/add.vue
Normal file
@ -0,0 +1,666 @@
|
||||
<template>
|
||||
<div class="wrap">
|
||||
<div class="form-drawer" v-show="drawerOpen">
|
||||
<el-scrollbar class="form-container">
|
||||
<div
|
||||
:style="{
|
||||
width: '96%',
|
||||
margin: '0 auto',
|
||||
}"
|
||||
>
|
||||
<el-form
|
||||
ref="routeFormRef"
|
||||
id="route-form"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
>
|
||||
<el-row justify="center">
|
||||
<el-form-item prop="carType">
|
||||
<el-radio-group v-model="formData.carType" size="small">
|
||||
<el-radio-button label="0">小车</el-radio-button>
|
||||
<el-radio-button label="1">货车</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row :gutter="10" justify="space-between">
|
||||
<el-col :span="2">
|
||||
<div
|
||||
:style="{
|
||||
height: '66px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}"
|
||||
>
|
||||
<el-icon color="#30a1f0"><Sort /></el-icon>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<el-form-item prop="startAddress">
|
||||
<el-popover
|
||||
:width="300"
|
||||
:visible="showStartSuggestion && !!formData.startAddress"
|
||||
title="搜索建议"
|
||||
>
|
||||
<div
|
||||
v-for="item in searchTips"
|
||||
:key="item.id"
|
||||
class="suggestion-item"
|
||||
@click="searchPoint(item.name, 'start')"
|
||||
>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<template #reference>
|
||||
<!-- TODO: @input="handleInput('start', $event)" -->
|
||||
<el-input
|
||||
v-model="formData.startAddress"
|
||||
size="small"
|
||||
@keydown.enter="
|
||||
searchPoint(formData.startAddress, 'start')
|
||||
"
|
||||
@focus="
|
||||
nowSearch = 'start';
|
||||
showStartSuggestion = true;
|
||||
"
|
||||
@blur="
|
||||
showStartSuggestion = false;
|
||||
nowSearch = '';
|
||||
"
|
||||
:suffix-icon="
|
||||
formData?.startLngLat?.length
|
||||
? `circle-check-filled`
|
||||
: null
|
||||
"
|
||||
>
|
||||
<template #prepend>起</template>
|
||||
</el-input>
|
||||
</template>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<el-form-item prop="endAddress">
|
||||
<el-popover
|
||||
:width="300"
|
||||
:visible="showEndSuggestion && !!formData.endAddress"
|
||||
title="搜索建议"
|
||||
>
|
||||
<div
|
||||
v-for="item in searchTips"
|
||||
:key="item.id"
|
||||
class="suggestion-item"
|
||||
@click="searchPoint(item.name, 'end')"
|
||||
>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<template #reference>
|
||||
<!--TODO: @input="getAutoComplete" -->
|
||||
<el-input
|
||||
v-model="formData.endAddress"
|
||||
size="small"
|
||||
@keydown.enter="searchPoint(formData.endAddress, 'end')"
|
||||
@focus="
|
||||
nowSearch = 'end';
|
||||
// map.clearMap();
|
||||
showEndSuggestion = true;
|
||||
"
|
||||
@blur="
|
||||
showEndSuggestion = false;
|
||||
nowSearch = '';
|
||||
// searchTips = [];
|
||||
"
|
||||
:suffix-icon="
|
||||
formData?.endLngLat?.length
|
||||
? `circle-check-filled`
|
||||
: null
|
||||
"
|
||||
>
|
||||
<template #prepend>终</template>
|
||||
</el-input>
|
||||
</template>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
<div
|
||||
:style="{
|
||||
height: '66px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}"
|
||||
>
|
||||
<el-icon color="#30a1f0"><CirclePlusFilled /></el-icon>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row
|
||||
justify="space-between"
|
||||
:style="{
|
||||
width: '90%',
|
||||
marginBottom: '18px',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}"
|
||||
>
|
||||
<el-button link @click="clearRoute" size="small"
|
||||
>清除路线</el-button
|
||||
>
|
||||
<el-button type="primary" @click="searchRoute" size="small"
|
||||
>查询</el-button
|
||||
>
|
||||
</el-row>
|
||||
<el-tabs
|
||||
type="border-card"
|
||||
v-if="showStepPane"
|
||||
v-model="formData.lineType"
|
||||
@tab-change="handleTabChange"
|
||||
>
|
||||
<el-tab-pane name="0" label="费用最低">
|
||||
<route-step-list
|
||||
:route-data="formData.lineJson"
|
||||
@step-click="handleRouteClick"
|
||||
@start-click="map.setFitView([startMarker])"
|
||||
@end-click="map.setFitView([endMarker])"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="1" label="时间最短">
|
||||
<route-step-list
|
||||
:route-data="formData.lineJson"
|
||||
@step-click="handleRouteClick"
|
||||
@start-click="map.setFitView([startMarker])"
|
||||
@end-click="map.setFitView([endMarker])"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="2" label="距离最短">
|
||||
<route-step-list
|
||||
:route-data="formData.lineJson"
|
||||
@step-click="handleRouteClick"
|
||||
@start-click="map.setFitView([startMarker])"
|
||||
@end-click="map.setFitView([endMarker])"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-form-item
|
||||
label="路径名称"
|
||||
prop="lineName"
|
||||
:style="{
|
||||
marginTop: '18px',
|
||||
}"
|
||||
>
|
||||
<el-input v-model="formData.lineName" size="small"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="报警开关" prop="reportType">
|
||||
<el-checkbox-group
|
||||
size="small"
|
||||
v-model="formData.reportType"
|
||||
class="ml-4"
|
||||
>
|
||||
<el-checkbox label="1">进入起点</el-checkbox>
|
||||
<el-checkbox label="2">偏离路径</el-checkbox>
|
||||
<el-checkbox label="3">到达终点</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-row class="submit-bar" :gutter="10">
|
||||
<el-col :span="12">
|
||||
<el-button size="small" type="danger" @click="back"
|
||||
>返回上层</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button size="small" type="primary" @click="submitForm"
|
||||
>保存</el-button
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<el-button
|
||||
id="drawer-switch"
|
||||
type="primary"
|
||||
plain
|
||||
circle
|
||||
:icon="drawerOpen ? 'ArrowLeftBold' : 'ArrowRightBold'"
|
||||
@click="switchDrawer"
|
||||
></el-button>
|
||||
<div id="map-container"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup name="AddLine">
|
||||
import tab from "@/plugins/tab";
|
||||
import { onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { loadAMap } from "@/utils/map";
|
||||
import { ElMessage } from "element-plus";
|
||||
import endPng from "@/assets/images/end.png";
|
||||
import RouteStepList from "./RouteStepList.vue";
|
||||
import startPng from "@/assets/images/start.png";
|
||||
import { addLine, getLine, updateLine } from "@/api/line/line";
|
||||
|
||||
const route = useRoute();
|
||||
const AMap = shallowRef(null);
|
||||
const map = shallowRef(null);
|
||||
const driving = shallowRef(null); // 驾车路线规划插件
|
||||
const truckDriving = shallowRef(null); // 卡车路线规划插件
|
||||
const geocoder = shallowRef(null); // 经纬度转地址插件
|
||||
const placeSearch = shallowRef(null); // 地点搜索插件
|
||||
const autoComplete = shallowRef(null); // 搜索建议插件
|
||||
const startMarker = shallowRef(null); // 起点标记
|
||||
const endMarker = shallowRef(null); // 终点标记
|
||||
const resultInfoWindow = shallowRef(null);
|
||||
const searchResultMarkers = ref([]); // 搜索结果表笔列表
|
||||
const stepsPolylines = ref([]); // 路线步骤标记列表
|
||||
const stepsList = ref([]);
|
||||
const drawerOpen = ref(true);
|
||||
const routeFormRef = ref(null);
|
||||
const searchTips = ref([]);
|
||||
const nowSearch = ref("");
|
||||
const showStartSuggestion = ref(false);
|
||||
const showEndSuggestion = ref(false);
|
||||
const activeTabName = ref("LEAST_FEE");
|
||||
|
||||
const showStepPane = computed(
|
||||
() =>
|
||||
formData.value?.startLngLat?.length === 2 &&
|
||||
formData.value?.endLngLat?.length === 2 &&
|
||||
formData.value.lineJson
|
||||
);
|
||||
|
||||
const policyMap = new Map([
|
||||
["0", "LEAST_FEE"],
|
||||
["1", "LEAST_TIME"],
|
||||
["2", "LEAST_DISTANCE"],
|
||||
]);
|
||||
const data = reactive({
|
||||
formData: {
|
||||
carType: "0",
|
||||
startLngLat: [],
|
||||
endLngLat: [],
|
||||
lineType: "0",
|
||||
lineJson: null,
|
||||
},
|
||||
routeExtData: {
|
||||
distance: 0,
|
||||
time: 0,
|
||||
policy: "",
|
||||
},
|
||||
rules: {
|
||||
lineName: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入路线名称",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
reportType: [
|
||||
{
|
||||
required: true,
|
||||
message: "请选择报警开关",
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const { formData, rules } = toRefs(data);
|
||||
// 切换左侧面板显示隐藏
|
||||
const switchDrawer = () => {
|
||||
drawerOpen.value = !drawerOpen.value;
|
||||
if (drawerOpen.value) {
|
||||
document.querySelector("#drawer-switch").style.left = "25%";
|
||||
document.querySelector("#drawer-switch").style.transform =
|
||||
"translate(-50%, -50%)";
|
||||
} else {
|
||||
document.querySelector("#drawer-switch").style.left = "0";
|
||||
document.querySelector("#drawer-switch").style.transform =
|
||||
"translate(0, -50%)";
|
||||
}
|
||||
};
|
||||
|
||||
// 地图点击
|
||||
const handleMapClick = (ev) => {
|
||||
// ev.preventDefault();
|
||||
// 起点输入框 focus 时
|
||||
if (nowSearch.value === "start") {
|
||||
map.value.clearMap();
|
||||
if (formData.value?.endLngLat?.length === 2) {
|
||||
map.value.add(endMarker.value);
|
||||
}
|
||||
startMarker.value.setPosition(
|
||||
new AMap.value.LngLat(ev.lnglat.lng, ev.lnglat.lat)
|
||||
);
|
||||
map.value.add(startMarker.value);
|
||||
regeoCode([ev.lnglat.lng, ev.lnglat.lat])
|
||||
.then((address) => {
|
||||
formData.value.startAddress = address;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
formData.value.startAddress = `${ev.lnglat.lng}, ${ev.lnglat.lat}`;
|
||||
});
|
||||
formData.value.startLngLat = [ev.lnglat.lng, ev.lnglat.lat];
|
||||
}
|
||||
// 终点输入框 focus 时
|
||||
else if (nowSearch.value === "end") {
|
||||
map.value.clearMap();
|
||||
if (formData.value?.startLngLat?.length === 2) {
|
||||
map.value.add(startMarker.value);
|
||||
}
|
||||
endMarker.value.setPosition(
|
||||
new AMap.value.LngLat(ev.lnglat.lng, ev.lnglat.lat)
|
||||
);
|
||||
map.value.add(endMarker.value);
|
||||
regeoCode([ev.lnglat.lng, ev.lnglat.lat])
|
||||
.then((address) => {
|
||||
formData.value.endAddress = address;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
formData.value.endAddress = `${ev.lnglat.lng}, ${ev.lnglat.lat}`;
|
||||
});
|
||||
formData.value.endLngLat = [ev.lnglat.lng, ev.lnglat.lat];
|
||||
}
|
||||
};
|
||||
|
||||
// 根据经纬度获取地址
|
||||
const regeoCode = (lnglat /** [lng, lat] */) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
geocoder.value.getAddress(lnglat, (status, result) => {
|
||||
if (status === "complete" && result.regeocode) {
|
||||
const address = result.regeocode.formattedAddress;
|
||||
resolve(address);
|
||||
} else {
|
||||
reject("根据经纬度查询地址失败");
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 搜索路线
|
||||
const searchRoute = () => {
|
||||
if (!driving.value) return;
|
||||
return new Promise((resolve, reject) => {
|
||||
const policy = policyMap.get(formData.value.lineType);
|
||||
if (policy) {
|
||||
driving.value.opt.policy =
|
||||
AMap.value.DrivingPolicy[policyMap.get(formData.value.lineType)];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
console.log("123");
|
||||
const startLngLat = formData.value.startLngLat;
|
||||
const endLngLat = formData.value.endLngLat;
|
||||
driving.value.search(startLngLat, endLngLat, (status, result) => {
|
||||
// 未出错时,result即是对应的路线规划方案
|
||||
if (status === "complete") {
|
||||
console.log(result);
|
||||
formData.value.lineJson = result;
|
||||
stepsList.value = result.routes[0].steps;
|
||||
drawRoute(result);
|
||||
resolve(result);
|
||||
} else {
|
||||
reject("获取路线失败");
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 绘制路线
|
||||
const drawRoute = (result) => {
|
||||
// 绘制路线之前,清除地图上所有标记
|
||||
map.value.clearMap();
|
||||
const startLngLat = formData.value.startLngLat;
|
||||
const endLngLat = formData.value.endLngLat;
|
||||
|
||||
// 重新添加开始和结束标记
|
||||
startMarker.value.setPosition(startLngLat);
|
||||
endMarker.value.setPosition(endLngLat);
|
||||
map.value.add(startMarker.value);
|
||||
map.value.add(endMarker.value);
|
||||
// 起点到开始驾驶地点距离以虚直线表示
|
||||
new AMap.value.Polyline({
|
||||
map: map.value,
|
||||
strokeWeight: 8,
|
||||
strokeOpacity: 1,
|
||||
strokeColor: "#808080",
|
||||
strokeStyle: "dashed",
|
||||
path: [
|
||||
new AMap.value.LngLat(...startLngLat),
|
||||
result.routes[0].steps[0].start_location,
|
||||
],
|
||||
});
|
||||
// 结束驾驶地点到终点距离以虚直线表示
|
||||
new AMap.value.Polyline({
|
||||
map: map.value,
|
||||
strokeWeight: 8,
|
||||
strokeOpacity: 1,
|
||||
strokeColor: "#808080",
|
||||
strokeStyle: "dashed",
|
||||
path: [
|
||||
result.routes[0].steps[result.routes[0].steps.length - 1].end_location,
|
||||
new AMap.value.LngLat(...endLngLat),
|
||||
],
|
||||
});
|
||||
// 在地图上绘制路线标记,并添加到数组中
|
||||
stepsPolylines.value = result.routes[0].steps.map((el) => {
|
||||
return new AMap.value.Polyline({
|
||||
map: map.value,
|
||||
strokeWeight: 8,
|
||||
strokeOpacity: 1,
|
||||
strokeColor: "#1bac2e",
|
||||
showDir: true,
|
||||
path: el.path,
|
||||
});
|
||||
});
|
||||
|
||||
map.value.setFitView(stepsPolylines.value);
|
||||
};
|
||||
|
||||
const handleRouteClick = (data, index) => {
|
||||
console.log(data, index);
|
||||
map.value.setFitView([stepsPolylines.value[index]]);
|
||||
};
|
||||
|
||||
// 清除路线,reset form
|
||||
const clearRoute = () => {
|
||||
formData.value.lineJson = null;
|
||||
formData.value.startAddress = "";
|
||||
formData.value.endAddress = "";
|
||||
formData.value.startLngLat = null;
|
||||
formData.value.endLngLat = null;
|
||||
stepsList.value = [];
|
||||
map.value.clearMap();
|
||||
};
|
||||
|
||||
// tab点击事件
|
||||
const handleTabChange = (tab) => {
|
||||
if (formData.value.startLngLat.length && formData.value.endLngLat.length) {
|
||||
searchRoute();
|
||||
} else {
|
||||
// ElMessage.error("err");
|
||||
}
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
routeFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
if (formData.value.lineId != null) {
|
||||
updateLine({
|
||||
...formData.value,
|
||||
lineJson: JSON.stringify(formData.value.lineJson),
|
||||
reportType: formData.value.reportType.join(","),
|
||||
}).then((response) => {
|
||||
ElMessage.success("修改成功");
|
||||
back();
|
||||
});
|
||||
} else {
|
||||
addLine({
|
||||
...formData.value,
|
||||
lineJson: JSON.stringify(formData.value.lineJson),
|
||||
reportType: formData.value.reportType.join(","),
|
||||
}).then((response) => {
|
||||
ElMessage.success("新增成功");
|
||||
back();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
formData.value = {
|
||||
// lineId: null,
|
||||
lineName: null,
|
||||
carType: "0",
|
||||
lineType: "0",
|
||||
startAddress: null,
|
||||
endAddress: null,
|
||||
reportType: [],
|
||||
lineJson: null,
|
||||
// linePath: null,
|
||||
// createBy: null,
|
||||
// createTime: null,
|
||||
// uuid: null,
|
||||
// tenantId: null,
|
||||
};
|
||||
routeFormRef.value && routeFormRef.value.resetFields();
|
||||
};
|
||||
const getFormData = async () => {
|
||||
if (route.query.lineId && route.query.uuid) {
|
||||
getLine(route.query.lineId, route.query.uuid).then((resp) => {
|
||||
let lineJson;
|
||||
try {
|
||||
lineJson = JSON.parse(resp.data.lineJson);
|
||||
} catch (error) {
|
||||
lineJson = null;
|
||||
}
|
||||
const reportType = resp.data.reportType
|
||||
? resp.data.reportType.split(",")
|
||||
: [];
|
||||
const startLngLat = lineJson?.start.location;
|
||||
const endLngLat = lineJson?.end.location;
|
||||
formData.value = {
|
||||
...resp.data,
|
||||
lineJson,
|
||||
reportType,
|
||||
startLngLat,
|
||||
endLngLat,
|
||||
};
|
||||
return "success";
|
||||
});
|
||||
} else {
|
||||
return "success";
|
||||
}
|
||||
};
|
||||
function back() {
|
||||
reset();
|
||||
tab.closeOpenPage({ path: "/basic/line" });
|
||||
}
|
||||
onMounted(async () => {
|
||||
reset();
|
||||
document.getElementById("map-container").onmousedown = (ev) => {
|
||||
ev.preventDefault();
|
||||
};
|
||||
await getFormData();
|
||||
loadAMap([
|
||||
"AMap.Driving",
|
||||
"AMap.Scale",
|
||||
"AMap.PlaceSearch",
|
||||
"AMap.AutoComplete",
|
||||
"AMap.Geocoder",
|
||||
// "AMap.TruckDriving",
|
||||
]).then((_AMap) => {
|
||||
AMap.value = _AMap;
|
||||
map.value = new AMap.value.Map("map-container", {
|
||||
zoom: 16, // 缩放级别
|
||||
center: [117.290345, 31.797813], // 中心点
|
||||
city: "合肥",
|
||||
});
|
||||
|
||||
/** 加载插件 */
|
||||
// 驾车路线规划插件
|
||||
driving.value = new AMap.value.Driving();
|
||||
|
||||
// 卡车路线规划插件 TODO:
|
||||
// truckDriving.value = new AMap.value.TruckDriving();
|
||||
// 地点搜索插件
|
||||
placeSearch.value = new AMap.value.PlaceSearch({
|
||||
// map: map.value,
|
||||
city: "0551",
|
||||
});
|
||||
geocoder.value = new AMap.value.Geocoder();
|
||||
// 搜索建议插件
|
||||
autoComplete.value = new AMap.value.AutoComplete({});
|
||||
/** 加载插件结束 */
|
||||
startMarker.value = new AMap.value.Marker({
|
||||
icon: startPng,
|
||||
anchor: "bottom-center",
|
||||
});
|
||||
endMarker.value = new AMap.value.Marker({
|
||||
icon: endPng,
|
||||
anchor: "bottom-center",
|
||||
});
|
||||
map.value.on("click", handleMapClick);
|
||||
|
||||
// 当修改时,从返回数据中读取路线数据绘制
|
||||
if (formData.value.lineJson) {
|
||||
drawRoute(formData.value.lineJson);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.wrap {
|
||||
position: relative;
|
||||
#map-container {
|
||||
width: 100%;
|
||||
height: calc(100vh - 84px);
|
||||
}
|
||||
.form-drawer {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 99;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
width: 25%;
|
||||
height: 100vh;
|
||||
box-shadow: rgba(0, 0, 0, 0.08) 0px 16px 48px 16px,
|
||||
rgba(0, 0, 0, 0.12) 0px 12px 32px 0px,
|
||||
rgba(0, 0, 0, 0.16) 0px 8px 16px -8px;
|
||||
transition: all 0.3s;
|
||||
:deep(.el-input__suffix) {
|
||||
color: #67c23a;
|
||||
}
|
||||
:deep(.el-scrollbar.form-container) {
|
||||
height: calc(100vh - 84px - 40px);
|
||||
}
|
||||
:deep(.el-form-item__label) {
|
||||
font-size: 12px;
|
||||
}
|
||||
:deep(.el-checkbox) {
|
||||
margin-right: 8px;
|
||||
.el-checkbox__label {
|
||||
padding-left: 3px;
|
||||
}
|
||||
}
|
||||
:deep(.submit-bar) {
|
||||
width: 100%;
|
||||
.el-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(#drawer-switch) {
|
||||
position: absolute;
|
||||
left: 25%;
|
||||
top: 50%;
|
||||
z-index: 100;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
</style>
|
344
src/views/basic/line/index.vue
Normal file
344
src/views/basic/line/index.vue
Normal file
@ -0,0 +1,344 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form
|
||||
ref="queryRef"
|
||||
:inline="true"
|
||||
:model="queryParams"
|
||||
v-show="showSearch"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="线路名称" prop="lineName">
|
||||
<el-input
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
placeholder="请输入线路名称"
|
||||
v-model="queryParams.lineName"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery"
|
||||
>搜索</el-button
|
||||
>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['line:line:add']"
|
||||
>新增</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['line:line:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['line:line:export']"
|
||||
>导出</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
@queryTable="getList"
|
||||
></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="lineList"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="线路ID" align="center" prop="lineId" />
|
||||
<el-table-column label="线路名称" align="center" prop="lineName" />
|
||||
<el-table-column label="车型" align="center" prop="${field}">
|
||||
<template #default="{ row }">
|
||||
{{
|
||||
row.carType == "0" ? "小车" : row.carType == "1" ? "货车" : "未知"
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="线路类型" align="center" prop="${field}">
|
||||
<template #default="{ row }">
|
||||
{{
|
||||
row.lineType == "0"
|
||||
? "费用最低"
|
||||
: row.lineType == "1"
|
||||
? "时间最短"
|
||||
: row.lineType == "2"
|
||||
? "距离最短"
|
||||
: "未知"
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="起点" align="center" prop="startAddress" />
|
||||
<el-table-column label="终点" align="center" prop="endAddress" />
|
||||
<el-table-column label="报警开关" align="center" prop="${field}">
|
||||
<template #default="{ row }">
|
||||
{{ "未知" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="线路JSON" align="center" prop="lineJson" />
|
||||
<el-table-column label="线路PATH" align="center" prop="linePath" />
|
||||
<el-table-column label="租户ID" align="center" prop="tenantId" /> -->
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
class-name="small-padding fixed-width"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['line:line:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 添加或修改线路规划对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
||||
<el-form ref="lineRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="线路名称" prop="lineName">
|
||||
<el-input v-model="form.lineName" placeholder="请输入线路名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="车型" prop="carType">
|
||||
<el-radio-group v-model="form.carType" class="ml-4">
|
||||
<el-radio label="0" size="large">小车</el-radio>
|
||||
<el-radio label="1" size="large">货车</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="线路类型" prop="lineType">
|
||||
<el-radio-group v-model="form.lineType" class="ml-4">
|
||||
<el-radio label="0" size="large">费用最低</el-radio>
|
||||
<el-radio label="1" size="large">时间最短</el-radio>
|
||||
<el-radio label="2" size="large">距离最短</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="起点" prop="startAddress">
|
||||
<el-input v-model="form.startAddress" placeholder="请输入起点" />
|
||||
</el-form-item>
|
||||
<el-form-item label="终点" prop="endAddress">
|
||||
<el-input v-model="form.endAddress" placeholder="请输入终点" />
|
||||
</el-form-item>
|
||||
<el-form-item label="报警开关" prop="reportType">
|
||||
<el-radio-group v-model="form.reportType" class="ml-4">
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="线路JSON" prop="lineJson">
|
||||
<el-input v-model="form.lineJson" placeholder="请输入线路JSON" />
|
||||
</el-form-item>
|
||||
<el-form-item label="线路PATH" prop="linePath">
|
||||
<el-input v-model="form.linePath" placeholder="请输入线路PATH" />
|
||||
</el-form-item>
|
||||
<el-form-item label="租户ID" prop="tenantId">
|
||||
<el-input v-model="form.tenantId" placeholder="请输入租户ID" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Line">
|
||||
import {
|
||||
listLine,
|
||||
getLine,
|
||||
delLine,
|
||||
addLine,
|
||||
updateLine,
|
||||
} from "@/api/line/line";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const lineList = ref([]);
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const title = ref("");
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
lineName: null,
|
||||
carType: null,
|
||||
lineType: null,
|
||||
},
|
||||
rules: {},
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询线路规划列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
listLine(queryParams.value).then((response) => {
|
||||
lineList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 取消按钮
|
||||
function cancel() {
|
||||
open.value = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
// 表单重置
|
||||
function reset() {
|
||||
form.value = {
|
||||
lineId: null,
|
||||
lineName: null,
|
||||
carType: null,
|
||||
lineType: null,
|
||||
startAddress: null,
|
||||
endAddress: null,
|
||||
reportType: null,
|
||||
lineJson: null,
|
||||
linePath: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
uuid: null,
|
||||
tenantId: null,
|
||||
};
|
||||
proxy.resetForm("lineRef");
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
// 多选框选中数据
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map((item) => {
|
||||
return { id: item.lineId, uuid: item.uuid };
|
||||
});
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
|
||||
/** 新增按钮操作 */
|
||||
function handleAdd() {
|
||||
router.push({
|
||||
path: "/basic/line/add",
|
||||
});
|
||||
}
|
||||
|
||||
/** 修改按钮操作 */
|
||||
function handleUpdate(row) {
|
||||
const { lineId, uuid } = row;
|
||||
router.push({
|
||||
path: "/basic/line/add",
|
||||
query: {
|
||||
lineId,
|
||||
uuid,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs["lineRef"].validate((valid) => {
|
||||
if (valid) {
|
||||
if (form.value.lineId != null) {
|
||||
updateLine(form.value).then((response) => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
} else {
|
||||
addLine(form.value).then((response) => {
|
||||
proxy.$modal.msgSuccess("新增成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const _lineIds = row.lineId
|
||||
? [{ id: row.lineId, uuid: row.uuid }]
|
||||
: ids.value;
|
||||
proxy.$modal
|
||||
.confirm(
|
||||
'是否确认删除线路规划编号为"' +
|
||||
_lineIds.map((el) => el.lineId).join(",") +
|
||||
'"的数据项?'
|
||||
)
|
||||
.then(function () {
|
||||
return delLine(_lineIds);
|
||||
})
|
||||
.then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download(
|
||||
"line/line/export",
|
||||
{
|
||||
...queryParams.value,
|
||||
},
|
||||
`line_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
}
|
||||
|
||||
getList();
|
||||
</script>
|
@ -2,7 +2,11 @@
|
||||
<div>
|
||||
<el-row class="search-bar" :gutter="10">
|
||||
<el-col :span="18">
|
||||
<el-input size="small"></el-input>
|
||||
<el-input
|
||||
size="small"
|
||||
placeholder="请输入地名或点击地图选点"
|
||||
@keypress.enter="searchPoint"
|
||||
></el-input>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" size="small" icon="search">搜索</el-button>
|
||||
@ -81,16 +85,19 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { listSiteGroup } from "@/api/site/group";
|
||||
import AMapLoader from "@amap/amap-jsapi-loader";
|
||||
import CitySelect from "@/components/CitySelect";
|
||||
import { addSite, getSite, updateSite } from "@/api/site/site";
|
||||
import tab from "@/plugins/tab";
|
||||
import { useRoute } from "vue-router";
|
||||
import { loadAMap } from "@/utils/map";
|
||||
import { listSiteGroup } from "@/api/site/group";
|
||||
import CitySelect from "@/components/CitySelect";
|
||||
import { addSite, getSite, updateSite } from "@/api/site/site";
|
||||
|
||||
const route = useRoute();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const geocoder = shallowRef(null); // 经纬度转地址插件
|
||||
const placeSearch = shallowRef(null);
|
||||
const searchResultMarkers = shallowRef([]); // 搜索结果表笔列表
|
||||
const searchKeyword = ref("");
|
||||
const AMap = shallowRef(null);
|
||||
const map = shallowRef(null);
|
||||
const siteGroupList = ref([]);
|
||||
@ -102,88 +109,7 @@ const data = reactive({
|
||||
});
|
||||
|
||||
const { form, rules } = toRefs(data);
|
||||
// 初始化地图
|
||||
const initMap = async () => {
|
||||
try {
|
||||
AMap.value = await AMapLoader.load({
|
||||
key: "377d7c36dd385e2a722f29d4c6e1ffbf", // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
// key: "7891f1238368a895ff1967c79643102d", // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||
plugins: [
|
||||
// "AMap.Driving",
|
||||
"AMap.Scale",
|
||||
// "AMap.PlaceSearch",
|
||||
// "AMap.AutoComplete",
|
||||
"AMap.Geocoder",
|
||||
// "AMap.TruckDriving",
|
||||
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||
AMapUI: {
|
||||
// 是否加载 AMapUI,缺省不加载
|
||||
version: "1.1", // AMapUI 版本
|
||||
plugins: ["overlay/SimpleMarker"], // 需要加载的 AMapUI ui插件
|
||||
},
|
||||
Loca: {
|
||||
// 是否加载 Loca, 缺省不加载
|
||||
version: "2.0", // Loca 版本
|
||||
},
|
||||
});
|
||||
geocoder.value = new AMap.value.Geocoder();
|
||||
map.value = new AMap.value.Map("map-container", {
|
||||
zoom: 16, // 缩放级别
|
||||
center: [117.290345, 31.797813], // 中心点
|
||||
city: "合肥",
|
||||
});
|
||||
window.mapins = map.value;
|
||||
/** 加载插件 */
|
||||
// 驾车路线规划插件
|
||||
// driving.value = new AMap.value.Driving({
|
||||
// // 驾车路线规划策略,AMap.DrivingPolicy.LEAST_TIME是最快捷模式
|
||||
// // policy: AMap.DrivingPolicy.MULTI_POLICIES,
|
||||
// // map: map.value,
|
||||
// panel: "panel",
|
||||
// });
|
||||
|
||||
// 卡车路线规划插件 TODO:
|
||||
// truckDriving.value = new AMap.value.TruckDriving({ map: map.value });
|
||||
// // 地点搜索插件
|
||||
// placeSearch.value = new AMap.value.PlaceSearch({
|
||||
// // map: map.value,
|
||||
// city: "0551",
|
||||
// });
|
||||
// geocoder.value = new AMap.value.Geocoder();
|
||||
// // 搜索建议插件
|
||||
// autoComplete.value = new AMap.value.AutoComplete({});
|
||||
// /** 加载插件结束 */
|
||||
// resultInfoWindow.value = new AMap.value.InfoWindow({
|
||||
// anchor: "bottom-center",
|
||||
// });
|
||||
// startMarker.value = new AMap.value.Marker({
|
||||
// icon: startPng,
|
||||
// anchor: "bottom-center",
|
||||
// });
|
||||
// endMarker.value = new AMap.value.Marker({
|
||||
// icon: endPng,
|
||||
// anchor: "bottom-center",
|
||||
// });
|
||||
// city: "010", //城市设为北京,默认:“全国”
|
||||
// radius: 1000, //范围,默认:500
|
||||
// AMap.value.Event.addListener(placeSearch.value, "markerClick", (e) => {
|
||||
// //添加事件
|
||||
// console.log(e); //获取点标注位置
|
||||
// if (nowSearch.value === "start") {
|
||||
// formData.value.startName = e.data.name;
|
||||
// formData.value.startLngLat = [e.data.location.lng, e.data.location.lat];
|
||||
// } else if (nowSearch.value === "end") {
|
||||
// formData.value.endName = e.data.name;
|
||||
// formData.value.endLngLat = [e.data.location.lng, e.data.location.lat];
|
||||
// }
|
||||
// });
|
||||
map.value.on("click", handleMapClick);
|
||||
map.value.addControl(new AMap.value.Scale());
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
// 根据经纬度获取地址
|
||||
const regeoCode = (lnglat /** [lng, lat] */) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -225,6 +151,49 @@ function submitForm() {
|
||||
});
|
||||
}
|
||||
|
||||
// 根据名称搜索地图标点
|
||||
const searchPoint = (val /** 搜索建议选项 */, type) => {
|
||||
map.value.clearMap();
|
||||
form.value.fencePath = [];
|
||||
|
||||
placeSearch.value.search(searchKeyword.value, (status, result) => {
|
||||
console.log(status, result);
|
||||
// TODO: 添加搜索结果标记
|
||||
if (status === "complete") {
|
||||
searchResultMarkers.value = result.poiList.pois.map((el, index) => {
|
||||
const marker = new AMap.value.Marker({
|
||||
map: map.value,
|
||||
position: new AMap.value.LngLat(el.location.lng, el.location.lat),
|
||||
extData: el,
|
||||
content: `
|
||||
<div
|
||||
style="
|
||||
background-image: url(${markBsPng});
|
||||
width: 19px;
|
||||
height: 31px;
|
||||
background-size: cover;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
padding-top: 3px;
|
||||
"
|
||||
>
|
||||
${index + 1}
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
marker.on("mouseover", handleResultClick);
|
||||
marker.on("click", handleMapClick);
|
||||
marker.on("mouseout", (ev) => {
|
||||
resultInfoWindow.value.close();
|
||||
});
|
||||
return marker;
|
||||
});
|
||||
map.value.setFitView(searchResultMarkers.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function back() {
|
||||
reset();
|
||||
tab.closeOpenPage({ path: "/basic/site" });
|
||||
@ -249,14 +218,25 @@ function reset() {
|
||||
proxy.resetForm("siteRef");
|
||||
}
|
||||
|
||||
// 取消按钮
|
||||
function cancel() {
|
||||
open.value = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initMap();
|
||||
onMounted(async () => {
|
||||
const _AMap = await loadAMap([
|
||||
"AMap.Scale",
|
||||
"AMap.Geocoder",
|
||||
"AMap.PlaceSearch",
|
||||
]);
|
||||
AMap.value = _AMap;
|
||||
map.value = new AMap.value.Map("map-container", {
|
||||
zoom: 16, // 缩放级别
|
||||
center: [117.290345, 31.797813], // 中心点
|
||||
});
|
||||
// 地点搜索插件
|
||||
placeSearch.value = new AMap.value.PlaceSearch({
|
||||
// map: map.value,
|
||||
city: "0551",
|
||||
});
|
||||
geocoder.value = new AMap.value.Geocoder();
|
||||
map.value.on("click", handleMapClick);
|
||||
map.value.addControl(new AMap.value.Scale());
|
||||
});
|
||||
|
||||
listSiteGroup().then((resp) => {
|
||||
|
@ -141,6 +141,7 @@ import { listSite, delSite } from "@/api/site/site";
|
||||
import { useRouter } from "vue-router";
|
||||
import AMapLoader from "@amap/amap-jsapi-loader";
|
||||
import { shallowRef, ref, reactive, toRefs } from "vue";
|
||||
import { loadAMap } from "@/utils/map";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const router = useRouter();
|
||||
@ -188,14 +189,9 @@ function getList() {
|
||||
map.value.setFitView(siteMarkerList.value);
|
||||
});
|
||||
}
|
||||
const handleRowClick = (row, col, ev) => {
|
||||
const handleRowClick = (row) => {
|
||||
if (map.value && row.marker) {
|
||||
map.value.setFitView(row.marker);
|
||||
// siteInfoWindow.value.setContent(`
|
||||
// <div>
|
||||
// ${123434}</div>
|
||||
// `);
|
||||
// siteInfoWindow.value.open(map.value, row.coordinate.split(","));
|
||||
}
|
||||
};
|
||||
/** 搜索按钮操作 */
|
||||
@ -269,50 +265,20 @@ function handleExport() {
|
||||
`site_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
}
|
||||
// 初始化地图
|
||||
const initMap = async () => {
|
||||
try {
|
||||
AMap.value = await AMapLoader.load({
|
||||
key: "377d7c36dd385e2a722f29d4c6e1ffbf", // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
// key: "7891f1238368a895ff1967c79643102d", // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||
plugins: [
|
||||
// "AMap.Driving",
|
||||
"AMap.Scale",
|
||||
// "AMap.PlaceSearch",
|
||||
// "AMap.AutoComplete",
|
||||
// "AMap.Geocoder",
|
||||
// "AMap.TruckDriving",
|
||||
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||
AMapUI: {
|
||||
// 是否加载 AMapUI,缺省不加载
|
||||
version: "1.1", // AMapUI 版本
|
||||
plugins: ["overlay/SimpleMarker"], // 需要加载的 AMapUI ui插件
|
||||
},
|
||||
Loca: {
|
||||
// 是否加载 Loca, 缺省不加载
|
||||
version: "2.0", // Loca 版本
|
||||
},
|
||||
});
|
||||
// geocoder.value = new AMap.value.Geocoder();
|
||||
map.value = new AMap.value.Map("map-container", {
|
||||
zoom: 16, // 缩放级别
|
||||
center: [117.290345, 31.797813], // 中心点
|
||||
city: "合肥",
|
||||
});
|
||||
siteInfoWindow.value = new AMap.value.InfoWindow({
|
||||
anchor: "bottom-center",
|
||||
});
|
||||
// map.value.on("click", handleMapClick);
|
||||
map.value.addControl(new AMap.value.Scale());
|
||||
return "success";
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initMap()
|
||||
.then(() => {
|
||||
loadAMap(["AMap.Scale"])
|
||||
.then((_AMap) => {
|
||||
AMap.value = _AMap;
|
||||
map.value = new AMap.value.Map("map-container", {
|
||||
zoom: 16, // 缩放级别
|
||||
center: [117.290345, 31.797813], // 中心点
|
||||
city: "合肥",
|
||||
});
|
||||
siteInfoWindow.value = new AMap.value.InfoWindow({
|
||||
anchor: "bottom-center",
|
||||
});
|
||||
map.value.addControl(new AMap.value.Scale());
|
||||
getList();
|
||||
})
|
||||
.catch(() => {
|
||||
|
Reference in New Issue
Block a user