Skip to content

区域绘制组件

功能介绍

通过远程调用接口获取墙体信息,实现标签交互的协同共享。

注:该功能使用了 elementUI 库,使用前需安装该库,具体方法见 elementUI 安装

不妨通过代码示例在 Vue 中尝试一下:

组件代码示例

默认路径为 components/paintModelSet/index.vue

vue
<template>
  <div>
    <!-- 区域绘制 -->
    <el-card class="box-card">
      <div class="title">区域绘制</div>
      <hr />
      <div class="button subButton" @click="startClick()">开始绘制</div>
      <div class="button subButton" @click="endClick()">停止绘制</div>
      <div class="button mainButton" @click="savePaint()">保存最新绘制</div>
      <div class="button" @click="clearPaint()">清除绘制</div>

      <el-table
        v-loading="loading"
        :data="testData"
        :empty-text="emptyText"
        style="width: 100%; min-width: 270px"
        max-height="300px"
        highlight-current-row
        @row-dblclick="onClickRow"
      >
        <el-table-column
          label="显隐"
          width="50"
          header-align="center"
          align="center"
        >
          <template slot-scope="scope">
            <el-switch
              v-model="scope.row.isShow"
              :active-value="1"
              :nactive-value="0"
              active-color="#13ce66"
              inactive-color="#ff4949"
              @change="handleChange(scope.$index, scope.row)"
            >
            </el-switch>
          </template>
        </el-table-column>

        <el-table-column
          label="绘制名称"
          width="120"
          header-align="center"
          align="center"
        >
          <template slot-scope="scope">
            <div slot="reference" class="name-wrapper">
              <el-tag size="medium">{{ scope.row.name }}</el-tag>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="操作" header-align="center" align="center">
          <template slot-scope="scope">
            <el-button
              size="mini"
              type="danger"
              @click="handleDelete(scope.$index, scope.row)"
              >删除</el-button
            >
          </template>
        </el-table-column>
      </el-table>
    </el-card>
  </div>
</template>

<script type="text/javascript">
import * as Cesium from 'cesium'
import axios from 'axios'
import { GET_GEOPOINT_LIST, INSERT_GEOPOINT, UPDATE_GEOPOINT_ISSHOW, DELETE_GEOPOINT } from '@/apis/geopoint/geopoint'

