jihongshun
12 小时以前 f03ea598d39abceac4eeb5f3a10b1fe7dd706b2c
src/views/system/shootPoint/components/shootPointDialog.vue
@@ -12,11 +12,16 @@
                <div class="chooseModel">
                  <el-button type="primary" @click='chooseModel'>选择模型</el-button>
                  <div class="modelType">
                    模型名称:模型塔1
                    模型名称:{{ modelName }}
                  </div>
                  <div>
                    模型类型:电塔
                    模型类型:{{ templateType }}
                  </div>
                  <el-form label-width="70px" :model="form">
                    <el-form-item label="模板名称">
                      <el-input v-model="form.templateName"></el-input>
                    </el-form-item>
                  </el-form>
                </div>
                <div class="modelTree">
                  <div class='modelTreeTitle'>巡检点目录</div>
@@ -34,70 +39,28 @@
              </el-card>
          </el-col>
          <el-col :span="20">
              <!-- <el-card class="noScroll"> -->
                  <InitMap v-if="showMap" @mergePoint="mergePoint"></InitMap>
              <!-- </el-card> -->
              <InitMap v-if="showMap" @mergePoint="mergePoint" :towerUrl="towerUrl"></InitMap>
          </el-col>
          <!-- <el-col :span="8">
                <div>
                  <div class="cameraView">
                    <a>相机视角</a>
                  </div>
                  <div class="uavView">
                    <a>无人机视角</a>
                  </div>
                  <div class="operationControl">
                    <a>操控相关(使用以下键控制相机视角)</a>
                    <div>
                      <el-button type="primary">地面点</el-button>
                      <el-button type="primary">空中点</el-button>
                      <el-button type="primary">悬停时间</el-button>
                    </div>
                    <div>
                       <div  class="container">
                          <div class="keypad">
                            <div class="row">
                              <div class="key">Q-上升</div>
                              <div class="key">W-向前</div>
                              <div class="key">E-下降</div>
                            </div>
                            <div class="row">
                              <div class="key">A- 向左</div>
                              <div class="key">S- 向后</div>
                              <div class="key">D- 向右</div>
                            </div>
                          </div>
                          <div class="arrow-pad">
                            <div class="arrow-row">
                              <div class="arrow empty"></div>
                              <div class="arrow">↑ - 向上看</div>
                              <div class="arrow empty"></div>
                            </div>
                            <div class="arrow-row">
                              <div class="arrow">← - 向左转</div>
                              <div class="arrow">↓ - 向下看</div>
                              <div class="arrow">→ - 向右转</div>
                            </div>
                          </div>
                        </div>
                    </div>
                  </div>
                </div>
          </el-col> -->
      </el-row>
      <span slot="footer" class="dialog-footer">
        <el-button @click="closeModal">取 消</el-button>
        <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
        <el-button type="primary" @click="submit()" >确 定</el-button>
      </span>
    </el-dialog>
    <ChooseModelDialog v-if="showModel" @cancel ='cancel' @getRowData="getRowData" ></ChooseModelDialog>
  </div>
