<script >
|
import svgLine from './svgLine.vue';
|
import svgStation from './svgStation.vue';
|
|
const colors = [
|
'#0f0',
|
'#f00',
|
'#00f',
|
'#ff0',
|
'#0ff',
|
'#f0f',
|
'#018AE8',
|
'#FF713B',
|
'#0AE88E',
|
];
|
|
export default {
|
components: {
|
svgLine,
|
svgStation,
|
},
|
data() {
|
return {
|
colors,
|
svgWidth: 1400,
|
svgHeight: 712,
|
padding: 100,
|
minLat: Infinity,
|
minLng: Infinity,
|
maxLat: -Infinity,
|
maxLng: -Infinity,
|
lines: [],
|
allLines: [3, 5, 6, 7, 8],
|
disableList: [],
|
viewBox: [0, 0, 100, 100],
|
lineObjs: [],
|
defaultData: [],
|
|
infoVisible: false,
|
info: {
|
name: '',
|
lineName: '',
|
status: 0,
|
},
|
left: 0,
|
top: 0,
|
transLeft: false,
|
transTop: false,
|
|
}
|
},
|
computed: {
|
filterLines() {
|
|
return this.allLines.filter(v => this.disableList.every(vv => vv != v));
|
},
|
resData() {
|
return this.defaultData.filter(v => this.filterLines.some(vv=>vv == v.ln.substr(0, 1))).map(v => {
|
return {
|
name: v.ln,
|
list: v.st.map(vv => ({
|
name: vv.n,
|
// points: vv.sl.split(',')
|
points: vv.p.split(' '),
|
p: vv.lg
|
}))
|
}
|
})
|
},
|
subWays() {
|
return this.defaultData.filter(v => this.allLines.some(vv=>vv == v.ln.substr(0, 1))).map(v => v.ln)
|
},
|
|
viewBoxArr() {
|
if (
|
this.minLat == Infinity
|
|| this.maxLat == -Infinity
|
|| this.minLng == Infinity
|
|| this.maxLng == -Infinity
|
) {
|
return [0, 0, this.svgWidth, this.svgHeight];
|
} else {
|
return [this.minLng - this.padding, this.minLat - this.padding, this.maxLng - this.minLng + this.padding * 2, this.maxLat - this.minLat + this.padding * 2];
|
}
|
},
|
},
|
methods: {
|
async getJson () {
|
|
const dynamicPath = `mapJson/subway/suzhou1.json`;
|
try {
|
const response = await fetch(dynamicPath);
|
if (!response.ok) {
|
throw new Error(`HTTP error! status: ${response.status}`);
|
}
|
const data = await response.json();
|
console.log('data', data, '=============');
|
this.defaultData = data.l;
|
} catch (error) {
|
console.error('加载 JSON 出错:', error);
|
}
|
},
|
getSize(points) {
|
|
points.forEach(point => {
|
let [lng, lat] = point;
|
lng *= 1;
|
lat *= 1;
|
if (lat < this.minLat) this.minLat = lat;
|
if (lat > this.maxLat) this.maxLat = lat;
|
if (lng < this.minLng) this.minLng = lng;
|
if (lng > this.maxLng) this.maxLng = lng;
|
});
|
|
},
|
async getPoints() {
|
let gpsPoints = this.resData;
|
|
let arr = [];
|
for (let i = 0, len = gpsPoints.length; i < len; i++) {
|
let item = gpsPoints[i];
|
item.list.forEach(v => {
|
arr.push(v.points);
|
})
|
}
|
|
console.log('arr', arr, '=============');
|
|
this.getSize(arr);
|
this.lines = gpsPoints.map((v, i) => ({ name: v.name, points: v.list.map(vv => vv.points) }));
|
this.lineObjs = gpsPoints.map((v, i) => ({ name: v.name, list: v.list.map(vv => ({ name: vv.name, points: vv.points, p: vv.p })) }));
|
},
|
toggleLine(idx) {
|
let _idx = this.subWays[idx].substr(0,1);
|
let arr = this.disableList;
|
// 判断是否是不显示的
|
let _i = arr.indexOf(_idx);
|
if(_i > -1) {
|
arr.splice(_i, 1);
|
} else {
|
arr.push(_idx);
|
}
|
this.disableList = arr;
|
// nextTick()
|
this.getPoints();
|
},
|
|
showInfo(e, data, lineName) {
|
this.infoVisible = true;
|
console.log('e', e, data, '=============');
|
this.left = e.clientX + 'px';
|
this.top = e.clientY + 'px';
|
let w = window.innerWidth;
|
let h = window.innerHeight;
|
if (e.clientX > w / 2) {
|
this.transLeft = true;
|
} else {
|
this.transLeft = false;
|
}
|
if (e.clientY > h / 2) {
|
this.transTop = true;
|
} else {
|
this.transTop = false;
|
}
|
this.info.name = data.name;
|
this.info.lineName = lineName;
|
},
|
|
hideInfo () {
|
this.infoVisible = false;
|
},
|
|
|
},
|
async mounted() {
|
await this.getJson();
|
this.getPoints();
|
},
|
}
|
</script>
|
|
<template>
|
<div class="main">
|
<div class="lines">
|
<div :class="['item', `level${item.substr(0, 1)}`, {disabled: disableList.indexOf(item.substr(0, 1)) > -1}]" v-for="(item, idx) in subWays" :key="idx" @click="toggleLine(idx)">
|
{{ item }}
|
</div>
|
</div>
|
<svg
|
class="map"
|
width="100%"
|
height="100%"
|
:viewBox="viewBoxArr.join(' ')"
|
xmlns="http://www.w3.org/2000/svg"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
style="position: relative;"
|
:transform="'scale(' + [1, 1].join(',') + ')'"
|
ref="map"
|
>
|
<svg-line
|
v-for="(item, idx) in lines"
|
:key="idx"
|
:points="item.points"
|
:color="colors[item.name.substr(0,1)]"
|
:name="subWays[idx]"
|
|
></svg-line>
|
|
<!-- 站点 -->
|
<template v-for="(item, i) in lineObjs">
|
<svg-station
|
v-for="(point, idx) in item.list"
|
:key="'line_' + i + '_point' + idx"
|
:points="point.points"
|
:name="point.name"
|
:color="colors[item.name.substr(0,1)]"
|
:pos="point.p"
|
@mouseenter.capture="(e) => showInfo(e, point, item.name)"
|
></svg-station>
|
</template>
|
</svg>
|
<teleport to='body' v-if="infoVisible">
|
<div :class="['info', {'trans-left': transLeft, 'trans-top': transTop}]" v-if="infoVisible" :style="{ left, top }">
|
<div class="info-item station-name">
|
<div class="label">站名:</div>
|
<div class="value">{{ info.name }}</div>
|
</div>
|
<div class="info-item line-name">
|
<div class="label">线路:</div>
|
<div class="value">{{ info.lineName }}</div>
|
</div>
|
<div class="info-item status">
|
<div class="label">状态:</div>
|
<div class="value">{{ info.status ? '在线' : '离线' }}</div>
|
</div>
|
</div>
|
<div class="mask" @mouseenter="hideInfo"></div>
|
</teleport>
|
</div>
|
</template>
|
|
<style scoped lang="less">
|
.item {
|
background: #0ff;
|
border-radius: 4px;
|
color: #000;
|
padding: 8px;
|
&.level3 {
|
background: #ff0;
|
}
|
&.level5 {
|
background: #f0f;
|
}
|
&.level6 {
|
background: #018AE8;
|
}
|
&.level7 {
|
background: #FF713B;
|
}
|
&.level8 {
|
background: #0AE88E;
|
}
|
&.disabled {
|
background: #ccc;
|
}
|
}
|
.main {
|
background: #333;
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
|
.lines {
|
display: flex;
|
flex-direction: row;
|
|
.item {
|
margin-right: 20px;
|
cursor: pointer;
|
}
|
}
|
|
.map {
|
flex: 1;
|
}
|
}
|
.info {
|
position: absolute;
|
background: #0ff;
|
z-index: 99;
|
color: #000;
|
padding: 12px;
|
border-radius: 8px;
|
white-space: nowrap;
|
&.trans-left {
|
transform: translateX(-100%);
|
}
|
&.trans-top {
|
transform: translateY(-100%);
|
}
|
&.trans-left.trans-top {
|
transform: translate(-100%, -100%);
|
}
|
&::after {
|
content: '';
|
position: absolute;
|
left: -6px;
|
top: -6px;
|
right: -6px;
|
bottom: -6px;
|
z-index: -1;
|
}
|
}
|
.info-item {
|
display: flex;
|
.label {
|
margin-right: 0.4em;
|
}
|
}
|
.mask {
|
position: fixed;
|
left: 0;
|
top: 0;
|
right: 0;
|
bottom: 0;
|
background: transparent;
|
z-index: 98;
|
}
|
</style>
|