这是项目维护期中需要添加的功能模块:其实可以算是在原有的系统上添加一个小型的管理系统了;这里从文件上传、iframe页面数据访问,数据渲染部分总结一下;

数据渲染

地图区域线条绘制

在编辑的时候从服务端请求到的点坐标集合在地图渲染,和新增共用一个地图; 但是操作逻辑不同,在新增是靠点击事件触发加载(第一次为加载,之后为显示隐藏),编辑需要直接渲染;

$('#historyIllegaleModal').modal('show');

$('#historyIllegaleModal').on('shown.bs.modal', e => {
  if (vm.state === 'Edit') {
    if (!viewer) {
      vm.initWebGl(); //初始加载
    }

    $.extend(vm.commitData, data); // 依据服务端进行数据替换
    vm.initChooseHistoryIllegaleArea(); // 显示 canvas 部分
      
    //加载完成的回调
    viewer.onEndFrame = () => { 
      if (historyIllegaleTableEditState) {
        drawMeshLine(data['XQWZ']); //调用绘制的方法
        historyIllegaleTableEditState = false; 
      }
    }
    historyIllegaleTableEditState = true; //先执行
  }
})

卫片对比区域渲染

在触发卫片对比功能中中依据主页面选中需要渲染的类型,其中绘制行政区划部分(为文字标注和区域划分两个部分);

process

构建标注图层

  let shapeCommonConfig = {
    width: "76px",
    height: "24px",
    padding: "0",
    borderRadius: "2px",
    color: "#ff0000",
    opacity: 1,
    backgroundColor: "none",
    fontWeight: "900",
  };

  let shapeArr = [streetShape, streetShapeRight, communityShape, communityShapeRight, jurisdictionShape, jurisdictionShapeRight];
  shapeArr.forEach((shapeArrItem, index) => {
    shapeArrItem = new CSS2DLabelLayer();  
    Object.assign(shapeArrItem.css2DStyle, shapeCommonConfig);
    index % 2 === 0 ? sceneRight.addCustomLayer(shapeArrItem) : sceneLeft.addCustomLayer(shapeArrItem);
  })

利用数据集合渲染出边界线和其中的文字

  // 边界线 && 文字 添加
  CITY_SHAPE_DATA.forEach((ele, curNum) => {
    // 根据主页 props 确定 那些需要加载;
    let currentState = parent.document.querySelector(`#cityshapes${curNum}`).checked;
    if (!currentState) {
      return;
    }

    let sceneArr = [sceneRight, sceneLeft];
    for (let i = 0; i < sceneArr.length; i++) {
      const item = sceneArr[i];
      var ftLayerPolygon = new tjh.ar.FeatureLayer(ele.url[0], "", false, item.terrainLayers, item.globalOffset);
      ftLayerPolygon.loadAllOnce = true;
      // polygon
      ftLayerPolygon.regulator.isLoadFeature = true;// 是否加载矢量streetSurface
      ftLayerPolygon.regulator.requestNum = 10;// 每帧加载数量
      ftLayerPolygon.regulator.color.set(Number(ele.setColor[0]));// 矢量颜色
      ftLayerPolygon.regulator.opacity = (ele.type === "community") ? 0.5 : 1;// 矢量透明度
      ftLayerPolygon.regulator.size = 3.0;
      // 矢量贴合方式--人工指定高层
      ftLayerPolygon.regulator.fitPattern = tjh.ar.FeatureLayer.FIT_PATTERN.FIT_TERRIAN_LOW;
      ftLayerPolygon.initMaterial();
      item.addFeatureLayer(ftLayerPolygon); //向其中添加边界线

      isRenderLabel = true; //触发封装库中的添加方法

      let textConfig = [
        { type: "street", target: streetShape, target2: streetShapeRight },
        { type: "community", target: communityShape, target2: communityShapeRight },
        { type: "jurisdiction", target: jurisdictionShape, target2: jurisdictionShapeRight }
      ];
      textConfig.forEach(element => {
        if (ele.type === element.type) {
          let { target, target2 } = element;
          for (let num = 0; num < ele.centerPosition.length; num++) {
            const { name, x, y } = ele.centerPosition[num];
            target.addLabel(null, name, new THREE.Vector3(x, y, 50));
            target2.addLabel(null, name, new THREE.Vector3(x, y, 50));
          }
        }
      });
    }
  });

