493 lines
17 KiB
Vue
493 lines
17 KiB
Vue
<template>
|
|
<div class="center-map">
|
|
<div class="china-map-box">
|
|
<div class="tab-list">
|
|
<div v-for="(item, i) in list" :class="[currentIndex == i ? 'active' : '']" class="tab" @click="changeTab(i)">
|
|
{{ item.name }}
|
|
</div>
|
|
</div>
|
|
<div v-show="currentIndex == list.length - 1" class="route-navigation">
|
|
<a-form :model="formState" layout="inline">
|
|
<a-form-item class="form-item-self" label="起点">
|
|
<a-input v-model:value="formState.start"></a-input>
|
|
</a-form-item>
|
|
<a-form-item class="form-item-self" label="终点">
|
|
<a-input v-model:value="formState.end"></a-input>
|
|
</a-form-item>
|
|
<a-form-item>
|
|
<a-button class="reset-btn" @click="removeInput">清空</a-button>
|
|
<a-button class="search-btn" @click="searchRoute">路线规划</a-button>
|
|
</a-form-item>
|
|
</a-form>
|
|
<div class="line-container" v-show="showRoutePanel" v-click-outside="outClick">
|
|
<div v-show="routeList.length" class="has-data">
|
|
<div class="line-title"> {{ panelTitle }}</div>
|
|
<div class="line-content">
|
|
<div class="start">起点</div>
|
|
<div class="line-body">
|
|
<div class="line"></div>
|
|
<div class="line-desc">
|
|
<p v-for="(item, i) in routeList" :key="i">{{ item }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="end">终点</div>
|
|
</div>
|
|
</div>
|
|
<div v-show="!routeList.length" class="empty-data"> 暂无数据</div>
|
|
</div>
|
|
</div>
|
|
<div class="china-map-container"></div>
|
|
<div v-show="false" id="container"></div>
|
|
<div v-show="false" id="panel"></div>
|
|
<div class="map-legend">
|
|
<div v-for="(item, i) in legendList" :key="i" class="legend-item">
|
|
<div><img :src="item.img" alt="" class="legend-img" /> {{ item.text }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="center-footer">
|
|
<div class="count-item">
|
|
<div class="num">1054</div>
|
|
<div class="text">累计受理</div>
|
|
</div>
|
|
<div class="count-item">
|
|
<div class="num">1054</div>
|
|
<div class="text">累计处置</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script lang="ts" setup>
|
|
import { onMounted, ref } from 'vue';
|
|
import * as echarts from 'echarts';
|
|
import mapJson from '/@/components/chinaMap/json/c';
|
|
import { tabList, mapIcons, legendList, getTimeStr, getKmStr, createChartOption } from './chinaHooks.ts';
|
|
import { message } from 'ant-design-vue';
|
|
|
|
const formState = ref({
|
|
start: '西安北站',
|
|
end: '',
|
|
});
|
|
const list = ref(tabList);
|
|
const currentIndex = ref(0);
|
|
const icons = ref([
|
|
{
|
|
type: 'scatter',
|
|
zlevel: 100,
|
|
coordinateSystem: 'geo',
|
|
//自定义图片的 位置(lng, lat)
|
|
data: [{ name: '西安', value: ['108.88', '34.40'], id: 'xian' }],
|
|
//自定义图片的 大小
|
|
symbolSize: [25, 63],
|
|
//自定义图片的 路径
|
|
// symbol: 'image://https://img2.baidu.com/it/u=1568770988,2090980470&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=567', // 图片 URL
|
|
symbol: 'image://' + mapIcons[0], // 图片 URL
|
|
},
|
|
]);
|
|
|
|
function changeTab(i) {
|
|
let x = Math.random() + 5 - Math.floor(Math.random() * 10) - i + Math.random() * 3.5;
|
|
let y = Math.floor(Math.random() * 10) - 5 + Math.random() + i + Math.random() * 5;
|
|
const o = {
|
|
type: 'scatter',
|
|
zlevel: 100,
|
|
coordinateSystem: 'geo',
|
|
//自定义图片的 位置(lng, lat)
|
|
data: [{ name: '太原', value: [112.55 + Math.random() + x, 37.98 + Math.random() + y], id: 'taiyuan' }],
|
|
//自定义图片的 大小
|
|
symbolSize: [25, 63],
|
|
symbol: 'image://' + mapIcons[Math.floor(Math.random() * 6)], // 图片 URL
|
|
};
|
|
const line = {
|
|
name: '线路',
|
|
type: 'lines',
|
|
coordinateSystem: 'geo',
|
|
zlevel: 100,
|
|
large: true,
|
|
effect: {
|
|
show: true,
|
|
constantSpeed: 30,
|
|
symbol: 'arrow', //ECharts 提供的标记类型包括 'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow'
|
|
symbolSize: 0,
|
|
trailLength: 0,
|
|
},
|
|
lineStyle: {
|
|
normal: {
|
|
color: '#0fff17',
|
|
width: 2,
|
|
opacity: 1.0,
|
|
curveness: 0.15,
|
|
},
|
|
},
|
|
data: [
|
|
{
|
|
fromName: '六安',
|
|
toName: '合肥市',
|
|
coords: [
|
|
[116.27, 31.786],
|
|
[117.37, 31.386],
|
|
],
|
|
},
|
|
],
|
|
};
|
|
icons.value = [];
|
|
icons.value.push(o);
|
|
// icons.value.push(line);
|
|
currentIndex.value = i;
|
|
let myChart = echarts.init(document.querySelector('.china-map-container'));
|
|
initMap(myChart);
|
|
}
|
|
|
|
onMounted(() => {
|
|
console.log('reload');
|
|
let myChart = echarts.init(document.querySelector('.china-map-container'));
|
|
echarts.registerMap('china', mapJson);
|
|
initMap(myChart);
|
|
});
|
|
|
|
function initMap(myChart) {
|
|
let option = createChartOption({ icons });
|
|
myChart.setOption(option);
|
|
// 关闭之前的点击事件
|
|
myChart.off('click');
|
|
myChart.on('click', function (res) {
|
|
mapClick(res, myChart);
|
|
});
|
|
myChart.getZr().on('click', (params) => {
|
|
if (params.target) {
|
|
// alert('画布非空白区');
|
|
} else {
|
|
// alert('画布空白区');
|
|
myChart.dispatchAction({
|
|
// 如果绑定了 geo 图层(指定了geoIndex),则是 geoUnSelect 事件
|
|
type: 'unselect',
|
|
// 指定 series 的索引
|
|
seriesIndex: 0,
|
|
// 指定要取消选中数据的索引, 如果要取消全部选中,则把所有数据的下标数组作为 dataIndex 即可
|
|
dataIndex: new Array(101).fill(null).map((_, i) => i), // 0 代表取消选中 data 中第1项的选中,依此类推
|
|
});
|
|
myChart.dispatchAction({
|
|
// 如果绑定了 geo 图层(指定了geoIndex),则是 geoUnSelect 事件
|
|
type: 'geoUnSelect',
|
|
// 指定 series 的索引
|
|
seriesIndex: 0,
|
|
// 指定要取消选中数据的索引, 如果要取消全部选中,则把所有数据的下标数组作为 dataIndex 即可
|
|
dataIndex: new Array(101).fill(null).map((_, i) => i), // 0 代表取消选中 data 中第1项的选中,依此类推
|
|
});
|
|
}
|
|
});
|
|
window.addEventListener('resize', () => {
|
|
myChart.resize();
|
|
});
|
|
}
|
|
|
|
function mapClick(res, myChart) {
|
|
if (res.componentType !== 'series') {
|
|
myChart.setOption({
|
|
series: [{ selected: {} }], // 取消所有省份的选中状态
|
|
});
|
|
}
|
|
|
|
console.log('res', res);
|
|
if ('scatter' === res.componentSubType) {
|
|
// alert(`点击了 ${res.name} 图标`);
|
|
}
|
|
if ('map' === res.componentSubType) {
|
|
// alert(`点击了 ${res.name} 地图`);
|
|
}
|
|
}
|
|
|
|
const routeList = ref([]);
|
|
const showRoutePanel = ref(false);
|
|
const panelTitle = ref();
|
|
|
|
function outClick() {
|
|
showRoutePanel.value = false;
|
|
}
|
|
|
|
function setTitle({ time, distance }) {
|
|
let str = '全程';
|
|
str += getTimeStr(time);
|
|
str += getKmStr(distance);
|
|
panelTitle.value = str;
|
|
}
|
|
|
|
function searchRoute(callback: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | null) {
|
|
showRoutePanel.value = false;
|
|
panelTitle.value = '';
|
|
if (!formState.value.start) {
|
|
return message.warn('请输入起点');
|
|
}
|
|
if (!formState.value.end) {
|
|
return message.warn('请输入终点');
|
|
}
|
|
//基本地图加载
|
|
let map = new AMap.Map('container', {
|
|
resizeEnable: true,
|
|
center: [116.397428, 39.90923], //地图中心点
|
|
zoom: 13, //地图显示的缩放级别
|
|
});
|
|
//构造路线导航类
|
|
let driving = new AMap.Driving({
|
|
map: map,
|
|
panel: 'panel',
|
|
});
|
|
// 根据起终点名称规划驾车导航路线
|
|
driving.search([{ keyword: formState.value.start }, { keyword: formState.value.end }], function (status, result) {
|
|
// result 即是对应的驾车导航信息,相关数据结构文档请参考 https://lbs.amap.com/api/javascript-api/reference/route-search#m_DrivingResult
|
|
if (status === 'complete') {
|
|
// console.log('绘制驾车路线完成');
|
|
} else {
|
|
// console.log('获取驾车数据失败:' + result);
|
|
}
|
|
});
|
|
AMap.Event.addListener(driving, 'complete', (res) => {
|
|
const data = JSON.parse(JSON.stringify(res));
|
|
if (data.info == 'OK') {
|
|
const { routes } = data;
|
|
setTitle(routes[0]);
|
|
let arr = [];
|
|
arr.push(data.originName);
|
|
routes[0].steps.map((item) => {
|
|
arr.push(item.instruction);
|
|
});
|
|
arr.push(data.destinationName);
|
|
showRoutePanel.value = true;
|
|
routeList.value = arr;
|
|
}
|
|
});
|
|
}
|
|
|
|
function removeInput() {
|
|
formState.value.start = '';
|
|
formState.value.end = '';
|
|
}
|
|
</script>
|
|
<style lang="less" scoped>
|
|
.center-map {
|
|
flex: 1;
|
|
color: #fff;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-content: space-between;
|
|
|
|
.china-map-box {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
}
|
|
|
|
.tab-list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
|
|
.tab {
|
|
width: calc(100% / 8);
|
|
height: 40px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
color: #29f1fa;
|
|
background: url('/@/assets/img/tab-default.png') no-repeat;
|
|
background-size: 100% 100%;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
|
|
&.active {
|
|
color: #fff;
|
|
background: url('/@/assets/img/tab-active.png') no-repeat;
|
|
background-size: 100% 100%;
|
|
}
|
|
}
|
|
}
|
|
|
|
.route-navigation {
|
|
margin: 12px 0;
|
|
width: 100%;
|
|
height: 46px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #002e64;
|
|
border-radius: 6px;
|
|
position: absolute;
|
|
top: 40px;
|
|
z-index: 999;
|
|
transition: all 0.3s;
|
|
|
|
:deep(.form-item-self) {
|
|
label {
|
|
color: #29f1fa;
|
|
}
|
|
|
|
.ant-input {
|
|
width: 280px;
|
|
color: #d6d9e0;
|
|
background: #2a4164;
|
|
border-radius: 2px 2px 2px 2px;
|
|
border: 1px solid #1b7ef2;
|
|
}
|
|
}
|
|
|
|
.reset-btn {
|
|
width: 64px;
|
|
height: 32px;
|
|
border-radius: 5px;
|
|
color: #45a2ff;
|
|
background: #002e64;
|
|
border: 1px solid #45a2ff;
|
|
}
|
|
|
|
.search-btn {
|
|
margin-left: 10px;
|
|
width: 88px;
|
|
height: 32px;
|
|
color: #efefef;
|
|
background: #45a2ff;
|
|
border-radius: 5px;
|
|
border-color: #45a2ff;
|
|
}
|
|
|
|
.line-container {
|
|
position: absolute;
|
|
right: 0;
|
|
top: 54px;
|
|
width: 288px;
|
|
padding: 9px 18px;
|
|
min-height: 200px;
|
|
max-height: 473px;
|
|
overflow: auto;
|
|
background: #2a4164;
|
|
border-radius: 2px;
|
|
border: 1px solid #1b7ef2;
|
|
|
|
.line-title {
|
|
margin-bottom: 9px;
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
color: rgba(255, 255, 255, 0.8);
|
|
}
|
|
|
|
.line-content {
|
|
font-size: 12px;
|
|
|
|
.start {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
&::before {
|
|
content: '起';
|
|
color: #fff;
|
|
width: 20px;
|
|
height: 20px;
|
|
margin-right: 7px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 50%;
|
|
background: #1478f5;
|
|
}
|
|
}
|
|
|
|
.end {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
&::before {
|
|
content: '终';
|
|
color: #fff;
|
|
width: 20px;
|
|
height: 20px;
|
|
margin-right: 7px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 50%;
|
|
background: #1478f5;
|
|
}
|
|
}
|
|
|
|
.line-body {
|
|
display: flex;
|
|
//max-height: 370px;
|
|
}
|
|
|
|
.line {
|
|
width: 1px;
|
|
//max-height: 370px;
|
|
background-color: #1478f5;
|
|
margin: 5px 19px 5px 8px;
|
|
}
|
|
|
|
.line-sesc {
|
|
flex: 1;
|
|
}
|
|
}
|
|
|
|
.empty-data {
|
|
margin-top: 30%;
|
|
text-align: center;
|
|
}
|
|
}
|
|
}
|
|
|
|
.china-map-container {
|
|
flex: 1;
|
|
margin: 10px;
|
|
//border: 1px solid red;
|
|
}
|
|
|
|
.map-legend {
|
|
position: absolute;
|
|
bottom: 20px;
|
|
left: 0;
|
|
width: 200px;
|
|
//border: 1px solid red;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
|
|
.legend-item {
|
|
width: 50%;
|
|
font-size: 12px;
|
|
font-weight: 400;
|
|
line-height: 30px;
|
|
|
|
.legend-img {
|
|
width: 20px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.center-footer {
|
|
display: flex;
|
|
justify-content: center;
|
|
margin-bottom: 10px;
|
|
gap: 20px;
|
|
|
|
.count-item {
|
|
width: 340px;
|
|
height: 80px;
|
|
display: flex;
|
|
color: #ffffff;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #00071b;
|
|
box-shadow: inset 0 0 40px 0 #1b7ef2;
|
|
border-radius: 4px;
|
|
|
|
.num {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.text {
|
|
font-size: 16px;
|
|
font-weight: 400;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style> |