开发一个前后端分离的webgis城市共享单车投放管理系统(5)

270 阅读2分钟

文章同步更新于我的个人博客:松果猿的博客,欢迎访问获取更多技术分享。

同时,您也可以关注我的微信公众号:松果猿的代码工坊,获取最新文章推送和编程技巧。

前言

通过前一期我们只实现了单车投放区域的查询和定位,下面我们来实现增设投放区域的功能

流程

新建src/components/RegionDialog.vue,用于显示和收集区域信息的表单:

<template>
  <div class="dialog-container">
    <div class="region-dialog-header">
      <el-icon class="close-icon" @click="handleClose"><Close /></el-icon>
    </div>
    <div class="region-dialog-content">
      <el-form :model="form" label-width="auto" style="max-width: 600px">
        <el-form-item label="区域类型">
          <el-select
            @change="handleGeometryChange"
            placeholder="请选择区域类型"
          >
            <el-option label="圆形" value="Circle" />
            <el-option label="矩形" value="Box" />
            <el-option label="多边形" value="Polygon" />
          </el-select>
        </el-form-item>
        <el-form-item label="区域名称">
          <el-input v-model="form.name" />
        </el-form-item>
        <el-form-item label="区域容量">
          <el-input v-model="form.capacity" />
        </el-form-item>
        <el-form-item label="区域存量">
          <el-input v-model="form.exist" />
        </el-form-item>
        <el-button @click="handleConfirm">确定</el-button>
      </el-form>
    </div>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { Close } from "@element-plus/icons-vue";
const form = ref({
  geometry: "",
  name: "",
  capacity: "",
  exist: "",
});
const handleConfirm = () => {
};
const handleClose = () => {
};
const handleGeometryChange = async (value) => {
};
</script>

<style lang="scss" scoped>
.dialog-container {
  position: absolute;
  width: 300px;
  height: 270px;
  background-color: #fff;
  top: 100px;
  right: 50px;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  padding: 20px;
  .region-dialog-header {
    margin-bottom: 10px;
    text-align: right;
    border-bottom: 1px solid #ebeef5;

    .close-icon {
      cursor: pointer;
      font-size: 20px;
      transition: all 0.3s;

      &:hover {
        color: #409eff;
        transform: rotate(90deg);
      }
    }
  }
}
</style>

store/contentStore.ts中添加状态管理,用于控制对话框的显示和隐藏:

Header.vue中添加必要代码,以便在用户界面中显示和隐藏区域对话框:

Home.vue中添加:

完成效果如下:

新建@/hooks/useRegionDraw.js,填写如下代码,将绘制的图层转为wkt数据:

import { useMapStore } from "@/stores/mapStore";
import { Vector as VectorSource } from "ol/source";
import { Vector as VectorLayer } from "ol/layer";
import { Draw } from "ol/interaction";
import { ref } from "vue";
import { WKT} from "ol/format";
import { Circle} from "ol/geom";
import { fromCircle } from "ol/geom/Polygon";
import { createBox } from "ol/interaction/Draw";

export function useRegionDraw() {
  const mapStore = useMapStore();
  const map = mapStore.map;
  const source = new VectorSource();
  const wktFormat = new WKT();
  const vector = new VectorLayer({
    source: source,
    style: {
      "fill-color": "rgba(255, 255, 255, 0.2)",
      "stroke-color": "#ffcc33",
      "stroke-width": 2,
      "circle-radius": 7,
      "circle-fill-color": "#ffcc33",
    },
  });
  const draw = ref(null);

  const DrawGeometry = (geometry) => {
    if (geometry == "Box") {
      draw.value = new Draw({
        source: source,
        type: "Circle",
        geometryFunction: createBox(),
      });
    } else {
      draw.value = new Draw({
        source: source,
        type: geometry,
      });
    }

    map.addLayer(vector);
    map.addInteraction(draw.value);

    return new Promise((resolve) => {
      draw.value.on("drawend", function (event) {
        map.removeInteraction(draw.value);

        const drawFeature = event.feature;
        let drawGeometry = drawFeature.getGeometry();

        if (drawGeometry instanceof Circle) {
          drawGeometry = fromCircle(drawGeometry);
        }
        const wkt = wktFormat.writeGeometry(drawGeometry,{
          dataProjection: "EPSG:4326",
          featureProjection: "EPSG:3857",
        });
        resolve(wkt);
      });
    });
  };

  const deleteDraw = () => {
    map.removeInteraction(draw.value);
    map.removeLayer(vector);
    source.clear();
  };

  return {
    DrawGeometry,
    deleteDraw,
  };
}

修改RegionDialog.vue,添加必要的hooks函数,:

import { ref } from "vue";
import { useContentStore } from "@/stores/contentStore";
import { useRegionDraw } from "@/hooks/useRegionDraw";
import { Close } from "@element-plus/icons-vue";
import axios from "axios";
const contentStore = useContentStore();
const { DrawGeometry,deleteDraw } = useRegionDraw();
const form = ref({
  geometry: "",
  name: "",
  capacity: "",
  exist: "",
});

const addRegion = async () => {
  try {
    console.log(form.value);
    const res = await axios.post(`${import.meta.env.VITE_API_URL}/regions`,form.value);
    deleteDraw();
	ElMessage.success("添加区域成功");
    form.value = {
      geometry: "",
      name: "",
      capacity: "",
      exist: "",
    };
  } catch (error) {
    console.error("获取区域数据失败:", error);
    ElMessage.error("获取区域数据失败");
  }
};

const handleConfirm = () => {
  addRegion();
};

const handleClose = () => {
  deleteDraw();
  contentStore.toggleRegionDialog(false);
};

const handleGeometryChange = async (value) => {
  try {
    const wkt = await DrawGeometry(value);
    form.value.geometry = wkt; 
  } catch (error) {
    console.error('绘制区域失败:', error);
    ElMessage.error('绘制区域失败');
  }
};

启动后端服务,实现效果如下:

项目地址:github.com/songguo1/Sh…