SpringBoot+Vue+Openlayers实现地图上新增和编辑坐标并保存提交

场景

若依前后端分离版手把手教你本地搭建环境并运行项目:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662

Vue中使用Openlayers加载Geoserver发布的ImageWMS:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/115867709

在上面搭建起来Vue+SpringBoot的前后端分离的项目的基础上,并且加载显示地图之后。

要实现在地图上显示所有摄像头的位置,并且可以新增时在地图上选择坐标,以及修改时可以在地图上回显坐标。

实现效果如下

新增摄像头

修改摄像头

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。

实现

1、mysql设计数据库如下

建表语句

DROP TABLE IF EXISTS `bus_surveillance_video`;
CREATE TABLE `bus_surveillance_video`  (
  `id` int(0) NOT NULL AUTO_INCREMENT COMMENT '序号',
  `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '摄像头IP',
  `port` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '摄像头端口号',
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '登录账号',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '登录密码',
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '名称',
  `area_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '区域位置',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

2、然后使用代码生成工具,生成相应的各层代码。

数据库设计主要使用area_name用来存取坐标,但是坐标是分x,y的,所以在实体类中再多加两个属性x和y,在新增和编辑的接口中进行

坐标的转换。

实体类代码:

package com.ruoyi.system.domain;

import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;

import java.math.BigDecimal;
import java.util.List;

public class BusSurveillanceVideo extends BaseEntity
{
    private static final long serialVersionUID = 1L;

    /** 序号 */
    private Long id;

    /** 摄像头IP */
    @Excel(name = "摄像头IP")
    private String ip;

    /** 摄像头端口号 */
    @Excel(name = "摄像头端口号")
    private String port;

    /** 登录账号 */
    @Excel(name = "登录账号")
    private String username;

    /** 登录密码 */
    @Excel(name = "登录密码")
    private String password;

    /** 摄像头通道 */
    @Excel(name = "摄像头通道")
    private List channels;

    /** 名称 */
    @Excel(name = "名称")
    private String name;

    /** 位置x坐标 */
    private BigDecimal siteX;

    /** 位置y坐标 */
    private BigDecimal siteY;

    /** 区域名称 */
    @Excel(name = "区域位置")
    private String areaName;

    public BigDecimal getSiteX() {
        return siteX;
    }

    public void setSiteX(BigDecimal siteX) {
        this.siteX = siteX;
    }

    public BigDecimal getSiteY() {
        return siteY;
    }

    public void setSiteY(BigDecimal siteY) {
        this.siteY = siteY;
    }

    public String getAreaName() {
        return areaName;
    }

    public void setAreaName(String areaName) {
        this.areaName = areaName;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setId(Long id)
    {
        this.id = id;
    }

    public Long getId()
    {
        return id;
    }
    public void setIp(String ip)
    {
        this.ip = ip;
    }

    public String getIp()
    {
        return ip;
    }
    public void setPort(String port)
    {
        this.port = port;
    }

    public String getPort()
    {
        return port;
    }
    public void setUsername(String username)
    {
        this.username = username;
    }

    public String getUsername()
    {
        return username;
    }
    public void setPassword(String password)
    {
        this.password = password;
    }

    public String getPassword()
    {
        return password;
    }

    public List getChannels() {
        return channels;
    }

    public void setChannels(List channels) {
        this.channels = channels;
    }

    @Override
    public String toString() {
        return "BusSurveillanceVideo{" +
                "id=" + id +
                ", ip='" + ip + '\'' +
                ", port='" + port + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", channels=" + channels +
                '}';
    }
}

Mapper代码:

package com.ruoyi.system.mapper;

import com.ruoyi.system.domain.BusSurveillanceVideo;

import java.util.List;

public interface BusSurveillanceVideoMapper
{
    /**
     * 查询摄像头参数
     *
     * @param id 摄像头参数ID
     * @return 摄像头参数
     */
    public BusSurveillanceVideo selectBusSurveillanceVideoById(Long id);

    /**
     * 查询摄像头参数列表
     *
     * @param busSurveillanceVideo 摄像头参数
     * @return 摄像头参数集合
     */
    public List<BusSurveillanceVideo> selectBusSurveillanceVideoList(BusSurveillanceVideo busSurveillanceVideo);

    /**
     * 新增摄像头参数
     *
     * @param busSurveillanceVideo 摄像头参数
     * @return 结果
     */
    public int insertBusSurveillanceVideo(BusSurveillanceVideo busSurveillanceVideo);

    /**
     * 修改摄像头参数
     *
     * @param busSurveillanceVideo 摄像头参数
     * @return 结果
     */
    public int updateBusSurveillanceVideo(BusSurveillanceVideo busSurveillanceVideo);

    /**
     * 删除摄像头参数
     *
     * @param id 摄像头参数ID
     * @return 结果
     */
    public int deleteBusSurveillanceVideoById(Long id);

    /**
     * 批量删除摄像头参数
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    public int deleteBusSurveillanceVideoByIds(Long[] ids);
}

mapper.xml代码

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.BusSurveillanceVideoMapper">

    <resultMap type="BusSurveillanceVideo" id="BusSurveillanceVideoResult">
        <result property="id" column="id"/>
        <result property="ip" column="ip"/>
        <result property="port" column="port"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="name" column="name"/>
        <result property="areaName" column="area_name"/>
    </resultMap>

    <sql id="selectBusSurveillanceVideoVo">
        select id, ip, port, username, password, name,area_name
        from bus_surveillance_video
    </sql>

    <select id="selectBusSurveillanceVideoList" parameterType="BusSurveillanceVideo"
            resultMap="BusSurveillanceVideoResult">
        <include refid="selectBusSurveillanceVideoVo"/>
        <where>
            <if test="ip != null  and ip != ''">and ip = #{ip}</if>
            <if test="port != null  and port != ''">and port = #{port}</if>
            <if test="username != null  and username != ''">and username like concat('%', #{username}, '%')</if>
            <if test="password != null  and password != ''">and password = #{password}</if>
            <if test="name != null  and name != ''">and name = #{name}</if>
            <if test="areaName!=null and areaName != ''">and area_name = #{areaName}</if>
        </where>
    </select>

    <select id="selectBusSurveillanceVideoById" parameterType="Long" resultMap="BusSurveillanceVideoResult">
        <include refid="selectBusSurveillanceVideoVo"/>
        where id = #{id}
    </select>

    <insert id="insertBusSurveillanceVideo" parameterType="BusSurveillanceVideo">
        insert into bus_surveillance_video
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="id != null">id,</if>
            <if test="ip != null">ip,</if>
            <if test="port != null">port,</if>
            <if test="username != null">username,</if>
            <if test="password != null">password,</if>
            <if test="name != null">name,</if>
            <if test="areaName !=null ">area_name,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="id != null">#{id},</if>
            <if test="ip != null">#{ip},</if>
            <if test="port != null">#{port},</if>
            <if test="username != null">#{username},</if>
            <if test="password != null">#{password},</if>
            <if test="name != null">#{name},</if>
            <if test="areaName!=null">#{areaName}</if>
        </trim>
    </insert>

    <update id="updateBusSurveillanceVideo" parameterType="BusSurveillanceVideo">
        update bus_surveillance_video
        <trim prefix="SET" suffixOverrides=",">
            <if test="ip != null">ip = #{ip},</if>
            <if test="port != null">port = #{port},</if>
            <if test="username != null">username = #{username},</if>
            <if test="password != null">password = #{password},</if>
            <if test="name != null">name = #{name},</if>
            <if test="areaName!=null">area_name =#{areaName}</if>
        </trim>
        where id = #{id}
    </update>

    <delete id="deleteBusSurveillanceVideoById" parameterType="Long">
        delete
        from bus_surveillance_video
        where id = #{id}
    </delete>

    <delete id="deleteBusSurveillanceVideoByIds" parameterType="String">
        delete from bus_surveillance_video where id in
        <foreach item="id" collection="array" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>

</mapper>

Service接口代码

package com.ruoyi.system.service;

import com.ruoyi.system.domain.BusSurveillanceVideo;

import java.util.List;

public interface IBusSurveillanceVideoService
{
    /**
     * 查询摄像头参数
     *
     * @param id 摄像头参数ID
     * @return 摄像头参数
     */
    public BusSurveillanceVideo selectBusSurveillanceVideoById(Long id);

    /**
     * 查询摄像头参数列表
     *
     * @param busSurveillanceVideo 摄像头参数
     * @return 摄像头参数集合
     */
    public List<BusSurveillanceVideo> selectBusSurveillanceVideoList(BusSurveillanceVideo busSurveillanceVideo);

    /**
     * 新增摄像头参数
     *
     * @param busSurveillanceVideo 摄像头参数
     * @return 结果
     */
    public int insertBusSurveillanceVideo(BusSurveillanceVideo busSurveillanceVideo);

    /**
     * 修改摄像头参数
     *
     * @param busSurveillanceVideo 摄像头参数
     * @return 结果
     */
    public int updateBusSurveillanceVideo(BusSurveillanceVideo busSurveillanceVideo);

    /**
     * 批量删除摄像头参数
     *
     * @param ids 需要删除的摄像头参数ID
     * @return 结果
     */
    public int deleteBusSurveillanceVideoByIds(Long[] ids);

    /**
     * 删除摄像头参数信息
     *
     * @param id 摄像头参数ID
     * @return 结果
     */
    public int deleteBusSurveillanceVideoById(Long id);
}

ServiceImpl代码

package com.ruoyi.system.service.impl;

import com.ruoyi.system.domain.BusSurveillanceVideo;
import com.ruoyi.system.mapper.BusSurveillanceVideoMapper;
import com.ruoyi.system.service.IBusSurveillanceVideoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Service
public class BusSurveillanceVideoServiceImpl implements IBusSurveillanceVideoService {
    @Autowired
    private BusSurveillanceVideoMapper busSurveillanceVideoMapper;

    /**
     * 查询摄像头参数
     *
     * @param id 摄像头参数ID
     * @return 摄像头参数
     */
    @Override
    public BusSurveillanceVideo selectBusSurveillanceVideoById(Long id) {
        return busSurveillanceVideoMapper.selectBusSurveillanceVideoById(id);
    }

    /**
     * 查询摄像头参数列表
     *
     * @param busSurveillanceVideo 摄像头参数
     * @return 摄像头参数
     */
    @Override
    public List<BusSurveillanceVideo> selectBusSurveillanceVideoList(BusSurveillanceVideo busSurveillanceVideo) {
        List<BusSurveillanceVideo> busSurveillanceVideos = busSurveillanceVideoMapper.selectBusSurveillanceVideoList(busSurveillanceVideo);
        busSurveillanceVideos.forEach(videos -> {
            videos.setChannels(new ArrayList());
            if (videos.getAreaName() != null) {
                String[] point = videos.getAreaName().substring(1, videos.getAreaName().length() - 1).split(",");
                if (!point[0].equals("null") && !point[1].equals("null")) {
                    videos.setSiteX(new BigDecimal(point[0]));
                    videos.setSiteY(new BigDecimal(point[1]));
                }
            }
        });
        return busSurveillanceVideos;
    }

    /**
     * 新增摄像头参数
     *
     * @param busSurveillanceVideo 摄像头参数
     * @return 结果
     */
    @Override
    public int insertBusSurveillanceVideo(BusSurveillanceVideo busSurveillanceVideo) {

        String area = "[" + busSurveillanceVideo.getSiteX() + "," + busSurveillanceVideo.getSiteY() + "]";
        busSurveillanceVideo.setAreaName(area);
        return busSurveillanceVideoMapper.insertBusSurveillanceVideo(busSurveillanceVideo);
    }

    /**
     * 修改摄像头参数
     *
     * @param busSurveillanceVideo 摄像头参数
     * @return 结果
     */
    @Override
    public int updateBusSurveillanceVideo(BusSurveillanceVideo busSurveillanceVideo) {
        String area = "[" + busSurveillanceVideo.getSiteX() + "," + busSurveillanceVideo.getSiteY() + "]";
        busSurveillanceVideo.setAreaName(area);
        return busSurveillanceVideoMapper.updateBusSurveillanceVideo(busSurveillanceVideo);
    }

    /**
     * 批量删除摄像头参数
     *
     * @param ids 需要删除的摄像头参数ID
     * @return 结果
     */
    @Override
    public int deleteBusSurveillanceVideoByIds(Long[] ids) {
        return busSurveillanceVideoMapper.deleteBusSurveillanceVideoByIds(ids);
    }

    /**
     * 删除摄像头参数信息
     *
     * @param id 摄像头参数ID
     * @return 结果
     */
    @Override
    public int deleteBusSurveillanceVideoById(Long id) {
        return busSurveillanceVideoMapper.deleteBusSurveillanceVideoById(id);
    }
}

注意在这层实现的坐标x和坐标y的拆分显示给前端以及组合存储更新到数据库。

Controller层代码

package com.ruoyi.web.controller.system;

import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.BusSurveillanceVideo;
import com.ruoyi.system.service.IBusSurveillanceVideoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/surveillanceVideo")
public class BusSurveillanceVideoController extends BaseController
{
    @Autowired
    private IBusSurveillanceVideoService busSurveillanceVideoService;

    /**
     * 查询摄像头参数列表
     */

    @GetMapping("/getVideoInfo")
    public TableDataInfo list(BusSurveillanceVideo busSurveillanceVideo)
    {
        startPage();
        List<BusSurveillanceVideo> list = busSurveillanceVideoService.selectBusSurveillanceVideoList(busSurveillanceVideo);
        return getDataTable(list);
    }


    /**
     * 获取摄像头参数详细信息
     */

    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id)
    {
        return AjaxResult.success(busSurveillanceVideoService.selectBusSurveillanceVideoById(id));
    }

    /**
     * 新增摄像头参数
     */

    @Log(title = "摄像头参数", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(BusSurveillanceVideo busSurveillanceVideo)
    {
        return toAjax(busSurveillanceVideoService.insertBusSurveillanceVideo(busSurveillanceVideo));
    }

    /**
     * 修改摄像头参数
     */

    @Log(title = "摄像头参数", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody BusSurveillanceVideo busSurveillanceVideo)
    {
        return toAjax(busSurveillanceVideoService.updateBusSurveillanceVideo(busSurveillanceVideo));
    }

    /**
     * 删除摄像头参数
     */

    @Log(title = "摄像头参数", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids)
    {
        return toAjax(busSurveillanceVideoService.deleteBusSurveillanceVideoByIds(ids));
    }
}

3、前端代码结构

前端代码实现结构

其中Index.vue是主页面,实现如下显示所有摄像头数据。

lampMap.vue是地图显示摄像头页面实现如下效果

videoMap.vue是点击新增或者编辑时点击选择摄像头时的弹窗

4、index.vue代码

<template>
  <div class="app-container">
    <el-form
      :model="queryParams"
      ref="queryForm"
      :inline="true"
      v-show="showSearch"
      label-width="68px"
    >
      <el-form-item label="名称" prop="name">
        <el-input
          v-model="queryParams.name"
          placeholder="请输入名称"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="摄像头ip" prop="ip">
        <el-input
          v-model="queryParams.ip"
          placeholder="请输入摄像头ip"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>

      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"
          >搜索</el-button
        >
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          plain
          icon="el-icon-plus"
          size="mini"
          @click="handleAdd"
          v-hasPermi="['runcontrolmange:surveillanceVideo:add']"
          >新增</el-button
        >
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="success"
          plain
          icon="el-icon-edit"
          size="mini"
          :disabled="single"
          @click="handleUpdate"
          v-hasPermi="['runcontrolmange:surveillanceVideo:edit']"
          >修改</el-button
        >
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          :disabled="multiple"
          @click="handleDelete"
          v-hasPermi="['runcontrolmange:surveillanceVideo:remove']"
          >删除</el-button
        >
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="primary"
          icon="el-icon-map-location"
          size="mini"
          @click="showLampPointData"
          >地图
        </el-button>
      </el-col>
    </el-row>

    <el-table
      v-loading="loading"
      :data="videoList"
      @selection-change="handleSelectionChange"
    >
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column show-overflow-tooltip label="名称" align="center" prop="name" />
      <el-table-column show-overflow-tooltip label="摄像头ip" align="center" prop="ip" />
      <el-table-column
        show-overflow-tooltip
        label="账号"
        align="center"
        prop="username"
      />
      <el-table-column
        show-overflow-tooltip
        label="密码"
        align="center"
        prop="password"
      />
      <el-table-column show-overflow-tooltip label="端口" align="center" prop="port" />
      <el-table-column
        show-overflow-tooltip
        label="摄像头坐标"
        align="center"
        prop="areaName"
      />
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['runcontrolmange:surveillanceVideo:edit']"
            >修改</el-button
          >
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['runcontrolmange:surveillanceVideo:remove']"
            >删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>

    <pagination
      v-show="total > 0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 添加或修改识别用户对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="名称" prop="name">
          <el-input v-model="form.name" placeholder="请输入名称" />
        </el-form-item>
        <el-form-item label="摄像头ip" prop="ip">
          <el-input v-model="form.ip" placeholder="请输入摄像头ip" />
        </el-form-item>
        <el-form-item label="账号" prop="username">
          <el-input v-model="form.username" placeholder="请输入账号" />
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input v-model="form.password" placeholder="请输入密码" />
        </el-form-item>
        <el-form-item label="端口" prop="port">
          <el-input v-model="form.port" placeholder="请输入端口" />
        </el-form-item>
        <el-form-item label="摄像头位置" prop="coordinate">
          <el-input v-model="lightPoint" placeholder="点击新增/更改摄像头坐标" @focus="onMap" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
    <!-- 地图 -->
    <videoMap ref="videoMap" @childEvent="parentEvent"></videoMap>
    <lampMap ref="lampMap"></lampMap>
  </div>
</template>

<script>
import {
  VideoInfoList,
  addVideo,
  updateVideo,
  delVideo,
} from "@/api/system/carVideo";
import videoMap from "./component/videoMap";
import lampMap from "./component/lampMap";

export default {
  name: "OpUser",
  components: {
    videoMap,
    lampMap,
  },
  data() {
    return {
      // 遮罩层
      loading: false,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 识别用户表格数据
      videoList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
      },
      // 表单参数
      form: {
        coordinate: "",
      },
      // 表单校验
      rules: {},
      dialogVisible: false,
      // 摄像头的参数
      lightPoint: [],
    };
  },
  created() {
    this.getList();
  },
  mounted() {},
  destroyed() {},
  methods: {
    parentEvent(data) {
      this.form.siteX = data[0];
      this.form.siteY = data[1];
      this.lightPoint = `${this.form.siteX},${this.form.siteY}`;
      this.form.coordinate = this.lightPoint;
    },
    onMap() {
      this.$refs.videoMap.dialogVisible = true;
      this.$refs.videoMap.init();
      this.$refs.videoMap.coordinate = this.form.coordinate;
      this.$refs.videoMap.drawPoint([this.form.siteX, this.form.siteY]);
    },
    // 所有摄像头地图
    showLampPointData(data) {
      this.$refs.lampMap.dialogVisible = true;
      this.$refs.lampMap.init();
      this.$refs.lampMap.drawPoint(this.videoList);
    },
    getList() {
      VideoInfoList(this.queryParams).then((res) => {
        this.videoList = res.rows;
        this.total = res.total;
        this.loading = false;
      });
    },
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },
    handleAdd() {
      this.reset();
      this.open = true;
      this.lightPoint = "";
      this.title = "新增摄像头";
    },
    handleUpdate(row) {
      this.reset();
      this.title = "修改";
      this.open = true;
      let id;
      if(row.id){
        id = row.id
        this.videoList.forEach((item) => {
          if (item.id == id) {
            this.form = JSON.parse(JSON.stringify(item));
            this.lightPoint = item.areaName;
          }
        });
      }else{
        id = this.ids;
          this.videoList.forEach((item) => {
          if (item.id == id[0]) {
            this.form = JSON.parse(JSON.stringify(item));
            this.lightPoint = item.areaName;
          }
        });
      }
    },
    /** 提交按钮 */
    submitForm() {
      this.$refs["form"].validate((valid) => {
        if (valid) {
          if (this.form.id != null) {
            updateVideo(this.form).then((response) => {
              this.msgSuccess("修改成功");
              this.open = false;
              this.getList();
            });
          } else {
            addVideo(this.form).then((response) => {
              this.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            });
          }
        }
      });
    },
    handleDelete(row) {
      const ids = row.id || this.ids;
      this.$confirm("是否确认删除该条数据项?", "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(function () {
          return delVideo(ids);
        })
        .then(() => {
          this.getList();
          this.msgSuccess("删除成功");
        });
    },
    cancel() {
      this.open = false;
      this.reset();
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.openVideoData = [];
      this.openVideoData = selection;
      this.ids = selection.map((item) => item.id);
      this.single = selection.length !== 1;
      this.multiple = !selection.length;
    },
    reset() {
      this.form = {
        ip: null,
        name: null,
        username: null,
        password: null,
        port: null,
        coordinate:''
      };
      this.resetForm("form");
    },
  },
};
</script>
<style scoped>