</template>
<script>
import { compileToFunctions } from "vue-template-compiler";
import CesiumMap from "../../../../utils/components/cesium-map.vue";
import InitMap from "../../../../utils/components/init-map.vue";
import ChooseModelDialog from './chooseModelDialog.vue';
import { addPoint , getPointInfo} from "@/api/system/template"
let globalon  = 0
let globalat = 0
let towerHeight = 47.47
//塔的朝向算法所用到的 旋转度数
let rotationAngle = Cesium.Math.toRadians(0)
export default{
  name:'shootPointDialog',
   components: {
@@ -105,32 +68,16 @@
    ChooseModelDialog,
    InitMap
  },
  props: {
    templateId: {
      type: String,
      defaule: null
    }
  },
  data(){
    return{
      dialogVisible :true,
      showModel:false,
      //  treeData: [{
      //     id: 1,
      //     label: '一级 1',
      //     children: [{
      //       id: 4,
      //       label: '二级 1-1',
      //     }]
      //   }, {
      //     id: 2,
      //     label: '一级 2',
      //     children: [{
      //       id: 5,
      //       label: '二级 2-1'
      //     }]
      //   }, {
      //     id: 3,
      //     label: '一级 3',
      //     children: [{
      //       id: 7,
      //       label: '二级 3-1'
      //     }]
      //   }],
        treeData:[],
        defaultProps: {
          children: 'children',
@@ -140,10 +87,128 @@
        initialCameraPosition: null,
        initialCameraOrientation: null,
        animationFrameId: null,
        showMap:false
        showMap:false,
        towerUrl:null,
        chooseModelId:null,
        modelName:null,
        templateType:null,
        form:{}
    }
  },
  mounted(){
    console.log(this.templateId)
    if(this.templateId) {
      //预览逻辑
      getPointInfo(this.templateId).then(res=>{
        if(res.code == 200 ) {
          console.log(res.data)
          this.getRowData(res.data)
          let drawArr = this.convertToTree(res.data.ardListWayPointsLS)
          setTimeout(() => {
            //渲染时间问题 加个延时器
            this.drawLines(drawArr)
          }, 500);
        }
      })
    }
  },
  methods:{
    convertToTree(data) {
      return data.map((item, index) => {
        const parentId = 'air_' + index;
        return {
          id: parentId,
          label: item.targetName,
          longitude: item.longitude,
          latitude: item.latitude,
          height: item.altitude,
          children: (item.ardGroundPoint || []).map((g, i) => {
            return {
              id: parentId + '_ground_' + i,
              label: g.targetName,
              longitude: g.longitude,
              latitude: g.latitude,
              height: g.height
            }
          })
        }
      });
    },
    drawLines(treeData) {
      console.log(treeData)
      console.log(window)
      console.log(window.viewerM)
      let viewer = window.viewerM
      // 收集空中点位置
      const airPositions = treeData.map(point => {
        return Cesium.Cartesian3.fromDegrees(
          point.longitude,
          point.latitude,
          point.height
        );
      });
      // 1. 空中点之间用黄色实线连接
      viewer.entities.add({
        polyline: {
          positions: airPositions,
          width: 2,
          material: Cesium.Color.YELLOW
        }
      });
      // 2. 每个空中点与地面点用蓝色虚线连接
      treeData.forEach(point => {
        const airPos = Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.height);
        point.children.forEach(child => {
          const groundPos = Cesium.Cartesian3.fromDegrees(child.longitude, child.latitude, child.height);
          viewer.entities.add({
            polyline: {
              positions: [airPos, groundPos],
              width: 1,
              material: new Cesium.PolylineDashMaterialProperty({
                color: Cesium.Color.BLUE,
                dashLength: 8
              })
            }
          });
        });
      });
    },
    addPoint(viewer, position, color, label) {
      viewer.entities.add({
        name: label,
        position,
        point: {
          pixelSize: 10,
          color: color,
          heightReference: Cesium.HeightReference.NONE
        },
        label: {
          text: label,
          font: '12px sans-serif',
          pixelOffset: new Cesium.Cartesian2(10, -10),
          style: Cesium.LabelStyle.FILL,
          fillColor: color,
          showBackground: true,
        }
      });
    },
    drawLine(viewer, positions, color, dashed = false) {
      viewer.entities.add({
        polyline: {
          positions,
          width: 2,
          material: dashed
            ? new Cesium.PolylineDashMaterialProperty({
                color: color,
                dashLength: 8,
              })
            : color,
        }
      });
    },
    handleClose(){
      this.dialogVisible = false
    },
@@ -156,17 +221,46 @@
    cancel(){
      this.showModel = false
    },
    rotateAllPoints(dataList) {
      return dataList.map(item => {
        console.log(item)
        const groundPoint = Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude, item.height);
        const towerPoint = Cesium.Cartesian3.fromDegrees(0, 0, 0);
        // 旋转地面点本身(会变,但绕自身旋转不会移动)
        const newGroundPoint = this.rotateAroundPoint( groundPoint,towerPoint, rotationAngle);
        const newGroundCarto = Cesium.Cartographic.fromCartesian(newGroundPoint);
        const newGround = {
          ...item,
          longitude: Cesium.Math.toDegrees(newGroundCarto.longitude),
          latitude: Cesium.Math.toDegrees(newGroundCarto.latitude),
          height: newGroundCarto.height
        };
        console.log(newGround)
        // 处理子空中点
        newGround.children = item.children.map(child => {
          const airPoint = Cesium.Cartesian3.fromDegrees(child.longitude, child.latitude, child.height);
          const rotatedAirPoint = this.rotateAroundPoint( airPoint,towerPoint, rotationAngle);
          const rotatedAirCarto = Cesium.Cartographic.fromCartesian(rotatedAirPoint);
          return {
            ...child,
            longitude: Cesium.Math.toDegrees(rotatedAirCarto.longitude),
            latitude: Cesium.Math.toDegrees(rotatedAirCarto.latitude),
            height: rotatedAirCarto.height
          };
        });
        return newGround;
      });
    },
    //合并逻辑  将附近n米范围内的点合并 并且处理成数据
    mergePoint(arr,mergeNumber,viewer){
      this.treeData = arr
      console.log(mergeNumber)
      const result = this.replaceCloseChildrenWithHighestPoint(this.treeData,mergeNumber);
      console.log(result)
      console.log(viewer)
      console.log(window.viewer)
      const updated = this.insertRaisedPoints(result, 125.1541, 46.5542, 236, 130);
      // const updated = this.insertRaisedPoints(result, 0,0, 236, 130);
      const updated = this.insertRaisedPoints(result, globalon, globalat, towerHeight);
      this.treeData = updated
      console.log(updated)
      const airPoints = []
      this.treeData.forEach(item => {
        const ground = item
@@ -179,9 +273,9 @@
              ground.longitude, ground.latitude, ground.height,
              air.longitude, air.latitude, air.height
            ]),
            width: 2,
            width: 5,
            material: new Cesium.PolylineDashMaterialProperty({
              color: Cesium.Color.WHITE,
              color: Cesium.Color.RED,
              dashLength: 16.0
            })
          }
@@ -194,13 +288,15 @@
        viewer.entities.add({
          polyline: {
            positions: Cesium.Cartesian3.fromDegreesArrayHeights(positions),
            width: 2,
            material: Cesium.Color.YELLOW
            width: 5,
            material: Cesium.Color.RED
          }
        })
      }
      console.log(this.treeData)
    },
    insertRaisedPoints(data, centerLon, centerLat, centerHeight, radius = 130) {
    //数组 塔的精度 塔的纬度  塔的高度  半径
    insertRaisedPoints(data, centerLon, centerLat, centerHeight, radius = 10) {
      const center = Cesium.Cartesian3.fromDegrees(centerLon, centerLat, centerHeight);
      const result = JSON.parse(JSON.stringify(data)); // 深拷贝避免污染原数据
      const inserts = []; // 用于存储插入项及其目标位置
@@ -346,35 +442,24 @@
    },
    getRowData(row){
      console.log(row)
      // //选择好模型 并且自动飞到模型位置
      // const position = Cesium.Cartesian3.fromDegrees(0, 0, 0);
      // // 设置模型方向(可选)
      // // const heading = Cesium.Math.toRadians(135); // 朝东南方向
      // let model = viewer.entities.getById("modelList");
      // const heading = Cesium.Math.toRadians(120); // 朝东南方向
      // const pitch = 0;
      // const roll = 0;
      // const orientation = Cesium.Transforms.headingPitchRollQuaternion(
      //   position,
      //   new Cesium.HeadingPitchRoll(heading, pitch, roll)
      // );
      // if(!model) {
      //   // 加载 glTF 模型
      //   const entity = viewer.entities.add({
      //     id: "modelList",
      //     name: "modelList",
      //     position: position,
      //     orientation: orientation,
      //     model: {
      //       uri: "/Model/tower.glb", // 替换成你的模型路径
      //       scale: 1000,
      //     },
      //   });
      //   viewer.flyTo(entity)
      // }else {
      //   model.orientation = orientation
      // }
      this.showMap = true
      this.modelName = row.modelName
      this.templateType = row.modelType
      if(this.templateId){
        this.form.templateName = row.templateName || ''
      }
      this.chooseModelId = row.id
      towerHeight = row.towerHeight || 47.47
      this.showMap =false
      this.$nextTick(()=>{
        // this.towerUrl = row.modelRoute
        if(this.templateId){
          this.towerUrl = row.ardTowerModel.modelRoute
        }else {
          this.towerUrl = row.modelRoute
        }
        this.showMap = true
      })
    },
    beforeDestroy() {
      // 清理 RAF 和事件
@@ -407,8 +492,71 @@
    },
    handleDrop (draggingNode, dropNode, dropType, ev) {
      console.log('拖拽完成', { draggingNode, dropNode, dropType })
      // Element-UI 已经帮你把 treeData 的顺序调好了,
      // 如果要保存顺序到后端,遍历 treeData 发请求即可
    },
    submit(){
      if(!this.form.templateName){
        return  this.$message({
          message: '请先输入模板名称再保存',
          type: 'warning'
        })
      }
      // this.dialogVisible = false
      // const dealArr = this.submitDealData()
      const transformed = this.treeData.map((ground, index) => {
        const pointNumber = index + 1;
        const child = ground.children?.[0] || {};
        return {
          altitude: child.height,
          ardGroundPoint: [
            {
              height: ground.height,
              latitude: ground.latitude,
              longitude: ground.longitude,
              pointNumber: 1,
              targetName: ground.label
            }
          ],
          latitude: child.latitude,
          longitude: child.longitude,
          pointNumber: pointNumber,
          targetName: child.label
        };
    })
    console.log(transformed)
    let parmas = {
      templateName:this.form.templateName,
      modelId:this.chooseModelId,
      ardListWayPointsLS:transformed
    }
    addPoint(parmas).then(res=>{
      console.log(res)
      if(res.code == 200) {
        this.$message({
          message: '新增模板成功',
          type: 'success'
        })
        this.$emit('on-submit')
        this.$emit('close')
      }
    })
    console.log(parmas)
    },
    // 计算点A绕点B逆时针旋转指定角度后的新位置
    //  空中点或者地面点笛卡尔坐标 塔的笛卡尔坐标    塔的朝向值
    rotateAroundPoint(startPoint,pivotPoint,rotationAngle) {
      // 创建一个从B点到本地坐标系的转换矩阵(东方向为X轴,北方向为Y轴,垂直方向为Z轴)
     const transformationMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(pivotPoint);
      // 获取世界坐标系到本地坐标系的转换矩阵
     const inverseTransformationMatrix = Cesium.Matrix4.inverse(transformationMatrix,new Cesium.Matrix4());
      // 将A点转换到局部坐标系中
     const localStartPoint = Cesium.Matrix4.multiplyByPoint(inverseTransformationMatrix, startPoint,new Cesium.Cartesian3());
      // 计算A点在局部坐标系中逆时针旋转指定角度后的新位置
     const rotatedX = localStartPoint.x * Math.cos(rotationAngle) + localStartPoint.y * Math.sin(rotationAngle);
     const rotatedY = localStartPoint.y * Math.cos(rotationAngle) - localStartPoint.x * Math.sin(rotationAngle);
     const rotatedZ = localStartPoint.z; // Z轴坐标保持不变
      // 将旋转后的局部坐标转换回世界坐标系
     return Cesium.Matrix4.multiplyByPoint(transformationMatrix, new Cesium.Cartesian3(rotatedX, rotatedY, rotatedZ), new Cesium.Cartesian3());
    }
  }
}
@@ -426,7 +574,7 @@
  text-align: center;
}
.chooseModel{
  height: 100px;
  height: 120px;
  /* border: 1px solid #dddddd; */
}
.cameraView{