离线地图实现方案+动态轨迹绘制

发布时间 2023-10-16 17:01:34作者: 你会很厉害的

1.项目简介及技术栈

简介:基于内网离线环境开发
1.考虑到地图问题做了离线地图,使用百度离线API,基于以上底图需要实现点击企业名称后定位到该企业所在位置处。
2.定位过去后通过点击点标记,可展示企业基本信息,通过点击查验按钮,调用腾讯会议API对该企业进行查验呼叫入会
3.企业通过智能眼镜或者单兵设备向web端发送坐标数据,根据数据id跟点坐标去匹配当前企业,在企业周围实现动态轨迹的绘制,实现对查验企业的监管
4.技术栈:SpringBoot+Thymeleaf+Vue+Websocket

2.离线地图下载及部署

2.1 离线地图下载器

网上有很多下载工具,我用到的是一个开源的项目,网盘链接放下面

链接:https://pan.baidu.com/s/1vZwBe2AxoHuVMEqipcN1yA
提取码:47iv

程序账号密码:admin/admin

说明:application.properties文件中的appk需要自己去百度地图申请

2.2 离线地图案例

ps:只需要下载下来瓦片后把瓦片放到tiles文件夹下即可,网盘链接也放上

链接:https://pan.baidu.com/s/1Nli1r8k-1bFZQtpe46peww
提取码:8dxt

文件结构

3.企业点标记及+调用腾讯会议快速入会

3.1 企业点位信息标记

数据目前为写死的假数据前端代码如下

数据

data() {
      return {
        // 线条样式

        polylinePoints:[],
        socket:null,
        // src:"../static/img/map.jpg",
        map: null, // 将地图对象定义为数据属性
        form: {
          name: '',
          region: '',
          date1: '',
          date2: '',
          delivery: false,
          type: [],
          resource: '',
          desc: ''
        },
        formLabelWidth: '120px',
        dialogFormVisible:false,
        // 企业基本信息
        items:[
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312c",companyName:"危化品公司1",info:"世界500强",address:"山东省济南市历下区",lng: 116.404, lat: 39.915},
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312c",companyName:"危化品公司2",info:"世界500强",address:"山东省济南市历下区",lng: 116.484, lat: 39.955},
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312c",companyName:"危化品公司3",info:"世界500强",address:"山东省济南市历下区",lng: 116.354, lat: 39.815},
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312c",companyName:"危化品公司4",info:"世界500强",address:"山东省济南市历下区",lng: 116.304, lat: 39.995},
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312a",companyName:"危化品公司5",info:"世界500强",address:"山东省济南市历下区",lng: 116.204, lat: 39.985},
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312d",companyName:"济南危化品公司6",info:"世界500强",address:"山东省济南市历下区",lng: 117.010213, lat: 36.608779},
        ],
      }
    }

渲染

mounted(){
      var map = new BMap.Map("container");          // 创建地图实例
      this.map=map;
      var point0 = new BMap.Point(117.010213, 36.608768);  // 创建点坐标
      map.centerAndZoom(point0, 18);                 // 初始化地图,设置中心点坐标和地图级别
      map.enableScrollWheelZoom(true);     //开启鼠标滚轮缩放

      // 创建点坐标数组
      var points = this.items;
      // 遍历点坐标数组,创建标记点和文本标注
      for (var i = 0; i < points.length; i++) {
        var point = new BMap.Point(points[i].lng, points[i].lat);

        // 创建点标记
        var marker = new BMap.Marker(point);
        map.addOverlay(marker);

        // 创建信息窗口
        var opts = {
          width: 200,
          height: 100,
          title: points[i].companyName,
          enableMessage: true  // 启用信息窗口发送消息功能
        };
        var contentHTML = '简介:' + points[i].info;

        // 添加按钮元素
        contentHTML += '<div style="text-align: right;">';  // 右对齐
        contentHTML += '<button id="customButton" class="button" >' +
                '<span>我要查验</span>' +
                '</button>';
        contentHTML += '</div>';  // 关闭右对齐

        var infoWindow = new BMap.InfoWindow(contentHTML, opts);

        // 点标记添加点击事件
        marker.addEventListener('click', (function (infoWin, pt, pointInfo) {
          return function () {
            map.openInfoWindow(infoWin, pt); // 开启信息窗口
            // 添加按钮点击事件处理程序
            var button = document.getElementById('customButton');
            button.addEventListener('click', function () {
              // 在这里执行您的事件处理逻辑,pointInfo 包含点位的信息
              var obj = JSON.stringify(pointInfo);
              axios.get('/hg/createMeeting/'+ pointInfo.id+"?companyName="+pointInfo.companyName)
                      .then(response => {
                        // 成功响应后设置 items 的值为获取的数据
                        console.log(response.data)
                        var url = response.data.msg;
                        var link = document.createElement('a');
                        link.href=url
                        link.click();
                        link.remove();
                        // alert('按钮被点击了!点位信息:' + response.data.msg);
                        // this.items = response.data;
                      })
                      .catch(error => {
                        console.error('Axios 请求出错:', error);
                      });

            });
          };
        })(infoWindow, point, points[i]));

        // 创建文本标注对象
        var labelOpts = {
          position: point,
          offset: new BMap.Size(-80, -90)
        };
        var label = new BMap.Label(points[i].companyName, labelOpts);

        // 自定义文本标注样式
        label.setStyle({
          color: 'black',
          borderRadius: '5px',
          borderColor: '#ccc',
          padding: '10px',
          fontSize: '16px',
          height: '50px',
          lineHeight: '30px',
          fontFamily: '微软雅黑',
          maxWidth:"none"
        });
        map.addOverlay(label);
      }
    }