</style>

其中调用增删改查后台接口的代码

​
import request from '@/utils/request'

export function VideoInfoList(query) {
    return request({
        url: '/surveillanceVideo/getVideoInfo',
        method: 'get',
        params: query
    })
}
// 新增

export function addVideo(query) {
    return request({
        url: '/surveillanceVideo',
        method: 'post',
        params: query
    })
}


// 修改
export function updateVideo(query) {
    return request({
        url: '/surveillanceVideo',
        method: 'put',
        data: query
    })
}

// 删除
export function delVideo(id) {
    return request({
        url: '/surveillanceVideo/' + id,
        method: 'delete'
    })
}

​

5、index.vue关键代码

点击地图按钮

        <el-button
          type="primary"
          icon="el-icon-map-location"
          size="mini"
          @click="showLampPointData"
          >地图
        </el-button>

显示所有摄像头地图

    // 所有摄像头地图
    showLampPointData(data) {
      this.$refs.lampMap.dialogVisible = true;
      this.$refs.lampMap.init();
      this.$refs.lampMap.drawPoint(this.videoList);
    },

首先显示lampMap组件的弹窗,其次初始化地图,然后调用地图画点的方法,并传递所有摄像头的坐标的list。

其中所有摄像头坐标的list是页面初始化请求接口获取的。

  created() {
    this.getList();
  },