export default {
  components: {

  },
  data () {
    return {
      modelId: 1,
      loading: false,
      emptyText: "暂无数据",
      mouseAxisList: [],
      testData: [],
      tempData: []
    }
  },

  methods: {

    /**
     * 生成区域绘制
     */
    initPaint () {
      // 暂时关闭定时器
      clearInterval(window.onlineInterval);
      window.onlineInterval = undefined;

      //  获取geopoint
      // TODO: 这里暂时扩大到500,500后的数据如何管理后面处理
      axios.get(GET_GEOPOINT_LIST, {
        params: { modelId: this.modelId, size: 500 }
      })
        .then((res) => {
          // 找出在testData中但不在云端中的数据,进行隐藏
          let badData = this.tempData.filter(item => !res.data.data.records.includes(item))
          badData.forEach((ele) => {
            uniCore.model.getPrimitivesById(ele.id) !== null ? uniCore.model.getPrimitivesById(ele.id).show = false : null;
          })
          this.tempData = res.data.data.records;


          this.testData = res.data.data.records;
          if (this.testData.length === 0) this.emptyText = "暂无数据";
          this.loading = false;

          this.testData.forEach(ele => {
            let res = uniCore.model.getPrimitivesById(ele.id);
            if (res === null) {
              if (ele.isShow) {
                for (let i = 0; i < ele.axisList.length - 1; i++) {
                  // 需要配合后端把数组里的float改为string
                  uniCore.model.paintWall(ele.id, [[parseFloat(ele.axisList[i][0]), parseFloat(ele.axisList[i][1])], [parseFloat(ele.axisList[i + 1][0]), parseFloat(ele.axisList[i + 1][1])]], ele.setLineheight, ele.wallLineheight);
                }
              }

            } else {
              // 这里控制显隐
              res.show = ele.isShow;
            }
          })


        }).then(() => {
          // 重启定时器
          this.$parent.createOnlineInterval();
        })
        .catch(error => {
          console.log(error);
          this.emptyText = "网络错误";
          this.$notify.error({
            title: '错误',
            message: '网络错误,无法更新数据',
          });
          this.loading = false;
        })










      // // TODO: 缓存当前数据,接后端后可考虑删除或修改此处逻辑
      // localStorage.setItem('paintData', JSON.stringify(this.testData));
    },

    onClickRow (val) {

      let axis = val.axisList[Math.round(val.axisList.length / 2)];
      uniCore.position.buildingPosition(uniCore.viewer, [axis[0], axis[1], 760], -20, -90, 1)
    },

    handleChange (index, row) {
      // 这里需要将修改提交到后端再进行更新标签状态,以保证多端协同
      this.loading = true;
      row.isShow = !row.isShow ? 0 : 1;

      axios.post(UPDATE_GEOPOINT_ISSHOW, {
        id: row.id, isShow: row.isShow
      })
        .then((res) => {
          // console.log(res);

          // 更新状态
          this.initPaint();
          this.loading = true;
        })
        .catch(error => {
          console.log(error);
          this.emptyText = "网络错误";
          this.$notify.error({
            title: '错误',
            message: '网络错误,无法更新数据',
          });
          this.loading = false;
        })
    },

    handleDelete (index, row) {
      this.loading = true;
      //  删除label
      axios.post(DELETE_GEOPOINT, {
        "id": row.id
      })
        .then((res) => {
          // console.log(res);
          // 删除绘制
          uniCore.model.delLine(row.id)
          // // 实时删除功能
          this.testData = this.testData.filter(e => e.id !== row.id)
          this.loading = false;

        })
        .catch(error => {
          console.log(error);
          this.$notify.error({
            title: '错误',
            message: '网络错误,无法更新数据',
          });
          this.loading = false;
        })



    },

    startClick () {
      let viewer = uniCore.viewer;
      viewer.scene.globe.depthTestAgainstTerrain = true; // 必须开启深度检测,否则点击的位置可能不在理想位置
      this.handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
      this.handler.setInputAction((event) => {
        const earthPosition = viewer.scene.pickPosition(event.position);
        if (Cesium.defined(earthPosition)) {

          let setLineheight = 60;
          let setWallLineheight = 100;
          let mouseAxis = uniCore.position.cartesian3_2axis(earthPosition)
          this.mouseAxisList.push([mouseAxis[0], mouseAxis[1]])
          try {
            uniCore.model.paintWall("PaintLineCollecion", [this.mouseAxisList[this.mouseAxisList.length - 2], this.mouseAxisList[this.mouseAxisList.length - 1]], setLineheight, setWallLineheight);
          } catch (error) { }

        }
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);



      this.handler.setInputAction((event) => {
        const earthPosition = viewer.scene.pickPosition(event.endPosition);
        if (Cesium.defined(earthPosition)) {

          if (this.mouseAxisList.length >= 1) {
            let setLineheight = 60;
            let setWallLineheight = 20;
            let mouseAxis = uniCore.position.cartesian3_2axis(earthPosition)
            mouseAxis = [mouseAxis[0], mouseAxis[1]]

            uniCore.model.delLine("PaintMoveLineCollecion");
            uniCore.model.paintLine("PaintMoveLineCollecion", [this.mouseAxisList[this.mouseAxisList.length - 1], mouseAxis], setLineheight, "#ffffff");

          }

        }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

      this.handler.setInputAction((event) => {
        this.endClick();
      }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);




      // let that = this.$parent;
      // that.isPaintModel = true;
      // window.uniCore.model.delLine("PaintLineCollecion");

    },
    endClick () {
      // 销毁当前方法点击事件
      if (this.handler) {
        this.handler.destroy();
        this.handler = undefined;
      }
      uniCore.model.delLine("PaintMoveLineCollecion");
    },

    clearPaint () {
      try {

        window.uniCore.model.delLine("PaintLineCollecion");
        window.uniCore.model.delLine("PaintMoveLineCollecion");
        this.mouseAxisList = [];

      } catch (error) {
        console.error(error);
        this.$notify({
          title: '提醒',
          message: '请先开始绘制',
          type: 'warning'
        });
      }
    },

    savePaint () {
      // 暂停绘制
      this.endClick();

      // 创建输入框
      let ret = prompt("请输入绘制内容名称");
      if (ret !== null && ret !== "") {

        let newArr = [];
        this.mouseAxisList.forEach(e => { let arr = []; e.forEach(ele => { arr.push(ele.toString()) }); newArr.push(arr) }); newArr

        // newArr = [['1', '1'], ['2', '2'], ['3', '3'], ['4', '4'], ['5', '5'], ['6', '6']]

        let lineObj = {};
        lineObj.modelId = this.modelId;
        lineObj.name = ret;
        lineObj.axisList = newArr;
        lineObj.setLineheight = 60;
        lineObj.wallLineheight = 100;
        lineObj.color = "#1a1a1a"
        lineObj.isShow = 1;

        this.loading = true;
        //  新增geopoint
        axios.post(INSERT_GEOPOINT, lineObj)
          .then((res) => {
            // console.log(res);
            // 保存后清除当前绘制
            this.clearPaint();
            // 更新状态
            this.initPaint();
            this.loading = true;
          })
          .catch(error => {
            console.log(error);
            this.$notify.error({
              title: '错误',
              message: '网络错误,无法更新数据',
            });
            this.loading = false;
          })

        // // TODO: 缓存当前数据,接后端后可考虑删除
        // localStorage.setItem('paintData', JSON.stringify(this.testData));



        function guid () {
          return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = Math.random() * 16 | 0,
              v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
          });
        }
      }






    }
  },

  mounted () {
    // // TODO:获取缓存数据,接后端后可考虑删除
    // if (localStorage.getItem('paintData') !== null) {
    //   this.testData = JSON.parse(localStorage.getItem('paintData'));
    // }
  }
}
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-card__body {
  padding: 20px 0px 0 0px;
}
::v-deep .box-card {
  position: absolute;
  top: 3%;
  left: 3%;
  width: 300px;
  z-index: 1;
  background: rgb(26 26 26 / 83%);
  border: 1px solid rgba(255, 255, 255, 0.3);
  box-shadow: 0px 24px 54px 0px rgba(35, 41, 50, 0.5);
  border-radius: 15px;
  padding: 0 24px 24px 24px;
  margin-bottom: 12px;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);

  .title {
    font-size: 18px;
    font-weight: bold;
    text-shadow: 1px 1px #000;
    color: #fefeff;
    display: block;
    margin-bottom: 10px;
  }

  hr {
    margin-left: 24px;
    margin-bottom: 10px;
    border: none;
    border-bottom: 1px solid #ffffff1a;
  }

  .el-table {
    border-radius: 15px;
  }

  .button {
    display: inline-flex;
    margin: 5px 10px;
    color: white;
    background: #4d4d4dd1;
    border-radius: 10px;
    padding: 7px 20px;
    cursor: pointer;
    transition: 0.3s;
  }

  .mainButton {
    background: #105bc5;
    font-weight: 700;
    padding: 7px 40px;
  }

  .subButton {
    background: #979797cc;
  }

  .mainButton:hover {
    background: #009fff;
    box-shadow: 0px 0px 54px 0px #009fffa8;
  }

  .subButton:hover {
    background: #d7d7d7cc;
    box-shadow: 0px 0px 54px 0px #d7d7d7a8;
  }
}
</style>