效果图

3.2 点击企业后定位

  1. 点击后定位到该企业的点位上,重新设置中心点

  1. 点击红色标记呼叫腾讯会议

  // 点标记添加点击事件
        marker.addEventListener('click', (function (infoWin, pt, pointInfo) {
          return function () {
            map.openInfoWindow(infoWin, pt); // 开启信息窗口
            // 添加按钮点击事件处理程序
            var button = document.getElementById('customButton');
            button.addEventListener('click', function () {
              // 在这里执行您的事件处理逻辑,pointInfo 包含点位的信息
              var obj = JSON.stringify(pointInfo);
              axios.get('/hg/createMeeting/'+ pointInfo.id+"?companyName="+pointInfo.companyName)
                      .then(response => {
                        // 成功响应后设置 items 的值为获取的数据
                        console.log(response.data)
                        var url = response.data.msg;
                        var link = document.createElement('a');
                        link.href=url
                        link.click();
                        link.remove();
                        // alert('按钮被点击了!点位信息:' + response.data.msg);
                        // this.items = response.data;
                      })
                      .catch(error => {
                        console.error('Axios 请求出错:', error);
                      });

            });
          };
        })(infoWindow, point, points[i]));

3.3 呼叫腾讯会议

腾讯会议用的是企业版,需要自己创建应用,获取相关key,具体方式请自行百度。

后台代码


package com.ruoyi.project.hg.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.project.hg.tencent.domain.MeetingData;
import com.ruoyi.project.hg.tencent.domain.Settings;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

public class TencentUtil {
    private static final Log log = LogFactory.getLog(TencentUtil.class);
    static String HMAC_ALGORITHM = "HmacSHA256";

    public final static String appId="";
    public final static String secretId="";
    public final static String sdkId="";
    public final static String secretKey="";
    public final static String address="https://api.meeting.qq.com";