然后是新增和编辑页面点击摄像头位置时

        <el-form-item label="摄像头位置" prop="coordinate">
          <el-input v-model="lightPoint" placeholder="点击新增/更改摄像头坐标" @focus="onMap" />
        </el-form-item>

注意这里的onMap方法

    onMap() {
      this.$refs.videoMap.dialogVisible = true;
      this.$refs.videoMap.init();
      this.$refs.videoMap.coordinate = this.form.coordinate;
      this.$refs.videoMap.drawPoint([this.form.siteX, this.form.siteY]);
    },

首先是显示页面,其次是地图初始化,然后是获取form对象的coordinate属性并传递给videoMap子组件的coordinate属性

用来在地图上进行回显摄像头位置。最后执行画点的方法,参数为form表单的x坐标与y坐标。

注意这里的表单中的prop是是coordinate,但是输入框的v-model却是lightPoint。

这样做才能实现弹窗之后回传的属性为x和y,然后表单需要提交给后台的是拼接后的单个字符串的格式。

6.lampMap代码

​
<template>
  <div class="cont">
    <el-dialog
      title="摄像头"
      :visible.sync="dialogVisible"
      width="70%"
      :modal="false"
      v-loading="loading"
    >
      <div id="xhdMap" v-if="dialogVisible"></div>
    </el-dialog>
  </div>
