Files
xj-screen/src/components/chinaMap/chinaMap.vue
T
2023-11-28 15:11:30 +08:00

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>