|
|
@@ -58,6 +58,9 @@ export default {
|
|
|
map: null,
|
|
|
infoWindow: null,
|
|
|
|
|
|
+ trafficLayer: null, // 存储交通图层实例
|
|
|
+ isTrafficVisible: true, // 控制开关状态
|
|
|
+
|
|
|
routeGroups: {},
|
|
|
polylines: [],
|
|
|
privateStyle: {
|
|
|
@@ -230,16 +233,16 @@ export default {
|
|
|
const dutyData = this.dutyRoutes.flat();
|
|
|
// 排除路线数据后,剩余数据分配给其他状态
|
|
|
const remainingData = this.intersectionData;
|
|
|
-
|
|
|
+
|
|
|
const normalStatusCount = 6;
|
|
|
const abnormalStatusCount = 3;
|
|
|
const chunkSize = Math.floor(remainingData.length / (normalStatusCount + abnormalStatusCount));
|
|
|
const maxAbnormalCount = 10;
|
|
|
|
|
|
this.statusIntersections = {
|
|
|
- "中心计划": remainingData.slice(0, chunkSize),
|
|
|
- "干线协调": trunkData,
|
|
|
- "勤务路线": dutyData,
|
|
|
+ "中心计划": remainingData.slice(0, 20),
|
|
|
+ "干线协调": [], // 清空,改为动态生成
|
|
|
+ "勤务路线": [], // 清空,改为动态生成
|
|
|
"定周期控制": remainingData.slice(chunkSize, chunkSize * 2),
|
|
|
"感应控制": remainingData.slice(chunkSize * 2, chunkSize * 3),
|
|
|
"自适应控制": remainingData.slice(chunkSize * 3, chunkSize * 4),
|
|
|
@@ -286,25 +289,38 @@ export default {
|
|
|
async initAMap() {
|
|
|
if (this._isDestroyed) return;
|
|
|
|
|
|
+ // 确保在加载前注入
|
|
|
window._AMapSecurityConfig = { securityJsCode: this.securityJsCode };
|
|
|
+
|
|
|
try {
|
|
|
- // 固定加载需要的插件
|
|
|
const AMap = await AMapLoader.load({
|
|
|
key: this.amapKey,
|
|
|
version: "2.0",
|
|
|
+ // 核心:确保 Driving 插件在此加载
|
|
|
plugins: ['AMap.Driving']
|
|
|
});
|
|
|
|
|
|
- // 异步回来后,首先检查组件是否还在
|
|
|
if (this._isDestroyed) return;
|
|
|
|
|
|
this.AMap = AMap;
|
|
|
this.map = new AMap.Map(this.$refs.mapContainer, {
|
|
|
- zoom: 14.5,
|
|
|
+ zoom: 13.8,
|
|
|
mapStyle: "amap://styles/darkblue",
|
|
|
- center: [116.663, 39.905],
|
|
|
+ center: [116.663, 39.905], // 通州区中心
|
|
|
+ });
|
|
|
+
|
|
|
+ this.trafficLayer = new AMap.TileLayer.Traffic({
|
|
|
+ autoRefresh: true, // 是否自动刷新,默认为false
|
|
|
+ interval: 180, // 刷新间隔,单位秒
|
|
|
+ zIndex: 10 // 确保交通线在底层,勤务/干线协调线(zIndex: 15)在其上方
|
|
|
});
|
|
|
|
|
|
+ // 如果默认开启,则添加
|
|
|
+ if (this.isTrafficVisible) {
|
|
|
+ this.map.add(this.trafficLayer);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 建议在地图加载完成后再画线
|
|
|
this.map.on('complete', () => {
|
|
|
if (!this._isDestroyed) {
|
|
|
this.drawStaticRoutes();
|
|
|
@@ -315,123 +331,134 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
-
|
|
|
-
|
|
|
+ // 1. 修改 drawStaticRoutes 方法
|
|
|
drawStaticRoutes() {
|
|
|
if (!this.isMapReady()) return;
|
|
|
|
|
|
- this.statusConfig.forEach((config, index) => {
|
|
|
- // 使用箭头函数保持 this 指向 Vue 实例
|
|
|
- setTimeout(() => {
|
|
|
- if (!this.isMapReady()) return;
|
|
|
-
|
|
|
- try {
|
|
|
- const intersections = this.statusIntersections[config.name] || [];
|
|
|
- const markers = [];
|
|
|
- const polylines = [];
|
|
|
-
|
|
|
- // 路线逻辑:对于路线类型,按分段创建连接线
|
|
|
- if (config.name === "干线协调") {
|
|
|
- this.trunkRoutes.forEach(route => {
|
|
|
- if (route.length >= 2) {
|
|
|
- const path = route.map(item => [item["位置-经度"], item["位置-纬度"]]);
|
|
|
- polylines.push(new this.AMap.Polyline({
|
|
|
- path: path,
|
|
|
- strokeColor: config.color,
|
|
|
- strokeWeight: 6,
|
|
|
- strokeOpacity: 0.6,
|
|
|
- zIndex: 15
|
|
|
- }));
|
|
|
- }
|
|
|
- });
|
|
|
- } else if (config.name === "勤务路线") {
|
|
|
- this.dutyRoutes.forEach(route => {
|
|
|
- if (route.length >= 2) {
|
|
|
- const path = route.map(item => [item["位置-经度"], item["位置-纬度"]]);
|
|
|
- polylines.push(new this.AMap.Polyline({
|
|
|
- path: path,
|
|
|
- strokeColor: config.color,
|
|
|
- strokeWeight: 6,
|
|
|
- strokeOpacity: 0.6,
|
|
|
- zIndex: 15
|
|
|
- }));
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+ const realRouteConfigs = {
|
|
|
+ "干线协调": [
|
|
|
+ { start: [116.6421, 39.9172], end: [116.6825, 39.9172], color: "#13C373" },
|
|
|
+ { start: [116.6432, 39.9071], end: [116.6833, 39.9071], color: "#13C373" },
|
|
|
+ { start: [116.6445, 39.8975], end: [116.6846, 39.8975], color: "#13C373" }
|
|
|
+ ],
|
|
|
+ "勤务路线": [
|
|
|
+ { start: [116.6521, 39.9225], end: [116.6521, 39.8971], color: "#BC301D" },
|
|
|
+ { start: [116.6734, 39.9225], end: [116.6734, 39.8971], color: "#BC301D" },
|
|
|
+ { start: [116.6850, 39.9170], end: [116.6850, 39.9000], color: "#BC301D" }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ this.statusConfig.forEach((config) => {
|
|
|
+ // 1. 处理普通非路线状态(从 mock 数据加载)
|
|
|
+ if (!realRouteConfigs[config.name]) {
|
|
|
+ const intersections = this.statusIntersections[config.name] || [];
|
|
|
+ const markers = intersections.map(item =>
|
|
|
+ this.createTrafficLightMarker([item["位置-经度"], item["位置-纬度"]], config)
|
|
|
+ ).filter(Boolean);
|
|
|
+ this.routeGroups[config.name] = markers;
|
|
|
+ if (this.activeLegends.includes(config.name)) this.map.add(markers);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 处理路线类(干线/特勤)
|
|
|
+ if (!this.routeGroups[config.name]) this.routeGroups[config.name] = [];
|
|
|
+ const driving = new this.AMap.Driving({ map: null, hideMarkers: true });
|
|
|
+
|
|
|
+ realRouteConfigs[config.name].forEach((line, lineIdx) => {
|
|
|
+ driving.search(line.start, line.end, (status, result) => {
|
|
|
+ if (status === 'complete' && result.routes[0]) {
|
|
|
+ const route = result.routes[0];
|
|
|
+ const fullPath = [];
|
|
|
+
|
|
|
+ // 提取完整路径用于画线
|
|
|
+ route.steps.forEach(step => fullPath.push(...step.path));
|
|
|
|
|
|
- // 为每个路口创建标记
|
|
|
- intersections.forEach((item, idx) => {
|
|
|
- const position = [item["位置-经度"], item["位置-纬度"]];
|
|
|
- const markerConfig = {
|
|
|
- ...config,
|
|
|
- name: config.name,
|
|
|
- id: item["路口编号"],
|
|
|
- road: item["路口名称"],
|
|
|
- time: new Date().toLocaleString('zh-CN')
|
|
|
- };
|
|
|
- markers.push(this.createTrafficLightMarker(position, markerConfig));
|
|
|
- });
|
|
|
-
|
|
|
- const overlays = [...markers, ...polylines].filter(Boolean);
|
|
|
- this.routeGroups[config.name] = overlays;
|
|
|
-
|
|
|
- if (this.isMapReady() && this.activeLegends.includes(config.name)) {
|
|
|
- this.map.add(overlays);
|
|
|
+ const polyline = new this.AMap.Polyline({
|
|
|
+ path: fullPath,
|
|
|
+ strokeColor: line.color,
|
|
|
+ strokeWeight: 6, strokeOpacity: 0.8, zIndex: 15
|
|
|
+ });
|
|
|
+ this.routeGroups[config.name].push(polyline);
|
|
|
+
|
|
|
+ // --- 密集点位生成逻辑 ---
|
|
|
+ // 策略:每隔固定步长从全路径 fullPath 中取点,确保视觉上点位均匀且密集
|
|
|
+ const totalPoints = fullPath.length;
|
|
|
+ // Demo 演示建议每条线显示 10-15 个点,根据路径长度动态调整步长
|
|
|
+ const stepSize = Math.max(Math.floor(totalPoints / 12), 1);
|
|
|
+
|
|
|
+ for (let i = 0; i < totalPoints; i += stepSize) {
|
|
|
+ const p = fullPath[i];
|
|
|
+ const marker = this.createTrafficLightMarker([p.lng, p.lat], {
|
|
|
+ ...config,
|
|
|
+ id: `MOCK-${config.name.charAt(0)}-${lineIdx}-${i}`,
|
|
|
+ road: `${config.name}路口-${i}`
|
|
|
+ });
|
|
|
+ if (marker) this.routeGroups[config.name].push(marker);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.activeLegends.includes(config.name)) {
|
|
|
+ this.map.add(this.routeGroups[config.name]);
|
|
|
+ }
|
|
|
}
|
|
|
- } catch (e) {
|
|
|
- console.warn('处理路线数据时出错:', e);
|
|
|
- }
|
|
|
- }, index * 200);
|
|
|
+ });
|
|
|
+ });
|
|
|
});
|
|
|
},
|
|
|
|
|
|
- createTrafficLightMarker(position, config) {
|
|
|
+ createTrafficLightMarker(position, config, type = 'normal') {
|
|
|
if (!position || !config) return null;
|
|
|
|
|
|
try {
|
|
|
- const isAbnormal = ["离线", "降级", "故障"].includes(config.name);
|
|
|
const lng = Number(position[0] || position.lng);
|
|
|
const lat = Number(position[1] || position.lat);
|
|
|
-
|
|
|
- // 验证坐标有效性
|
|
|
if (isNaN(lng) || isNaN(lat)) return null;
|
|
|
|
|
|
- // 3. 【视觉优化】调整 Marker 大小比例
|
|
|
- // 异常状态图标略大(为了警示),普通点位略小且半透明
|
|
|
- const size = isAbnormal ? '18px' : '14px';
|
|
|
- const opacity = isAbnormal ? '1' : '0.85';
|
|
|
- const shadow = isAbnormal ? `0 0 10px ${config.color || '#999'}` : `0 0 5px ${config.color || '#999'}`;
|
|
|
+ // 状态文字:起、终、或者状态配置的首字母
|
|
|
+ let displayText = config.name ? config.name.charAt(0) : '';
|
|
|
+ if (type === 'start') displayText = '起';
|
|
|
+ if (type === 'end') displayText = '终';
|
|
|
+
|
|
|
+ const isAbnormal = ["离线", "降级", "故障"].includes(config.name);
|
|
|
+
|
|
|
+ // 动态计算尺寸:起终点最大,异常点次之,普通点最小
|
|
|
+ const size = (type === 'start' || type === 'end') ? '22px' : (isAbnormal ? '18px' : '14px');
|
|
|
+
|
|
|
+ // 边框样式:起终点用实白边框
|
|
|
+ const borderStyle = (type === 'start' || type === 'end')
|
|
|
+ ? '2px solid #fff'
|
|
|
+ : '1.5px solid rgba(255,255,255,0.7)';
|
|
|
|
|
|
const marker = new this.AMap.Marker({
|
|
|
position: [lng, lat],
|
|
|
- zIndex: isAbnormal ? 110 : 100, // 异常图标显示在更上层
|
|
|
+ // 这里的 type 已经从参数拿到了,不会报错了
|
|
|
+ zIndex: (type === 'start' || type === 'end') ? 120 : (isAbnormal ? 110 : 100),
|
|
|
content: `
|
|
|
- <div class="pure-light-node ${isAbnormal ? 'breathe abnormal-node' : ''}"
|
|
|
- style="
|
|
|
- width: ${size};
|
|
|
- height: ${size};
|
|
|
- background: ${config.color || '#999'};
|
|
|
- box-shadow: ${shadow};
|
|
|
- opacity: ${opacity};
|
|
|
- border: 1.5px solid rgba(255,255,255,0.7);
|
|
|
- font-size: 12px;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- color: #fff;
|
|
|
- padding: 8px;
|
|
|
- ">
|
|
|
- <span style="transform: scale(0.7); font-weight: bold;">${config.name.charAt(0)}</span>
|
|
|
- </div>
|
|
|
- `,
|
|
|
- offset: new this.AMap.Pixel(-8, -8),
|
|
|
+ <div class="pure-light-node ${isAbnormal ? 'breathe' : ''}"
|
|
|
+ style="
|
|
|
+ width: ${size};
|
|
|
+ height: ${size};
|
|
|
+ background: ${config.color || '#999'};
|
|
|
+ box-shadow: 0 0 8px ${config.color};
|
|
|
+ border: ${borderStyle};
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ color: #fff;
|
|
|
+ border-radius: 50%;
|
|
|
+ cursor: pointer;
|
|
|
+ padding: 8px;
|
|
|
+ ">
|
|
|
+ <span style="transform: scale(0.8); font-weight: bold; font-size: 12px;">${displayText}</span>
|
|
|
+ </div>
|
|
|
+ `,
|
|
|
+ offset: new this.AMap.Pixel(-11, -11),
|
|
|
extData: {
|
|
|
...config,
|
|
|
position: [lng, lat],
|
|
|
statusColor: config.color || '#999',
|
|
|
statusLabel: isAbnormal ? config.name : "正常运行",
|
|
|
- road: config.road || '未知路口',
|
|
|
- time: config.time || new Date().toLocaleString('zh-CN')
|
|
|
+ road: config.road || '规划路口',
|
|
|
+ time: new Date().toLocaleString('zh-CN')
|
|
|
}
|
|
|
});
|
|
|
|