Vue数据响应式更新

由于 Vue 是在初始化实例时,将实例属性执行getter/settter对于已经创建的vue实例不允许动态添加跟级别的相应式属性。因为这样不会触发变化的监控,所以视图的更新无法自动刷新;

使用 Vue.set 或者 vm.$set运行时候进行动态添加;

vm.$set(object,propertyName,value);// 适用于后端数据返回后处理成自己需要的属性集合
//eg: 向每个item添加自定义属性
this.posts =  res.map( item => {
    return { ...item, //somepropertyName }
})

根节点数据初始化的时候全部添加

data() {  // 当变量很多的时候,考虑分组后合并,避免混乱
  const userInfo = { //....
  };
  const config = { //....
  };
  return { ...userInfo, ...config, }
}

文件上传

在使用过程中涉及到 Post 请求用于上传文件,,使用 FormData 进行文件上传;

<!-- type=file 代表上传文件  -->
<!-- accept='' 代表上传文件限制,这里 .xls, .xlsx 代表限定为 excel类型 -->
<input type="file" name=""  id="" accept=".xls, .xlsx" >

FormData 对象用以将数据编译成键值对,以便用 XMLHttpRequest 来发送数据,其中如果表单 enctype 属性设为 multipart/form-data ,则会使用表单的 submit() 方法来发送数据;

var formData = new FormData();

formData.append("name", "tom");
formData.append("num", 123); //数字123会被立即转换成字符串 "123"


// 在jq使用 - 设置不处理数据及内容类型即可
var formData = new FormData(document.querySelector("form"));
formData.append("file", fileInputElement.files[0]);
$.ajax({
  url: "stash.php",
  type: "POST",
  data: formData,
  processData: false,  // 不处理数据
  contentType: false   // 不设置内容类型
  success(res){},
});

在 vue 模板配合 axios 使用如下:

<template>
  <div id="app">
    <input id="fileinput" @change="uploading($event)" type="file" accept="image/*">
    <button @click.prevent="submit()"></button>
  </div>
</template>

<script>
export default {
  name: "#app",
  data() {
    return { file: "", src: "" };
  },
  methods: {
    uploading(event) {
      this.file = event.target.files[0]; //获取文件
      var windowURL = window.URL || window.webkitURL;
      this.file = event.target.files[0];
      this.src = windowURL.createObjectURL(event.target.files[0]);//创建图片文件的url
    },

    submit() {
      let formdata = new FormData();
      formdata.append("file", this.file);
      let config = {//此处必须设置为  multipart/form-data
        headers: {
          "Content-Type": "multipart/form-data" //之前说的以表单传数据的格式来传递fromdata
        }
      };

      this.$http.post("/upload", formData, config)
        .then(res => {
          //do something
        })
        .catch(error => {});
    }
  }
};
</script>

iframe页面数据访问

由于三维地图浏览功能和其他大部分逻辑无关,所以放置在一个新的 iframe 中进行业务逻辑编写; 但是数据获取传入以及调用,需要调用方法和给变量赋值;

<!-- parent.html -->
<div class='iframeContainer'>
  <iframe src='' id='myIframe'>
</div>
//iframe.js
var testValue;  // 因为我们从主页面获取的是 window 对象,所以我们使用 var 声明变量,以便于直接进行获取;

function subFunction(){
  //do something
}


// parent.js
let childWindow = document.querySelector('#myIframe').contentWindows; //获取到子页面的 window 对象
childWindow.testValue = 'changed' ; //修改
childWindow.subFunction(); //执行子页面的方法


// 对于子页面访问父页面则是可以通过`parent`即可访问到;