Przeglądaj źródła

feat: 首页地图简化为位置图标并按需加载站点详情,解决首页加载缓慢

- 首屏改用 /charge/mapStation 轻量接口单次拉取并缓存,地图拖动仅本地重排序,不再全量调用外部 stationStatus/实时价格接口

- marker 去除空闲/总数 label,只显示位置图标;点击站点时按需查询空闲数/活动并填充卡片,下拉展开列表时批量补齐

- 清理失效的分页与 label 残留逻辑,顺带修复翻页导致列表重复追加站点的问题;空闲数未加载时显示占位符
skyline 3 tygodni temu
rodzic
commit
1988a19cf0

+ 1 - 0
charge-front/src/App.vue

@@ -5,6 +5,7 @@ export default <any>{
     token: "",
     lastData: {},
     stations: [],
+    mapStations: [], // 首页地图轻量站点缓存(不含枪统计/活动)
     normalCode: "",
     manualStop:false,//手工停止充电标识
   },

+ 83 - 0
charge-front/src/api/charge.ts

@@ -276,6 +276,89 @@ export function fetchStation(id: number) {
         });
 }
 
+/**
+ * 轻量地图站点列表(仅基础信息+当前时段费率,不含枪统计/活动/实时状态),
+ * 供首页地图 marker 使用;结果缓存于 globalData.mapStations,
+ * 地图拖动时仅按新中心本地重排序,不再请求后台
+ */
+export function fetchMapStations(
+    latitude?: number,
+    longitude?: number,
+    baseLatitude?: number,
+    baseLongitude?: number
+): Promise<any[]> {
+    return _getMapStations(latitude, longitude).then((res) => {
+        const data: any[] = JSON.parse(JSON.stringify(res));
+        // 按传入坐标(地图中心或定位点)由近到远排序
+        if (latitude && longitude) {
+            data.forEach((item: any) => {
+                item.stationLatDistance = _getDistance(
+                    latitude,
+                    longitude,
+                    item.location.stationLat,
+                    item.location.stationLng
+                );
+            });
+            data.sort((item1, item2) => {
+                return item1.stationLatDistance - item2.stationLatDistance;
+            });
+        }
+        // 卡片展示的距离以用户自身定位为准
+        if (baseLatitude && baseLongitude) {
+            data.forEach((item: any) => {
+                item.stationLatDistance = _getDistance(
+                    baseLatitude,
+                    baseLongitude,
+                    item.location.stationLat,
+                    item.location.stationLng
+                );
+            });
+        }
+        return data;
+    });
+}
+
+function _getMapStations(latitude?: number, longitude?: number): Promise<any[]> {
+    if (getApp<any>().globalData.mapStations?.length > 0) {
+        return Promise.resolve(getApp<any>().globalData.mapStations);
+    }
+    // 轻量接口单次拉取全部站点
+    return cHttp
+        .get(`/charge/mapStation?pageNum=1&pageSize=999&latitude=${latitude || 22.696779}&longitude=${longitude || 114.044805}`)
+        .then((res: any) => {
+            const data: any[] = res.list || [];
+            data.forEach((item: any) => {
+                item.StationID = item.stationId;
+                item.fastEquipmentInfos = [];
+                item.slowEquipmentInfos = [];
+                item.totalFee = Number(
+                    Number(
+                        Number(item.electricityFee) +
+                        Number(item.parkFee) +
+                        Number(item.serviceFee)
+                    ).toFixed(2)
+                );
+            });
+            const list = data.filter((item: any) => {
+                return !["1657"].includes(item.StationID);
+            });
+            getApp<any>().globalData.mapStations = list;
+            return list;
+        });
+}
+
+/**
+ * 站点卡片详情(空闲桩数量/活动),点击站点图标或展开列表时按需批量查询
+ */
+export function fetchStationCards(stationIds: Array<string | number>): Promise<any[]> {
+    if (!stationIds || !stationIds.length) {
+        return Promise.resolve([]);
+    }
+    return cHttp
+        .get(`/charge/stationCard?stationIds=${stationIds.join(",")}`)
+        .then((res: any) => res || []);
+}
+
 export function fetchStationDetail(stationId: string) {
     return cHttp
         .get(`/charge/stationInfo/${stationId}`)

+ 2 - 2
charge-front/src/components/charge-station/charge-station.vue

@@ -58,7 +58,7 @@
             <charge-icon type="slow"></charge-icon>
           </view>
           <view class="fs-22 ml-8 color-666"
-          >空闲:{{ availableParkingNum }}|共:{{ parkingNum }}
+          >空闲:{{ availableParkingNum == null ? '···' : availableParkingNum }}|共:{{ parkingNum }}
           </view
           >
         </view>
@@ -72,7 +72,7 @@
           >
             <charge-icon type="slow"></charge-icon>
             <view class="ml-6 fs-22 color-666"
-            >{{ availableParkingNum }} / {{ parkingNum }}
+            >{{ availableParkingNum == null ? '···' : availableParkingNum }} / {{ parkingNum }}
             </view
             >
           </view>

+ 95 - 102
charge-front/src/pages/map/map.vue

@@ -127,7 +127,6 @@
       :scale="mapProps.scale"
       @regionchange="mapChange"
       @updated="mapUpdated"
-      @labeltap="tapMarker"
       @markertap="tapMarker"
       :show-scale="true"
     ></map>
@@ -269,11 +268,9 @@ const defaultScale = 12;
 const pointSize = {
   width: 30,
   height: 55,
-  fontSize: 7,
   iconPath: "/static/images/map-point.png",
   currentWidth: 42,
   currentHeight: 72,
-  currentFontSize: 10,
   currentIconPath: "/static/images/map-point-current.png",
   androidX:-4,
   androidCurrentX: -7,
@@ -282,15 +279,14 @@ import { fetchHomeBanner } from "@/api";
 import { deCode } from "../../utils/code";
 import { rpxToPx } from "../../utils/device";
 import { fetchToken, onLogin } from "@/api/auth";
-import { fetchStations, fetchChargeStatus } from "@/api/charge";
+import { fetchMapStations, fetchStationCards, fetchChargeStatus } from "@/api/charge";
 import { fetchCollectList } from "@/api/user";
 import { fetchLocation } from "@/utils/location";
 import { to } from "@/utils/navigate";
 import { onLoad, onShow } from "@dcloudio/uni-app";
-import { ref } from "vue";
+import { ref, watch } from "vue";
 
-const currentLocation = ref(null)
-const isIOS = ref(false);
+const currentLocation = ref<any>(null)
 const token = ref<string>();
 const ready = ref(false);
 const loading = ref(false);
@@ -364,11 +360,6 @@ const filterDialog = ref({
   ],
 });
 
-const stationPage = ref({
-  page: 1,
-  pageSize: 6,
-  hasNext: false,
-});
 const station = ref<any[]>([]);
 const charging = ref<any>();
 
@@ -387,92 +378,42 @@ const mapBanner = ref<any[]>([]);
 let isIgnoreChangeLocation = false;
 
 const refreshStation = (location: any) => {
-  let length = 0;
-  let available = 0;
   const { latitude, longitude } = location;
-  return fetchStations(
-    stationPage.value.page,
-    stationPage.value.pageSize,
+  // 轻量接口:仅站点基础信息,地图拖动时走缓存本地重排序,不再请求后台
+  return fetchMapStations(
     latitude,
     longitude,
     mapProps.value.selflatitude,
-    mapProps.value.selflongitude,
-    {
-      distance: filterDialog.value.options.distance,
-      status: filterDialog.value.options.status,
-    }
+    mapProps.value.selflongitude
   ).then((res) => {
-    const _markersIndex = stationPage.value.page === 1 ? 0 : markersIndex.value;
-    // console.log("xxxxx",res)
-    const _markers: any[] = res.map((item, index) => {
-      item.totalFee =(Number(item.currentPolicyInfo?.servicePrice||0)+Number(item.currentPolicyInfo?.elecPrice||0)).toFixed(2)
-      length = 0;
-      available = 0;
-      // item.equipmentInfos &&
-      //   item.equipmentInfos.forEach((eq: any) => {
-      //     eq.connectorInfos &&
-      //       eq.connectorInfos.forEach((co: any) => {
-      //         length += 1;
-      //         if (
-      //           co.connectorStatusInfo &&
-      //           co.connectorStatusInfo.status === 1
-      //         ) {
-      //           available += 1;
-      //         }
-      //       });
-      //   });
+    const _markers: any[] = res.map((item: any, index: number) => {
+      item.totalFee = (Number(item.currentPolicyInfo?.servicePrice||0)+Number(item.currentPolicyInfo?.elecPrice||0)).toFixed(2)
       return {
         id: Number(item.StationID),
-        // (Number()+Number(item.electricityFee)||0).toFixed(2),
         latitude: item.location.stationLat,
         longitude: item.location.stationLng,
         iconPath:
-          index === _markersIndex
+          index === 0
             ? pointSize.currentIconPath
             : pointSize.iconPath,
-        width:
-          index === _markersIndex ? pointSize.currentWidth : pointSize.width,
-        height:
-          index === _markersIndex ? pointSize.currentHeight : pointSize.height,
-        label: {
-          content: `${item.availableParkingNum||0}/${item.parkingNum||0}`,
-          // content: `1/10`,
-          color: "#ffffff",
-          fontSize:
-            index === _markersIndex
-              ? pointSize.currentFontSize
-              : pointSize.fontSize,
-          textAlign: isIOS.value ? "center" : "left",
-          anchorX: isIOS.value
-            ? 0
-            : index === _markersIndex
-            ? -17 // 针对安卓设备调整 anchorX 实现居中
-            : -10,
-          anchorY: -(
-            (index === _markersIndex
-              ? pointSize.currentHeight
-              : pointSize.height- 3)
-          ),
-        },
+        width: index === 0 ? pointSize.currentWidth : pointSize.width,
+        height: index === 0 ? pointSize.currentHeight : pointSize.height,
       };
     });
-    if (stationPage.value.page === 1) {
-      _markers.push({
-        id: -1,
-        latitude: mapProps.value.selflatitude,
-        longitude: mapProps.value.selflongitude,
-        iconPath: "/static/images/map-current.png",
-        width: 34,
-        height: 34,
-      });
-    }
+    _markers.push({
+      id: -1,
+      latitude: mapProps.value.selflatitude,
+      longitude: mapProps.value.selflongitude,
+      iconPath: "/static/images/map-current.png",
+      width: 34,
+      height: 34,
+    });
     isIgnoreChangeLocation = markers.value.length !== _markers.length;
-    stationPage.value.hasNext = res.length >= stationPage.value.pageSize;
-    // empty.value = stationPage.value.page === 1 ? res.length <= 0 : false;
-    station.value =
-      stationPage.value.page === 1 ? res : [...station.value, ...res];
-    markersIndex.value = _markersIndex;
+    station.value = res;
+    markersIndex.value = 0;
     markers.value = _markers;
+    // 默认选中距离最近的站点,按需加载其卡片详情(空闲数/活动)
+    loadStationCard(0);
     return res;
   });
 };
@@ -486,8 +427,6 @@ const refresh = () => {
     title: "加载中",
   });
   loading.value = true;
-  stationPage.value.page = 1;
-  stationPage.value.hasNext = false;
   station.value = [];
   markers.value = [];
   markersIndex.value = 0;
@@ -549,8 +488,6 @@ onLoad((query: any) => {
     console.log("扫普通码", decodeURIComponent(query.q));
     getApp<any>().globalData.normalCode = decodeURIComponent(query.q); // 获取到二维码原始链接内容
   }
-  const device = uni.getSystemInfoSync();
-  isIOS.value = device.osName === "ios";
 
   setTimeout(() => {
     token.value = getApp<any>().globalData.token || "";
@@ -690,7 +627,6 @@ const mapChange = (e: any) => {
 
 const debounceRefreshStation = throttle(() => {
   const { latitude, longitude } = currentLocation.value;
-  stationPage.value.page = 1;
   console.log("debounceRefreshStation run...")
   refreshStation({
     latitude,
@@ -698,35 +634,90 @@ const debounceRefreshStation = throttle(() => {
   });
 }, 500)
 
+/**
+ * 按需加载单个站点卡片详情(空闲桩数量/活动),点击站点图标时调用
+ */
+const loadStationCard = (index: number) => {
+  const item = station.value[index];
+  if (!item || item.cardLoaded) {
+    return;
+  }
+  item.cardLoaded = true; // 先标记防止重复请求,失败时回滚
+  fetchStationCards([item.StationID])
+    .then((cards: any[]) => {
+      const card = cards.find(
+        (c: any) => Number(c.stationId) === Number(item.StationID)
+      );
+      if (card) {
+        item.availableParkingNum = card.availableParkingNum;
+        item.activityList = card.activityList;
+        if (card.parkingNum != null) {
+          item.parkingNum = card.parkingNum;
+        }
+      }
+    })
+    .catch(() => {
+      item.cardLoaded = false;
+    });
+};
+
+/**
+ * 展开列表模式时,批量加载尚未加载详情的站点卡片(空闲桩数量/活动)
+ */
+const loadAllStationCards = () => {
+  const ids = station.value
+    .filter((item: any) => !item.cardLoaded)
+    .map((item: any) => item.StationID);
+  if (!ids.length) {
+    return;
+  }
+  station.value.forEach((item: any) => (item.cardLoaded = true));
+  fetchStationCards(ids)
+    .then((cards: any[]) => {
+      cards.forEach((card: any) => {
+        const item = station.value.find(
+          (s: any) => Number(s.StationID) === Number(card.stationId)
+        );
+        if (item) {
+          item.availableParkingNum = card.availableParkingNum;
+          item.activityList = card.activityList;
+          if (card.parkingNum != null) {
+            item.parkingNum = card.parkingNum;
+          }
+        }
+      });
+    })
+    .catch(() => {
+      // 失败时回滚未加载站点的标记,允许下次重试
+      station.value.forEach((item: any) => {
+        if (item.availableParkingNum == null) {
+          item.cardLoaded = false;
+        }
+      });
+    });
+};
+
+// 下拉展开列表时批量补齐卡片详情
+watch(mapMode, (val) => {
+  if (!val) {
+    loadAllStationCards();
+  }
+});
+
 const _changeMarker = (current: number) => {
   const _markers = JSON.parse(JSON.stringify(markers.value));
   const markersNewIndex = current;
   _markers[markersIndex.value].iconPath = pointSize.iconPath;
   _markers[markersIndex.value].width = pointSize.width;
   _markers[markersIndex.value].height = pointSize.height;
-  _markers[markersIndex.value].label.fontSize = pointSize.fontSize;
-  _markers[markersIndex.value].label.anchorY = -(pointSize.height - 3);
-  _markers[markersIndex.value].label.textAlign = isIOS.value ? "center" : "left";
-  _markers[markersIndex.value].label.anchorX = isIOS.value ? 0 : -10;
 
   _markers[markersNewIndex].iconPath = pointSize.currentIconPath;
   _markers[markersNewIndex].width = pointSize.currentWidth;
   _markers[markersNewIndex].height = pointSize.currentHeight;
-  _markers[markersNewIndex].label.fontSize = pointSize.currentFontSize;
-  _markers[markersNewIndex].label.textAlign = isIOS.value ? "center" : "left";
-  _markers[markersNewIndex].label.anchorY = -(pointSize.currentHeight - 3);
-  _markers[markersNewIndex].label.anchorX = isIOS.value ? 0 : -17;
 
   isIgnoreChangeLocation = true;
   markers.value = _markers;
   markersIndex.value = markersNewIndex;
-  if (stationPage.value.hasNext && markersNewIndex >= _markers.length - 2) {
-    stationPage.value.page += 1;
-    refreshStation({
-      latitude: mapProps.value.selflatitude,
-      longitude: mapProps.value.selflongitude,
-    });
-  }
 };
 const tapMarker = (e: any) => {
   if (e.detail.markerId === -1) {
@@ -737,6 +728,8 @@ const tapMarker = (e: any) => {
   );
   if (findIndex >= 0) {
     _changeMarker(findIndex);
+    // 点击站点图标时按需查询详情(空闲数/活动)
+    loadStationCard(findIndex);
   }
 };