API代码示例(服务器需自行配置):

js
let baseURL = process.env.VUE_APP_DEV_API || ""

export const GET_GEOPOINT_LIST = baseURL + "/bim/geopoints/list"
export const INSERT_GEOPOINT = baseURL + "/bim/geopoints/add"
export const UPDATE_GEOPOINT_ISSHOW = baseURL + "/bim/geopoints/update"
export const DELETE_GEOPOINT = baseURL + "/bim/geopoints/del"

接口配置可参考 示例项目 Apifox 文档 中有关于 geopoints 的接口响应示例。

调用代码示例

vue
<template>
  <div id="unicoreContainer">
    <!-- 区域绘制窗口卡片开始 -->
    <pmSet ref="pmSetId"></pmSet>
    <!-- 区域绘制窗口卡片结束 -->
  </div>
</template>

<script>
import { UniCore } from 'unicore-sdk'
import { config } from 'unicore-sdk/unicore.config'
import 'unicore-sdk/Widgets/widgets.css'
import pmSet from '@/components/paintModelSet/index'; //区域绘制组件


export default {

  components: {
    pmSet
  },
  // 生命周期 - 挂载完成(可以访问DOM元素)
  mounted () {
    this.init();

    // 可以不断调用 initPaint 方法实现实时轮询最新数据,如
    setInterval(() => {
      this.$refs.pmSetId.initPaint();
    }, 1000)
  },

  // 方法集合
  methods: {

    /**
    * 通用图形引擎初始化
    */
    init () {

      // 初始化UniCore

      // 目前采用Cesium的地形&底图数据,这里配置Cesium的token
      let accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxNjEwMzI4My01MjBmLTQzYzktOGZiMS0wMDRhZjE0N2IyMGIiLCJpZCI6MTc1NzkyLCJpYXQiOjE3MTM3NzQ3OTh9.zU-R4MNvHr8rvn1v28PQfDImyutnpPF2lmEgGeSPckQ";
      // 初始化unicore
      let uniCore = new UniCore(config, accessToken);
      uniCore.init("unicoreContainer");
      window.uniCore = uniCore;
      let viewer = uniCore.viewer;

      // 视角初始化
      uniCore.position.buildingPosition(viewer, [113.12380548015745, 28.250758831850005, 700], -20, -45, 1);

    }
  }

}
</script>
<style scoped>
#unicoreContainer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: black;
}
</style>

调用代码示例中的关键代码

js
setInterval(() => {
  this.$refs.pmSetId.initPaint();
}, 1000)