</template>

<script>
import Map from "ol/Map";
import View from "ol/View";
import Feature from "ol/Feature";
import { Point, LineString } from "ol/geom";
import { Image as ImageLayer, Vector as VectorLayer, Tile as TileLayer } from "ol/layer";
import { Circle, Fill, Icon, Stroke, Style, Text } from "ol/style";
import { Cluster, OSM, Vector as VectorSource, XYZ, ImageWMS, TileWMS } from "ol/source";

export default {
  data() {
    return {
      dialogVisible: false,
      loading: false,
      layers: null,
      map: null,
      zoom: null,
      lightLayer: null,
      lightData: [],
    };
  },
  mounted() {},
  beforeDestroy() {},
  methods: {
    // 初始化地图
    init() {
      let self = this;
      this.wsIsRun = true;
      self.$nextTick(() => {
        self.layers = new ImageLayer({
          extent: [911908.3769988124, 110617.87078181792,1596307.9757537232, 420506.5270969288],  // 边界,
          source: new ImageWMS({
            url: "http://127.0.0.1:8000/geoserver/nyc/wms",
            // Layers需要指定要显示的图层名
            params: {
              LAYERS: "nyc:nyc_roads",
              exceptions: "application/vnd.ogc.se_inimage",
              FORMAT: "image/png",
            },
            serverType: "geoserver",
          }),
        });
         this.lightLayer = new VectorLayer({
          source: new VectorSource({ features: [] }),
        });

        // 绘制线的图层
        this.map = new Map({
          layers: [this.layers,this.lightLayer],
          target: "xhdMap",
          view: new View({
            center: [987701, 213364.299370935],
            zoom: 12, // 放大级数
            maxZoom: 20,
            minZoom: 4,
          }),
        });
      });
    },
    // 画点
    drawPoint(data) {
      let url = "";
      this.lightData = data;
      url = "/images/video.png";
      this.$nextTick(() => {
        data.forEach((item, index) => {
          let feature = new Feature({
            // geometry 几何图形
            geometry: new Point([Number(item.siteX), Number(item.siteY)]),
          });
          feature.setId(item.id);
          let style = new Style({
            image: new Icon({
              scale: 0.3,
              src: url,
              anchor: [0.48, 0.52],
            }),
          });
          feature.setStyle(style);
          // getSource() 获得目前事件的事件源
          this.lightLayer.getSource().addFeature(feature);
        });
      });
    },
  },
};
</script>
<style>
#xhdMap {
  width: 100%;
  height: 80vh;
}
.el-dialog__header {
  background-color: #409eff;
}
.el-dialog__title,
.el-dialog__close {
  color: #fff !important;
}
.el-dialog__body {
  padding: 5px;
}
</style>
<style scoped>
.popup {
  position: absolute;
  height: 40%;
  width: 30%;
  right: 10px;
  bottom: 10px;
  z-index: 999;
  color: #fff;
  padding: 0 15px;
  background: url("~@/assets/home/lamp_bg.png") no-repeat center;

  background-size: 100% 100%;
}
.msg_box {
  position: relative;
  top: calc(50% - 124px);
}
.msg_box div {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 4px;
}
.show_label {
  font-size: 16px;
  color: #ffffff;
  font-family: 时尚中黑;
  width: 150px;
}
.popup .msg_box /deep/ .el-input__inner {
  display: inline-block;
  color: #ffffff;
  background: url("~@/assets/home/input_bg.png") no-repeat;
  border: none;
  background-size: 100% 100%;
  width: calc(100% - 5px);
}
.popup .el-select {
  width: calc(100% - 10px);
}
.ol-popup-closer {
  text-decoration: none;
  position: absolute;
  top: -13px;
  right: 9px;
  z-index: 2001;
}

