wzp
2025-05-12 2f66c1634aad641ae396b8ae9ed64c0377968e08
ruoyi-ui/src/views/system/gps/map.vue
@@ -26,6 +26,8 @@
          range-separator="-"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          :default-time="['00:00:00', '23:59:59']"
          @change="handleDateRangeChange"
        ></el-date-picker>
      </el-form-item>
      <el-form-item>
@@ -56,7 +58,9 @@
          >
            <el-table-column label="时间" align="center" prop="collectTime" />
            <el-table-column label="速度(km/h)" align="center" prop="speed" />
            <el-table-column label="经度" align="center" prop="longitude" />
            <el-table-column label="纬度" align="center" prop="latitude" />
            <el-table-column label="方向(°)" align="center" prop="direction" />
          </el-table>
        </el-card>
      </el-col>
@@ -104,7 +108,7 @@
</template>
<script>
import { listGps } from "@/api/system/gps";
import { anonymousList } from "@/api/system/gps";
export default {
  name: "GpsMap",
@@ -126,9 +130,28 @@
      dateRange: [],
      // 查询参数
      queryParams: {
        vehicleNo: undefined,
        orderByColumn: "collect_time",
        isAsc: "desc",
        pageNum: 1,
        pageSize: 1000,
        deviceId: null,
        appId: null,
        sign: null,
        timestamp: null,
        vehicleNo: null,
        beginTime: null,
        endTime: null
      },
      // 表单参数
      form: {
        vehicleNo: null
      },
      // 表单校验
      rules: {
        vehicleNo: [
          { required: true, message: "车牌号不能为空", trigger: "blur" }
        ],
        dateRange: [
          { required: true, message: "请选择时间范围", trigger: "change" }
        ]
      },
      // 地图对象
      map: null,
@@ -153,19 +176,54 @@
  created() {
    // 获取URL参数
    const query = this.$route.query;
    if (query.vehicleNo) {
      this.queryParams.vehicleNo = query.vehicleNo;
    // if (query.vehicleNo) {
    //   this.queryParams.vehicleNo = query.vehicleNo;
    // } else {
    //   this.$message.error('缺少车牌号参数');
    //   return;
    // }
    //获取订单号
    this.queryParams.orderId = query.orderId;
    if(this.queryParams.orderId==null)
    {
      this.$message.error('缺少订单号参数');
      return;
    }
    // 设置时间范围
    if (query.startTime && query.endTime) {
      this.dateRange = [query.startTime, query.endTime];
    // 检查时间参数
    // if (query.beginTime && query.endTime) {
    //   // 格式化时间
    //   this.dateRange = [
    //     this.formatDateTime(query.beginTime),
    //     this.formatDateTime(query.endTime)
    //   ];
    //   this.queryParams.beginTime = this.dateRange[0];
    //   this.queryParams.endTime = this.dateRange[1];
    // } else {
    //   this.$message.error('缺少时间范围参数');
    //   return;
    // }
    // 设置认证参数
    if (query.appId) {
      this.queryParams.appId = query.appId;
    }
    if (query.sign) {
      this.queryParams.sign = query.sign;
    }
    if (query.timestamp) {
      this.queryParams.timestamp = query.timestamp;
    }
    this.getList();
  },
  mounted() {
    // 动态加载百度地图API
    this.loadBMapScript().then(() => {
      this.initMap();
    this.initMap().then(() => {
      window.initMapFlag = true;
      if (window.loadGpsList) {
        this.drawTrack();
      }
    });
  },
  methods: {
@@ -176,76 +234,147 @@
          resolve(window.BMap);
          return;
        }
        window.initBMap = () => {
          console.log("百度地图API加载成功");
          resolve(window.BMap);
        };
        const script = document.createElement("script");
        script.type = "text/javascript";
        script.src =
          "https://api.map.baidu.com/api?v=3.0&ak=n5z5pKfAnaP3fYMR4RJOAQsR1wQ2avAn&callback=initBMap";
        script.onerror = reject;
        document.head.appendChild(script);
        window.initBMap = () => {
          // 加载坐标转换库
          const convertorScript = document.createElement("script");
          convertorScript.type = "text/javascript";
          convertorScript.src =
            "https://api.map.baidu.com/getscript?v=3.0&ak=n5z5pKfAnaP3fYMR4RJOAQsR1wQ2avAn&services=&t=20230101100000";
          convertorScript.onload = () => {
            // 加载坐标转换工具
            const toolsScript = document.createElement("script");
            toolsScript.type = "text/javascript";
            toolsScript.src =
              "https://api.map.baidu.com/library/Convertor/1.4/src/Convertor_min.js";
            toolsScript.onload = () => {
              resolve(window.BMap);
            };
            document.head.appendChild(toolsScript);
          };
          document.head.appendChild(convertorScript);
        script.onerror = (error) => {
          console.error("百度地图API加载失败", error);
          reject(error);
        };
        document.head.appendChild(script);
      });
    },
    /** 格式化时间 */
    parseTime(time) {
      const year = time.getFullYear();
      const month = String(time.getMonth() + 1).padStart(2, '0');
      const day = String(time.getDate()).padStart(2, '0');
      const hours = String(time.getHours()).padStart(2, '0');
      const minutes = String(time.getMinutes()).padStart(2, '0');
      const seconds = String(time.getSeconds()).padStart(2, '0');
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    },
    /** 处理时间格式 */
    formatDateTime(dateTimeStr) {
      if (!dateTimeStr) return '';
      // 如果时间字符串不包含秒,添加秒
      if (dateTimeStr.length === 16) { // yyyy-MM-dd HH:mm
        return dateTimeStr + ':00';
      }
      return dateTimeStr;
    },
    /** 查询GPS列表 */
    getList() {
      this.loading = true;
      listGps(this.addDateRange(this.queryParams, this.dateRange)).then(
        (response) => {
          this.gpsList = response.rows;
          this.total = response.total;
          this.loading = false;
      this.loading = true;
      // 构建查询参数
      const params = {
        ...this.queryParams
      };
      // 如果没有选择时间范围,则使用URL中的时间
      // if (!this.dateRange || this.dateRange.length === 0) {
      //   const query = this.$route.query;
      //   if (query.beginTime && query.endTime) {
      //     params.beginTime = query.beginTime;
      //     params.endTime = query.endTime;
      //   } else {
      //     this.$message.error('请选择时间范围');
      //     this.loading = false;
      //     return;
      //   }
      // } else {
      //   params.beginTime = this.dateRange[0];
      //   params.endTime = this.dateRange[1];
      // }
      anonymousList(params).then(response => {
        this.gpsList = response.rows;
        this.total = response.total;
        this.loading = false;
        window.loadGpsList=true;
        if(window.initMapFlag){
          this.drawTrack();
        }
      );
      });
    },
    async translatePoints(points) {
      // 将WGS84坐标转换为百度坐标
      var translatePoints = [];
      return new Promise((resolve, reject) => {
    /** 坐标转换方法 */
    translatePoint(point) {
      return new Promise((resolve) => {
        // 使用百度地图API内置的坐标转换
        const convertor = new BMap.Convertor();
        convertor.translate(points, 1, 5, (data) => {
        const pointArr = [];
        pointArr.push(point);
        convertor.translate(pointArr, 1, 5, (data) => {
          if (data.status === 0) {
            translatePoints = data.points;
            resolve(translatePoints);
            resolve(data.points[0]);
          } else {
            // 如果转换失败,返回原始坐标
            resolve(point);
          }
        });
      });
    },
    /** 批量坐标转换 */
    async translatePoints(points) {
      const translatePoints = [];
      for (const point of points) {
        const translatedPoint = await this.translatePoint(point);
        translatePoints.push(translatedPoint);
      }
      return translatePoints;
    },
    /** 搜索按钮操作 */
    handleQuery() {
      if (!this.queryParams.vehicleNo) {
        this.$message.error('请输入车牌号');
        return;
      }
      if (!this.dateRange || this.dateRange.length !== 2) {
        this.$message.error('请选择时间范围');
        return;
      }
      // 更新查询参数
      this.queryParams.beginTime = this.dateRange[0];
      this.queryParams.endTime = this.dateRange[1];
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.dateRange = [];
      // 重置为当天时间范围
      const today = new Date();
      const startTime = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0);
      const endTime = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59);
      this.dateRange = [this.parseTime(startTime), this.parseTime(endTime)];
      this.resetForm("queryForm");
      // 保留车牌号
      this.queryParams.vehicleNo = this.$route.query.vehicleNo;
      // 更新查询参数
      this.queryParams.beginTime = this.dateRange[0];
      this.queryParams.endTime = this.dateRange[1];
      this.handleQuery();
    },
    /** 初始化地图 */
    initMap() {
      // 创建地图实例
      this.map = new BMap.Map("mapContainer");
      // 设置地图中心点和缩放级别
      this.map.centerAndZoom(new BMap.Point(116.404, 39.915), 11);
      // 启用滚轮放大缩小
      this.map.enableScrollWheelZoom();
    async initMap() {
      try {
        await this.loadBMapScript();
        // 创建地图实例
        this.map = new BMap.Map("mapContainer");
        // 设置地图中心点和缩放级别
        this.map.centerAndZoom(new BMap.Point(116.404, 39.915), 11);
        // 启用滚轮放大缩小
        this.map.enableScrollWheelZoom();
        console.log("地图初始化成功");
      } catch (error) {
        console.error("地图初始化失败", error);
        this.$message.error("地图加载失败,请刷新页面重试");
      }
    },
     /** 计算两点之间的距离(米) */
     getDistance(point1, point2) {
@@ -258,34 +387,7 @@
        return Math.atan2(dy, dx) * 180 / Math.PI;
      },
     /** 在两点之间插入平滑点 */
     getSmoothPoints(point1, point2) {
        const distance = this.getDistance(point1, point2);
        if (distance < this.minDistance) {
          return [point1, point2];
        }
        const angle = this.getAngle(point1, point2);
        const midPoint = new BMap.Point(
          (point1.lng + point2.lng) / 2,
          (point1.lat + point2.lat) / 2
        );
        // 计算控制点
        const controlPoint = new BMap.Point(
          midPoint.lng + this.smoothFactor * distance * Math.cos((angle + 90) * Math.PI / 180),
          midPoint.lat + this.smoothFactor * distance * Math.sin((angle + 90) * Math.PI / 180)
        );
        // 使用二次贝塞尔曲线生成平滑点
        const points = [];
        for (let t = 0; t <= 1; t += 0.1) {
          const x = Math.pow(1 - t, 2) * point1.lng + 2 * (1 - t) * t * controlPoint.lng + Math.pow(t, 2) * point2.lng;
          const y = Math.pow(1 - t, 2) * point1.lat + 2 * (1 - t) * t * controlPoint.lat + Math.pow(t, 2) * point2.lat;
          points.push(new BMap.Point(x, y));
        }
        return points;
      },
    /** 绘制轨迹 */
    async drawTrack() {
@@ -315,7 +417,7 @@
      );
      const currentSegment = this.gpsList.slice(startIndex, endIndex);
      //先获得所有坐标数组
      // 获取所有坐标数组
      const originPoints = currentSegment.map(
        (item) => new BMap.Point(item.longitude, item.latitude)
      );
@@ -323,21 +425,29 @@
      this.gpsList.sort((a, b) => {
        return new Date(b.collectTime) - new Date(a.collectTime);
      }).forEach(item => {
       item.speed=item.speed/1000;
        item.speed = item.speed/1000;
      });
      //批量转换坐标
      var translatePoints = await this.translatePoints(originPoints);
      // 批量转换坐标
      const translatePoints = await this.translatePoints(originPoints);
      // 创建轨迹点数组
      const points = translatePoints;
      translatePoints.forEach((item, index) => {
        const bdPoint = item;
        // 只在起点和终点创建标记
        if (index === 0 || index === translatePoints.length - 1) {
        // 判断起点和终点是否相同
        const isStartPoint = index === 0;
        const isEndPoint = index === translatePoints.length - 1;
        const isStartEndSame = isStartPoint && isEndPoint &&
          translatePoints[0].lng === translatePoints[translatePoints.length - 1].lng &&
          translatePoints[0].lat === translatePoints[translatePoints.length - 1].lat;
        // 只在起点和终点创建标记,且起点和终点不同时才显示起点标记
        if ((isStartPoint && !isStartEndSame) || isEndPoint) {
          let marker;
          let direction=currentSegment[index].direction;
          if (index === 0) {
          if (isStartPoint && !isStartEndSame) {
            // 起点显示"起"字
            const label = new BMap.Label("起", {
              offset: new BMap.Size(0, 0),
@@ -356,7 +466,6 @@
            marker.setLabel(label);
          } else {
            // 终点显示车辆图标
            const myIcon = new BMap.Icon(
              "/car_blue.png",
              new BMap.Size(20, 20),
@@ -384,7 +493,6 @@
            borderRadius: "3px",
          });
          marker.setLabel(label);
          // 获取地址信息
          const geoc = new BMap.Geocoder();
@@ -397,13 +505,14 @@
              addComp.street +
              addComp.streetNumber;
            // 添加信息窗口
            const infoWindow = new BMap.InfoWindow(
              `时间:${item.collectTime}<br/>速度:${
                item.speed
              }km/h<br/>方向:${item.direction}°<br/>地址:${address}`
            );
            // 添加点击事件监听器
            marker.addEventListener("click", () => {
              // 创建信息窗口
              const infoWindow = new BMap.InfoWindow(
                `车牌号:${currentSegment[index].vehicleNo}<br/>时间:${currentSegment[index].collectTime}<br/>速度:${
                  currentSegment[index].speed
                }km/h<br/>方向:${currentSegment[index].direction}°<br/>地址:${address}`
              );
              this.map.openInfoWindow(infoWindow, bdPoint);
            });
          });
@@ -411,12 +520,6 @@
          this.map.addOverlay(marker);
          this.markers.push(marker);
        }
        // const smoothPoints = [];
        // for (let i = 0; i < points.length - 1; i++) {
        //   const segmentPoints = this.getSmoothPoints(points[i], points[i + 1]);
        //   smoothPoints.push(...segmentPoints);
        // }
        // smoothPoints.push(points[points.length - 1]);
        // 如果是最后一个点,绘制轨迹线
        if (index === currentSegment.length - 1) {
@@ -475,12 +578,25 @@
          });
          marker.setLabel(label);
          
          // 添加信息窗口
          const infoWindow = new BMap.InfoWindow(
            `时间:${row.collectTime}<br/>速度:${row.speed}km/h<br/>方向:${row.direction}°<br/>地址:${row.address}`
          );
          marker.addEventListener("click", () => {
            this.map.openInfoWindow(infoWindow, data.points[0]);
          // 获取地址信息
          const geoc = new BMap.Geocoder();
          geoc.getLocation(data.points[0], (rs) => {
            const addComp = rs.addressComponents;
            const address =
              addComp.province +
              addComp.city +
              addComp.district +
              addComp.street +
              addComp.streetNumber;
            // 添加点击事件监听器
            marker.addEventListener("click", () => {
              // 创建信息窗口
              const infoWindow = new BMap.InfoWindow(
                `车牌号:${row.vehicleNo}<br/>时间:${row.collectTime}<br/>速度:${row.speed}km/h<br/>方向:${row.direction}°<br/>地址:${address}`
              );
              this.map.openInfoWindow(infoWindow, data.points[0]);
            });
          });
          
          // 保存当前标记点引用
@@ -557,6 +673,30 @@
      }
      this.isPlaying = false;
    },
    /** 处理时间范围变化 */
    handleDateRangeChange(val) {
      if (val && val.length === 2) {
        // 格式化时间
        this.dateRange = [
          this.formatDateTime(val[0]),
          this.formatDateTime(val[1])
        ];
        // 检查结束时间是否包含具体时间
        const endDate = new Date(this.dateRange[1]);
        const hasSpecificTime = endDate.getHours() !== 0 || endDate.getMinutes() !== 0;
        if (!hasSpecificTime) {
          // 如果没有具体时间,设置为23:59:59
          endDate.setHours(23, 59, 59);
          this.dateRange[1] = this.parseTime(endDate);
        }
        // 更新查询参数
        this.queryParams.beginTime = this.dateRange[0];
        this.queryParams.endTime = this.dateRange[1];
      }
    },
  },
  beforeDestroy() {
    this.stopPlayback();