    static char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};


   public static String bytesToHex(byte[] bytes) {


        char[] buf = new char[bytes.length * 2];
        int index = 0;
        for (byte b : bytes) {
            buf[index++] = HEX_CHAR[b >>> 4 & 0xf];
            buf[index++] = HEX_CHAR[b & 0xf];
        }


        return new String(buf);
    }

    /**
     * 生成签名,开发版本oracle jdk 1.8.0_221
     *
     * @param secretId 邮件下发的secret_id
     * @param secretKey 邮件下发的secret_key
     * @param httpMethod http请求方法 GET/POST/PUT等
     * @param headerNonce X-TC-Nonce请求头,随机数
     * @param headerTimestamp X-TC-Timestamp请求头,当前时间的秒级时间戳
     * @param requestUri 请求uri,eg:/v1/meetings
     * @param requestBody 请求体,没有的设为空串
     * @return 签名,需要设置在请求头X-TC-Signature中
     * @throws NoSuchAlgorithmException e
     * @throws InvalidKeyException e
     */
    static String sign(String secretId, String secretKey, String httpMethod, String headerNonce, String headerTimestamp, String requestUri, String requestBody) throws NoSuchAlgorithmException, InvalidKeyException {


        String tobeSig =
                httpMethod + "\nX-TC-Key=" + secretId + "&X-TC-Nonce=" + headerNonce + "&X-TC-Timestamp=" + headerTimestamp + "\n" + requestUri + "\n" + requestBody;
        Mac mac = Mac.getInstance(HMAC_ALGORITHM);
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), mac.getAlgorithm());
        mac.init(secretKeySpec);
        byte[] hash = mac.doFinal(tobeSig.getBytes(StandardCharsets.UTF_8));
        String hexHash = bytesToHex(hash);
        return new String(Base64.getEncoder().encode(hexHash.getBytes(StandardCharsets.UTF_8)));
    }
    /**
     * 获取公共请求头
     *
     * @param httpMethod  请求方式:POST|GET
     * @param requestUri  请求uri
     * @param requestBody 请求体 GET方法请求体需传""
     * @return 拼接好的请求头
     */
    public static Map<String, String> getHeader(String httpMethod, String requestUri,
                                                String requestBody) {
        HashMap<String, String> header = new HashMap<>(8);
//      请求随机数
        String headerNonce = String.valueOf(new Random().nextInt(999999));
        //  当前时间的UNIX时间戳
        String headerTimestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String signature = null;
        try {
            signature = sign(secretId, secretKey, httpMethod, headerNonce, headerTimestamp, requestUri,
                    requestBody);
        } catch (Exception e) {
            log.error("签名生成异常", e);
        }

        header.put("Content-Type", "application/json");
        header.put("X-TC-Key", secretId);
        header.put("X-TC-Timestamp", headerTimestamp);
        header.put("X-TC-Nonce", headerNonce);
        header.put("AppId", appId);
        header.put("X-TC-Version", "1.0");
        header.put("X-TC-Signature", signature);
        header.put("SdkId", sdkId);
        header.put("X-TC-Registered", "1");

        return header;
    }


    /**
     * 腾讯会议发送post请求  携带生产签名和公共请求头参数
     *
     * @param address     请求地址
     * @param uri         请求uri生产签名使用
     * @param requestBody 请求参数
     * @return 			请求响应结果
     */
    public static String sendRequest(String method,String address, String uri, String requestBody) {

        if (method.equals("GET"))requestBody="";
        //生成公共请求头参数和签名
        Map<String, String> headerMap = getHeader(method, uri, requestBody);
        String jsonStr = "";
        StringBuilder sub = new StringBuilder();
        try {
            URL url = new URL(address+uri);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // 设置连接请求属性post
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setRequestMethod(method);
//            设置请求头
            for (Map.Entry<String, String> header : headerMap.entrySet()) {
                conn.setRequestProperty(header.getKey(), header.getValue());
            }

            DataOutputStream out =new DataOutputStream(conn.getOutputStream());
            out.write(requestBody.getBytes());
            out.flush();
            out.close();
            // 定义BufferedReader输入流来读取URL的响应
            int code = conn.getResponseCode();
            System.out.print("====="+conn.getResponseMessage());
            if (HttpURLConnection.HTTP_OK == code) {
                BufferedReader in = new BufferedReader(new InputStreamReader(
                        conn.getInputStream(), "UTF-8"));
                String line;
                while ((line = in.readLine()) != null) {
                    sub.append(line);
                }
                in.close();
            }
            return sub.toString();

//            log.info("腾讯会议httpPost请求相应信息{}", jsonStr);
        } catch (IOException e) {
            log.info("腾讯会议http"+method+"发送异常", e);
        }
        return jsonStr;
    }


    /**
     * 创建会议
     */
    public static String creatMeeting(String userId,String companyName) throws JsonProcessingException {
        String[] invitees = userId.split(",");
//        String string = Arrays.toString(invitees);
//        开始时间
        long startTime = System.currentTimeMillis();
        // 获取当前时间
        Calendar calendar = Calendar.getInstance();
        Calendar calendar2 = Calendar.getInstance();
        // 增加30分钟
        calendar.add(Calendar.MINUTE, 30);
        calendar2.add(Calendar.MINUTE, 3);
        // 获取增加后的时间戳
        long endTime = calendar.getTimeInMillis();
        startTime = calendar2.getTimeInMillis();
        long endTimeInSeconds = endTime / 1000;
        long startTimeInSeconds = startTime / 1000;
        String st = String.valueOf(startTimeInSeconds);
        String et = String.valueOf(endTimeInSeconds);
        String data="{\n" +
                "\t\"userid\": \"9d2fbf1c-4376-838a-4d06-ada53d20915b\",\n" +
                "\t\"instanceid\": 1,\n" +
                "\t\"subject\": \"测试demo\",\n" +
                "\t\"type\": 0,\n" +
                "\t\"invitees\": [\"51c89d4f-6623-9d27-43f4-516a0fdc312c\"],\n" +
                "\t\"start_time\": \"1696823400\",\n" +
                "\t\"end_time\": \"1696824000\",\n" +
                "\t\"settings\": {\n" +
                "\t\t\"mute_enable_join\": true,\n" +
                "\t\t\"allow_unmute_self\": true,\n" +
                "\t\t\"play_ivr_on_leave\": false,\n" +
                "\t\t\"play_ivr_on_join\": false,\n" +
                "\t\t\"allow_in_before_host\": true,\n" +
                "\t\t\"auto_in_waiting_room\": false,\n" +
                "\t\t\"allow_screen_shared_watermark\": false,\n" +
                "\t\t\"only_enterprise_user_allowed\": false\n" +
                "\t\t\n" +
                "\t},\n" +
                "\t\"meeting_type\": 0,\n" +
                "\t\"enable_live\": false\n" +
                "}";
        Settings settings=new Settings();
        settings.setMute_enable_join(true);
        settings.setAllow_in_before_host(true);
        settings.setAllow_screen_shared_watermark(false);
        settings.setAllow_unmute_self(true);
        settings.setPlay_ivr_on_join(false);
        settings.setPlay_ivr_on_leave(false);
        settings.setAuto_in_waiting_room(false);
        settings.setOnly_enterprise_user_allowed(false);
        settings.setAuto_record_type("local");
        MeetingData meetingData=new MeetingData();
//        发起人的id
        meetingData.setUserid("9d2fbf1c-4376-838a-4d06-ada53d20915b");
        meetingData.setInstanceid(1);
        meetingData.setSubject("已发起对"+companyName+"的会议~");
        meetingData.setType(0);
        meetingData.setInvitees(invitees);
        meetingData.setStart_time(st);
        meetingData.setEnd_time(et);
        meetingData.setSettings(settings);
        meetingData.setMeeting_type(0);
        meetingData.setEnable_live(false);
        // 创建ObjectMapper对象
        ObjectMapper objectMapper = new ObjectMapper();

        // 使用ObjectMapper将Java对象转换为JSON字符串
        String json = objectMapper.writeValueAsString(meetingData);
//        创建会议室
        String s = sendRequest("POST",address, "/v1/meetings", json);
//        String s2 = sendRequest("POST",address, "/v1/meetings/4583169091896823219/invitees", data2);
        JSONObject jsonObject = JSONObject.parseObject(s);
        JSONArray meetingInfoList = jsonObject.getJSONArray("meeting_info_list");
        Map<String, Object> objectMap = (Map<String, Object>) meetingInfoList.get(0);
        String string1 = (String) objectMap.get("join_url");
//        System.out.println(jsonObject);
        return string1;
    }

}