.ol-popup-closer:after {
  content: "✖";
  font-size: 20px;
}
.xhd .title {
  line-height: 40px !important;
  text-indent: 0.6rem !important;
}
.online_state {
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-right: 6px;
  border-radius: 50%;
}
.online_state_red {
  background: red;
}
.online_state_green {
  background: green;
}
</style>

​

7、lampMap关键代码

这里绘制map时分两个图层,一个是显示地图线的图层layers,一个是显示摄像头的图层lightlayers

主要是上面点击地图按钮时将所有的摄像头的坐标数据传递给画摄像头位置的图层的实现

    drawPoint(data) {
      let url = "";
      this.lightData = data;
      url = "/images/video.png";
      this.$nextTick(() => {
        data.forEach((item, index) => {
          let feature = new Feature({
            // geometry 几何图形
            geometry: new Point([Number(item.siteX), Number(item.siteY)]),
          });
          feature.setId(item.id);
          let style = new Style({
            image: new Icon({
              scale: 0.3,
              src: url,
              anchor: [0.48, 0.52],
            }),
          });
          feature.setStyle(style);
          // getSource() 获得目前事件的事件源
          this.lightLayer.getSource().addFeature(feature);
        });
      });
    }

8、videoMap.vue代码

​
<template>
  <div class="cont">
    <el-dialog
      title="选取摄像头位置"
      :visible.sync="dialogVisible"
      width="70%"
      :modal="false"
      v-loading="loading"
      :before-close="handleClose"
    >
      <div id="pMap" v-if="dialogVisible"></div>
      <p class="showPoint">经纬度:{
   
   { coordinate }}</p>
      <div class="dialogfooter">
        <el-button type="primary" size="small" @click="submitForm">确 定</el-button>
        <el-button size="small" @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import Map from "ol/Map";
import View from "ol/View";
import Feature from "ol/Feature";
import { Point, LineString } from "ol/geom";
import { Circle, Fill, Icon, Stroke, Style, Text } from "ol/style";
import { getVectorContext } from "ol/render";
import { easeOut } from "ol/easing";
import { Image as ImageLayer, Vector as VectorLayer, Tile as TileLayer } from "ol/layer";
import { Cluster, OSM, Vector as VectorSource, XYZ, ImageWMS, TileWMS } from "ol/source";

export default {
  data() {
    return {
      dialogVisible: false,
      loading: false,
      layers: null,
      map: null,
      zoom: null,
      lightLayer: null,
      coordinate: null,
    };
  },
  mounted() {
    if (this.coordinate) {
      this.drawPoint(this.coordinate);
    }
  },
  watch: {},
  methods: {
    // 初始化地图
    init() {
      let self = this;
      self.$nextTick(() => {
        self.layers = new ImageLayer({
          extent: [911908.3769988124, 110617.87078181792,1596307.9757537232, 420506.5270969288], // 边界,
          source: new ImageWMS({
            url: "http://127.0.0.1:8000/geoserver/nyc/wms",
            // Layers需要指定要显示的图层名
            params: {
              LAYERS: "nyc:nyc_roads",
              exceptions: "application/vnd.ogc.se_inimage",
              FORMAT: "image/png",
            },
            serverType: "geoserver",
          }),
         
        });
        // 摄像头位置所放的图层
        this.lightLayer = new VectorLayer({
          source: new VectorSource({ features: [] }),
        });
        // 绘制地图线的图层
        this.map = new Map({
          layers: [this.layers,this.lightLayer],
          target: "pMap",
          view: new View({
           //地图中心点
            center: [987777.93778, 213834.81024],
            zoom: 12,
            maxZoom: 20,
            minZoom: 4,
          }),
        });
         this.onPoint();
      });
    },
    drawPoint(data, isTrue) {
      let url = "";
      url = "/images/video.png";
      this.$nextTick(() => {
        if (isTrue) {
          this.removePoint();
        }
        let feature = new Feature({
          // geometry 几何图形
          geometry: new Point([Number(data[0]), Number(data[1])]),
        });
        let style = new Style({
          image: new Icon({
            scale: 0.3,
            src: url,
            anchor: [0.48, 0.52],
          }),
        });
        feature.setStyle(style);
        this.lightLayer.getSource().addFeature(feature);
      });
    },
    removePoint() {
      let self = this;
      let allPointFeatures = self.lightLayer.getSource().getFeatures();
      allPointFeatures.forEach((item) => {
        self.lightLayer.getSource().removeFeature(item);
      });
    },
    onPoint() {
      // 监听singleclick事件
      let _this = this;
      this.map.on("singleclick", function (e) {
        _this.coordinate = e.coordinate;
        debugger
        if (_this.coordinate) {
          _this.drawPoint(_this.coordinate, true);
        }
      });
    },

    handleClose() {
      this.dialogVisible = false;
    },
    submitForm() {
      this.$emit("childEvent", this.coordinate);
      this.dialogVisible = false;
    },
    cancel() {
      this.dialogVisible = false;
    },
  },
};
</script>
<style>
#pMap {
  width: 100%;
  height: 80vh;
}
.el-dialog__header {
  background-color: #409eff;
}
.el-dialog__title,
.el-dialog__close {
  color: #fff !important;
}
.el-dialog__body {
  padding: 5px;
}