返回结果

ps 通过join_url可调起腾讯会议并入会

{
    "meeting_number": 1,
    "meeting_info_list":[
    {
        "subject": "tester's meeting",
        "meeting_id": "433471464134410364",
        "meeting_code": "637228110",
        "password": "1111",
        "host_key":"168168",
        "start_time" : "1572172200",
        "end_time" : "1572175800",
        "hosts":["test1"],
        "participants":[],
        "join_url": "https://wemeet.qq.com/w/5oxCqAc",
        "settings":{
            "mute_enable_type_join": 2
         },
        "enable_live":true,
        "live_config":{
                "live_addr":"https://meeting.tencent.com/l/ehewFSfengBPae"
            }
    }
    ]
}

4.实时轨迹绘制

ps:主要是用到了websocket实现实时监听绘制,关于websocket可参考我的另一篇文章 https://www.cnblogs.com/sy2022/p/17611638.html

4.1 注册websocket

const socketUrl = "ws://localhost:2000/websocket/admin";
        console.log(socketUrl);
        if(this.socket!=null){
          this.socket.close();
          this.socket=null;
        }
        this.socket = new WebSocket(socketUrl);
        //打开事件
        this.socket.onopen = () =>  {
          console.log("websocket已打开");
          this.open2();
        };

4.1 接收后台传过来的点位数据,并完成轨迹绘制。

    this.socket.onmessage = (event) => {
          const receivedData = JSON.parse(event.data);
          // 获取数据中的id字段
          const id = receivedData.id;
          // 检查是否已经存在具有相同id的轨迹数组
          if (!idToPolylinesMap[id]) {
            idToPolylinesMap[id] = [];
          }
          console.log("收到WebSocket消息:", receivedData);
          idToPolylinesMap[id].push(new BMap.Point(receivedData.longitude, receivedData.latitude));
          // 创建线条对象
          var polyline = new BMap.Polyline(idToPolylinesMap[id], polylineOptions);
//      将线条添加到地图
          this.map.addOverlay(polyline);
          // 此时,receivedData 是您从后端发送的 map 对象的 JavaScript 表示
          // 您可以在这里处理它,访问其中的属性和值
        };