.showPoint {
  position: absolute;
  top: 50px;
  color: #070707;
  z-index: 1;
  left: 50px;
}
.dialogfooter {
  position: absolute;
  bottom: 10px;
  right: 10px;
}
</style>

​

9、videoMap.vue关键代码

主要是监听地图的单击事件,在单击事件中获取坐标并赋值在左上角显示,然后再进行绘制摄像头图标图层。

    onPoint() {
      // 监听singleclick事件
      let _this = this;
      this.map.on("singleclick", function (e) {
        _this.coordinate = e.coordinate;
        debugger
        if (_this.coordinate) {
          _this.drawPoint(_this.coordinate, true);
        }
      });
    },

这里的绘制的方法加了一个参数,用来作为页面上是否只留一个图标的限制。

如果传递true则先清理图层在重新绘制,保证只能有一个最新的摄像头位置。

        if (isTrue) {
          this.removePoint();
        }

在removePoint中遍历所有摄像头图层然后删除掉

    removePoint() {
      let self = this;
      let allPointFeatures = self.lightLayer.getSource().getFeatures();
      allPointFeatures.forEach((item) => {
        self.lightLayer.getSource().removeFeature(item);
      });
    },

最后是点击确定时

    submitForm() {
      this.$emit("childEvent", this.coordinate);
      this.dialogVisible = false;
    },

将选取的坐标值传递给父组件。在父组件通过

    parentEvent(data) {
      this.form.siteX = data[0];
      this.form.siteY = data[1];
      this.lightPoint = `${this.form.siteX},${this.form.siteY}`;
      this.form.coordinate = this.lightPoint;
    },

接受传值并分别赋值给输入框要回显的坐标值以及表单要提交绑定的值。

猜你喜欢

转载自blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/121150132