4.2 开启关闭轨迹监听

开启

  // 开启绘制点位信息
      beginFlow(){
        var self = this;
        const socketUrl = "ws://localhost:2000/websocket/admin";
        console.log(socketUrl);
        if(this.socket!=null){
          this.socket.close();
          this.socket=null;
        }
        this.socket = new WebSocket(socketUrl);
        //打开事件
        this.socket.onopen = () =>  {
          console.log("websocket已打开");
          this.open2();
        };
        this.socket.onmessage = (event) => {
          const receivedData = JSON.parse(event.data);
          // 获取数据中的id字段
          const id = receivedData.id;
          // 检查是否已经存在具有相同id的轨迹数组
          if (!idToPolylinesMap[id]) {
            idToPolylinesMap[id] = [];
          }
          console.log("收到WebSocket消息:", receivedData);
          idToPolylinesMap[id].push(new BMap.Point(receivedData.longitude, receivedData.latitude));
          // 创建线条对象
          var polyline = new BMap.Polyline(idToPolylinesMap[id], polylineOptions);
//      将线条添加到地图
          this.map.addOverlay(polyline);
          // 此时,receivedData 是您从后端发送的 map 对象的 JavaScript 表示
          // 您可以在这里处理它,访问其中的属性和值
        };
        //关闭事件
        this.socket.onclose = function() {
          console.log("websocket已关闭");
        };
        //发生了错误事件
        this.socket.onerror = function() {
          console.log("websocket发生了错误");
        }
      },

关闭

 closFlow(){
        if(this.socket!=null){
          this.socket.close();
          this.socket=null;
        }
        this.open1();
      },

效果如下

5.代码

前端

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!--  <link rel="stylesheet" href="../element-ui/lib/theme-chalk/index.css">-->
  <th:block th:include="include :: header('危化品')" />
  <title>Baidu Map </title>
  <style type="text/css">
    html{height:100%}
    body{height:100%;margin:0px;padding:0px}
    .header{
      text-align: left; /* 水平居中 */
      height: 60px; /* 垂直居中,根据高度调整 */
      line-height: 60px; /* 垂直居中,根据高度调整 */
      background-color: #76a1dc;
      font-weight: bold; /* 加粗字体 */
      color: white; /* 字体颜色白色 */
      font-size: 50px;
      font-family: 楷体, KaiTi, STKaiti, "楷体_GB2312", "楷体_GB18030"; /* 使用楷体字体 */
    }
    .body{
      height: 100%;
    }
    .el-menu{
      height: 100%;
    }
    .el-menu-item{
      width: 100%;
    }
    .button{
      -webkit-font-smoothing: antialiased;
      -webkit-tap-highlight-color: transparent;
      font-family: inherit;
      display: inline-block;
      line-height: 1;
      white-space: nowrap;
      cursor: pointer;
      background: #fff;
      border: 1px solid #dcdfe6;
      -webkit-appearance: none;
      text-align: center;
      box-sizing: border-box;
      outline: none;
      margin: 0;
      transition: .1s;
      font-weight: 500;
      -webkit-user-select: none;
      font-size: 14px;
      color: #fff;
      background-color: #409eff;
      border-color: #409eff;
      border-radius: 20px;
      padding: 12px 23px;
      margin-left: 10px;
    }
    /*#container{height:90%}*/
  .btn{
    width: 30%;
    height: 50px;
    float: right;
    margin-top: 5px;
  }
  </style>
</head>
<body>
<div id="app" class="body">
  <div class="header">危化品企业一览
    <div class="btn">
    <el-button type="primary" plain @click="beginFlow()">开启轨迹监听</el-button>
    <el-button type="danger" plain @click="closFlow()">关闭轨迹监听</el-button>
    </div>
  </div>
  <el-container style="height: 90%; border: 1px solid #eee">
    <el-aside width="250px"  style="height: 100%; background-color: rgb(238, 241, 246)">
      <el-menu :default-openeds="['1', '3']">
        <el-submenu index="1">
          <template slot="title"><i class="el-icon-message"></i>企业信息</template>
          <el-menu-item-group>
            <el-menu-item  class="el-icon-reading" @click="clickmenu(item)" v-for="(item,index) in items" :key="index" :index="index.toString()">{{ item.companyName }}</el-menu-item>
          </el-menu-item-group>
        </el-submenu>
      </el-menu>
    </el-aside>
    <el-main id="container">
    </el-main>
  </el-container>
</div>
</body>
<!--<script src="../js/vue.js"></script>-->
<!--<script src="../element-ui/lib/index.js"></script>-->
<!--<script src="../js/axios-0.18.0.js"></script>-->
<th:block th:include="include :: footer" />
<script type="text/javascript" src=""></script>
<script>
  //    创建点数组,用于构建线条
  var idToPolylinesMap = {};
  var polylinePoints = [];
  var  polylineOptions={
            strokeColor: "#ef4343", // 线条颜色
            strokeWeight: 3,        // 线条宽度
            strokeOpacity: 0.8      // 线条透明度
          };
  new Vue({
    el: "#app",
    methods:{
      open1() {
        this.$message({
          message: '轨迹监听已关闭',
          type: 'warning'
        });
      },
      open2() {
        this.$message({
          message: '轨迹监听已打开',
          type: 'success'
        });
      },
      closFlow(){
        if(this.socket!=null){
          this.socket.close();
          this.socket=null;
        }
        this.open1();
      },
      // 开启绘制点位信息
      beginFlow(){
        var self = this;
        const socketUrl = "ws://localhost:2000/websocket/admin";
        console.log(socketUrl);
        if(this.socket!=null){
          this.socket.close();
          this.socket=null;
        }
        this.socket = new WebSocket(socketUrl);
        //打开事件
        this.socket.onopen = () =>  {
          console.log("websocket已打开");
          this.open2();
        };
        this.socket.onmessage = (event) => {
          const receivedData = JSON.parse(event.data);
          // 获取数据中的id字段
          const id = receivedData.id;
          // 检查是否已经存在具有相同id的轨迹数组
          if (!idToPolylinesMap[id]) {
            idToPolylinesMap[id] = [];
          }
          console.log("收到WebSocket消息:", receivedData);
          idToPolylinesMap[id].push(new BMap.Point(receivedData.longitude, receivedData.latitude));
          // 创建线条对象
          var polyline = new BMap.Polyline(idToPolylinesMap[id], polylineOptions);
//      将线条添加到地图
          this.map.addOverlay(polyline);
          // 此时,receivedData 是您从后端发送的 map 对象的 JavaScript 表示
          // 您可以在这里处理它,访问其中的属性和值
        };
        //关闭事件
        this.socket.onclose = function() {
          console.log("websocket已关闭");
        };
        //发生了错误事件
        this.socket.onerror = function() {
          console.log("websocket发生了错误");
        }
      },
      clickmenu(item){
        var point0 = new BMap.Point(item.lng, item.lat);  // 创建点坐标
        debugger
        this.map.centerAndZoom(point0, 18);
      }
    },
    data() {
      return {
        // 线条样式

        polylinePoints:[],
        socket:null,
        // src:"../static/img/map.jpg",
        map: null, // 将地图对象定义为数据属性
        form: {
          name: '',
          region: '',
          date1: '',
          date2: '',
          delivery: false,
          type: [],
          resource: '',
          desc: ''
        },
        formLabelWidth: '120px',
        dialogFormVisible:false,
        // 企业基本信息
        items:[
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312c",companyName:"危化品公司1",info:"世界500强",address:"山东省济南市历下区",lng: 116.404, lat: 39.915},
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312c",companyName:"危化品公司2",info:"世界500强",address:"山东省济南市历下区",lng: 116.484, lat: 39.955},
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312c",companyName:"危化品公司3",info:"世界500强",address:"山东省济南市历下区",lng: 116.354, lat: 39.815},
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312c",companyName:"危化品公司4",info:"世界500强",address:"山东省济南市历下区",lng: 116.304, lat: 39.995},
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312a",companyName:"危化品公司5",info:"世界500强",address:"山东省济南市历下区",lng: 116.204, lat: 39.985},
          {id:"51c89d4f-6623-9d27-43f4-516a0fdc312d",companyName:"济南危化品公司6",info:"世界500强",address:"山东省济南市历下区",lng: 117.010213, lat: 36.608779},
        ],
      }
    },
    beforeMount(){
      // 在狗子函数出发之前执行,可以获取items中的数据
      // 在组件挂载之前执行 Axios 请求获取数据
      // axios.get('your_backend_api_url')
      //         .then(response => {
      //           // 成功响应后设置 items 的值为获取的数据
      //           this.items = response.data;
      //         })
      //         .catch(error => {
      //           console.error('Axios 请求出错:', error);
      //         });
    },
    created(){
    },
    mounted(){
      var map = new BMap.Map("container");          // 创建地图实例
      this.map=map;
      var point0 = new BMap.Point(117.010213, 36.608768);  // 创建点坐标
      map.centerAndZoom(point0, 18);                 // 初始化地图,设置中心点坐标和地图级别
      map.enableScrollWheelZoom(true);     //开启鼠标滚轮缩放

      // 创建点坐标数组
      var points = this.items;
      // 遍历点坐标数组,创建标记点和文本标注
      for (var i = 0; i < points.length; i++) {
        var point = new BMap.Point(points[i].lng, points[i].lat);

        // 创建点标记
        var marker = new BMap.Marker(point);
        map.addOverlay(marker);

        // 创建信息窗口
        var opts = {
          width: 200,
          height: 100,
          title: points[i].companyName,
          enableMessage: true  // 启用信息窗口发送消息功能
        };
        var contentHTML = '简介:' + points[i].info;

        // 添加按钮元素
        contentHTML += '<div style="text-align: right;">';  // 右对齐
        contentHTML += '<button id="customButton" class="button" >' +
                '<span>我要查验</span>' +
                '</button>';
        contentHTML += '</div>';  // 关闭右对齐

        var infoWindow = new BMap.InfoWindow(contentHTML, opts);

        // 点标记添加点击事件
        marker.addEventListener('click', (function (infoWin, pt, pointInfo) {
          return function () {
            map.openInfoWindow(infoWin, pt); // 开启信息窗口
            // 添加按钮点击事件处理程序
            var button = document.getElementById('customButton');
            button.addEventListener('click', function () {
              // 在这里执行您的事件处理逻辑,pointInfo 包含点位的信息
              var obj = JSON.stringify(pointInfo);
              axios.get('/hg/createMeeting/'+ pointInfo.id+"?companyName="+pointInfo.companyName)
                      .then(response => {
                        // 成功响应后设置 items 的值为获取的数据
                        console.log(response.data)
                        var url = response.data.msg;
                        var link = document.createElement('a');
                        link.href=url
                        link.click();
                        link.remove();
                        // alert('按钮被点击了!点位信息:' + response.data.msg);
                        // this.items = response.data;
                      })
                      .catch(error => {
                        console.error('Axios 请求出错:', error);
                      });

            });
          };
        })(infoWindow, point, points[i]));

        // 创建文本标注对象
        var labelOpts = {
          position: point,
          offset: new BMap.Size(-80, -90)
        };
        var label = new BMap.Label(points[i].companyName, labelOpts);

        // 自定义文本标注样式
        label.setStyle({
          color: 'black',
          borderRadius: '5px',
          borderColor: '#ccc',
          padding: '10px',
          fontSize: '16px',
          height: '50px',
          lineHeight: '30px',
          fontFamily: '微软雅黑',
          maxWidth:"none"
        });
        map.addOverlay(label);
      }
    }

  })

</script>
</html>