Selaa lähdekoodia

可以上传不超过1G的模型,添加平望场景

WQQ 2 kuukautta sitten
vanhempi
commit
7d587c18d1
100 muutettua tiedostoa jossa 1045 lisäystä ja 52 poistoa
  1. 1 0
      RuoYi-Vue3/package.json
  2. 718 0
      RuoYi-Vue3/src/supermap-cesium-module/components/draw/draw-solid-figure/ModelEditor.js
  3. 50 29
      RuoYi-Vue3/src/supermap-cesium-module/components/draw/draw-solid-figure/draw-solid-figure.js
  4. 56 11
      RuoYi-Vue3/src/supermap-cesium-module/components/draw/draw-solid-figure/draw-solid-figure.vue
  5. 212 4
      RuoYi-Vue3/src/supermap-cesium-module/views/layout/aside.vue
  6. 3 3
      ruoyi-admin/src/main/resources/application.yml
  7. 3 3
      ruoyi-admin/target/classes/application.yml
  8. BIN
      ruoyi-admin/target/classes/com/ruoyi/RuoYiApplication.class
  9. BIN
      ruoyi-admin/target/classes/com/ruoyi/RuoYiServletInitializer.class
  10. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/cesium/CesiumMapConfigController.class
  11. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CaptchaController.class
  12. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CommonController.class
  13. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/CacheController.class
  14. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/ServerController.class
  15. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysLogininforController.class
  16. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysOperlogController.class
  17. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysUserOnlineController.class
  18. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysConfigController.class
  19. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDeptController.class
  20. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictDataController.class
  21. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictTypeController.class
  22. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysIndexController.class
  23. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysLoginController.class
  24. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysMenuController.class
  25. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysNoticeController.class
  26. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysPostController.class
  27. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysProfileController.class
  28. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRegisterController.class
  29. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRoleController.class
  30. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysUserController.class
  31. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/TestController.class
  32. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/UserEntity.class
  33. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/watershed/WatershedModelController.class
  34. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/controller/watershed/WatershedServiceController.class
  35. BIN
      ruoyi-admin/target/classes/com/ruoyi/web/core/config/SwaggerConfig.class
  36. BIN
      ruoyi-admin/target/ruoyi-admin.jar
  37. BIN
      ruoyi-admin/target/ruoyi-admin.jar.original
  38. BIN
      ruoyi-admin/uploads/models/2026/04/09/启闭机_20260409151853A006.glb
  39. BIN
      ruoyi-admin/uploads/models/2026/04/09/启闭机_20260409162511A009.glb
  40. BIN
      ruoyi-admin/uploads/models/2026/04/09/平望场景_20260409162416A008.glb
  41. BIN
      ruoyi-admin/uploads/models/2026/04/09/平望场景_20260409165637A001.glb
  42. BIN
      ruoyi-admin/uploads/models/2026/04/10/平望场景_20260410134234A003.glb
  43. 2 2
      ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
  44. BIN
      ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$ColumnType.class
  45. BIN
      ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$Type.class
  46. BIN
      ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel.class
  47. BIN
      ruoyi-common/target/classes/com/ruoyi/common/config/RuoYiConfig.class
  48. BIN
      ruoyi-common/target/classes/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.class
  49. BIN
      ruoyi-common/target/classes/com/ruoyi/common/constant/CacheConstants.class
  50. BIN
      ruoyi-common/target/classes/com/ruoyi/common/constant/Constants.class
  51. BIN
      ruoyi-common/target/classes/com/ruoyi/common/constant/GenConstants.class
  52. BIN
      ruoyi-common/target/classes/com/ruoyi/common/constant/HttpStatus.class
  53. BIN
      ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants$Status.class
  54. BIN
      ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants.class
  55. BIN
      ruoyi-common/target/classes/com/ruoyi/common/constant/UserConstants.class
  56. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController$1.class
  57. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController.class
  58. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/AjaxResult.class
  59. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/BaseEntity.class
  60. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/R.class
  61. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeEntity.class
  62. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeSelect.class
  63. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDept.class
  64. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictData.class
  65. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictType.class
  66. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysMenu.class
  67. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysRole.class
  68. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysUser.class
  69. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginBody.class
  70. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginUser.class
  71. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/RegisterBody.class
  72. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/page/PageDomain.class
  73. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/page/TableDataInfo.class
  74. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/page/TableSupport.class
  75. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/redis/RedisCache.class
  76. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/text/CharsetKit.class
  77. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/text/Convert.class
  78. BIN
      ruoyi-common/target/classes/com/ruoyi/common/core/text/StrFormatter.class
  79. BIN
      ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessStatus.class
  80. BIN
      ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessType.class
  81. BIN
      ruoyi-common/target/classes/com/ruoyi/common/enums/DataSourceType.class
  82. BIN
      ruoyi-common/target/classes/com/ruoyi/common/enums/DesensitizedType.class
  83. BIN
      ruoyi-common/target/classes/com/ruoyi/common/enums/HttpMethod.class
  84. BIN
      ruoyi-common/target/classes/com/ruoyi/common/enums/LimitType.class
  85. BIN
      ruoyi-common/target/classes/com/ruoyi/common/enums/OperatorType.class
  86. BIN
      ruoyi-common/target/classes/com/ruoyi/common/enums/UserStatus.class
  87. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/DemoModeException.class
  88. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/GlobalException.class
  89. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/ServiceException.class
  90. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/UtilException.class
  91. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/base/BaseException.class
  92. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileException.class
  93. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.class
  94. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileSizeLimitExceededException.class
  95. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileUploadException.class
  96. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class
  97. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidImageExtensionException.class
  98. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class
  99. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class
  100. BIN
      ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException.class

+ 1 - 0
RuoYi-Vue3/package.json

@@ -24,6 +24,7 @@
     "autofit.js": "^3.2.8",
     "axios": "1.9.0",
     "cesium": "^1.137.0",
+    "cesium-transform-gizmo": "^0.1.1",
     "clipboard": "2.0.11",
     "echarts": "5.6.0",
     "element-plus": "2.10.7",

+ 718 - 0
RuoYi-Vue3/src/supermap-cesium-module/components/draw/draw-solid-figure/ModelEditor.js

@@ -0,0 +1,718 @@
+// 模型编辑控制器
+class ModelEditor {
+  constructor(viewer, entity) {
+    this.viewer = viewer;
+    this.entity = entity;
+    this.handler = null;
+    this.activePrimitive = null;
+    this.activeAxis = null;
+    this.startMousePosition = null;
+    this.startMouseCartesian = null;
+    this.dragMode = null;
+    
+    this.translateEnabled = true;
+    this.rotateEnabled = true;
+    this.scaleEnabled = true;
+    
+    this.primitives = [];
+    this.originPoint = null;
+    this.moveAxes = [];
+    this.axisPlanes = [];
+    this.rotateAxes = [];
+    this.scalePoints = [];
+    
+    // 获取 primitives 的辅助方法
+    this.primitivesCollection = this.viewer.primitives || (this.viewer.scene && this.viewer.scene.primitives);
+    
+    this.center = this.getCenter();
+    this.radius = this.getRadius();
+    
+    if (this.entity.model) {
+      if (this.entity.model.readyPromise) {
+        this.entity.model.readyPromise.then(() => {
+          console.log('模型加载完成(通过 readyPromise)');
+          this.center = this.getCenter();
+          this.radius = this.getRadius();
+          this.init();
+        }).catch(() => {
+          console.log('模型加载失败,使用当前位置初始化枢轴');
+          this.center = this.getCenter();
+          this.radius = this.getRadius();
+          this.init();
+        });
+      } else if (this.entity.model.boundingSphere) {
+        this.init();
+      } else {
+        console.log('模型没有 readyPromise,等待 boundingSphere');
+        this.entity.model.definitionChanged.addEventListener(() => {
+          if (this.entity.model.boundingSphere) {
+            console.log('模型加载完成(通过 definitionChanged)');
+            this.center = this.getCenter();
+            this.radius = this.getRadius();
+            this.init();
+          }
+        });
+        
+        setTimeout(() => {
+          if (!this.entity.model.boundingSphere) {
+            console.log('模型加载超时,使用当前位置初始化枢轴');
+            this.center = this.getCenter();
+            this.radius = this.getRadius();
+            this.init();
+          }
+        }, 3000);
+      }
+    } else {
+      this.init();
+    }
+  }
+  
+  async waitForModelLoad() {
+    if (this.initPromise) {
+      await this.initPromise;
+    }
+  }
+  
+  getCenter() {
+    // 首先尝试从 entity.position 获取中心点
+    if (this.entity.position) {
+      const position = this.entity.position.getValue(this.viewer.clock.currentTime);
+      if (position) {
+        return position;
+      }
+    }
+    
+    // 尝试多种方式获取模型中心点
+    if (this.entity.model) {
+      // 尝试直接访问 boundingSphere
+      if (this.entity.model.boundingSphere) {
+        return this.entity.model.boundingSphere.center;
+      }
+      // 尝试通过 _boundingSphere 访问
+      if (this.entity.model._boundingSphere) {
+        return this.entity.model._boundingSphere.center;
+      }
+      // 尝试通过 _primitive 访问
+      if (this.entity.model._primitive && this.entity.model._primitive.boundingSphere) {
+        return this.entity.model._primitive.boundingSphere.center;
+      }
+    }
+    
+    // 如果实体有 position 属性但没有获取到值,尝试获取实体的默认位置
+    if (this.entity.position) {
+      return this.entity.position.getValue(this.viewer.clock.currentTime) || this.viewer.camera.position;
+    }
+    
+    // 返回相机位置作为默认值
+    return this.viewer.camera.position;
+  }
+  
+  getRadius() {
+    // 首先尝试从模型获取半径
+    if (this.entity.model) {
+      // 尝试直接访问 boundingSphere
+      if (this.entity.model.boundingSphere) {
+        return this.entity.model.boundingSphere.radius;
+      }
+      // 尝试通过 _boundingSphere 访问
+      if (this.entity.model._boundingSphere) {
+        return this.entity.model._boundingSphere.radius;
+      }
+      // 尝试通过 _primitive 访问
+      if (this.entity.model._primitive && this.entity.model._primitive.boundingSphere) {
+        return this.entity.model._primitive.boundingSphere.radius;
+      }
+    }
+    
+    // 使用较小的默认半径
+    return 2;
+  }
+  
+  init() {
+    // 先清理旧的 primitives
+    this.clearPrimitives();
+    
+    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.canvas);
+    this.createPrimitives();
+    this.addEventListener();
+  }
+  
+  createPrimitives(isModel = true) {
+    this.createOriginPoint();
+    
+    if (this.translateEnabled) {
+      this.createMoveAxis();
+      this.createAxisPlane();
+    }
+    
+    if (isModel && this.rotateEnabled) {
+      this.createRotateAxis();
+    }
+    
+    if (isModel && this.scaleEnabled) {
+      this.createScaleAxis();
+    }
+  }
+  
+  clearPrimitives() {
+    this.primitives.forEach(p => {
+      if (this.primitivesCollection) {
+        this.primitivesCollection.remove(p);
+      }
+    });
+    this.primitives = [];
+    this.moveAxes = [];
+    this.axisPlanes = [];
+    this.rotateAxes = [];
+    this.scalePoints = [];
+  }
+  
+  createOriginPoint() {
+    if (!this.center) return;
+    
+    const primitives = this.primitivesCollection;
+    if (!primitives) return;
+    
+    const size = Math.max(this.radius * 0.05, 0.1);
+    
+    const primitive = primitives.add(new Cesium.Primitive({
+      geometryInstances: new Cesium.GeometryInstance({
+        geometry: new Cesium.BoxGeometry({
+          vertexFormat: Cesium.VertexFormat.POSITION_AND_COLOR,
+          maximum: new Cesium.Cartesian3(
+            this.center.x + size,
+            this.center.y + size,
+            this.center.z + size
+          ),
+          minimum: new Cesium.Cartesian3(
+            this.center.x - size,
+            this.center.y - size,
+            this.center.z - size
+          )
+        }),
+        attributes: {
+          color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE.withAlpha(0.8))
+        }
+      }),
+      appearance: new Cesium.PerInstanceColorAppearance({
+        flat: true
+      })
+    }));
+    
+    this.primitives.push(primitive);
+    this.originPoint = primitive;
+  }
+  
+  getModelRotationMatrix() {
+    try {
+      console.log('开始获取模型旋转矩阵');
+      const entity = this.entity;
+      
+      if (entity.model) {
+        const model = entity.model;
+        console.log('model 属性:', Object.keys(model));
+        console.log('model._nodeTransformations:', model._nodeTransformations);
+        
+        if (model._nodeTransformations) {
+          console.log('遍历 nodeTransformations:');
+          for (const key in model._nodeTransformations) {
+            const node = model._nodeTransformations[key];
+            console.log('  节点:', key, node);
+            if (node.rotation) {
+              console.log('    rotation:', node.rotation);
+              const rot = Cesium.Matrix3.fromQuaternion(node.rotation, new Cesium.Matrix3());
+              console.log('从 nodeTransformations.rotation 获取旋转矩阵成功');
+              return rot;
+            }
+            if (node.matrix) {
+              console.log('    matrix:', node.matrix);
+              const mat = new Cesium.Matrix4(
+                node.matrix[0], node.matrix[1], node.matrix[2], node.matrix[3],
+                node.matrix[4], node.matrix[5], node.matrix[6], node.matrix[7],
+                node.matrix[8], node.matrix[9], node.matrix[10], node.matrix[11],
+                node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15]
+              );
+              const rot = Cesium.Matrix4.getMatrix3(mat, new Cesium.Matrix3());
+              console.log('从 nodeTransformations.matrix 获取旋转矩阵成功');
+              return rot;
+            }
+          }
+        }
+        
+        if (model._root) {
+          console.log('model._root 存在');
+          const root = model._root;
+          console.log('root 属性:', Object.keys(root));
+          
+          if (root.transform) {
+            console.log('root.transform:', root.transform);
+            const rot = Cesium.Matrix4.getMatrix3(root.transform, new Cesium.Matrix3());
+            console.log('从 root.transform 获取旋转矩阵成功');
+            return rot;
+          }
+          
+          if (root._transform) {
+            console.log('root._transform:', root._transform);
+            const rot = Cesium.Matrix4.getMatrix3(root._transform, new Cesium.Matrix3());
+            console.log('从 root._transform 获取旋转矩阵成功');
+            return rot;
+          }
+        }
+      }
+      
+      console.log('无法获取旋转矩阵,使用单位矩阵');
+    } catch (e) {
+      console.log('获取旋转矩阵失败:', e);
+    }
+    
+    return Cesium.Matrix3.IDENTITY.clone();
+  }
+  
+  createMoveAxis() {
+    if (!this.center) return;
+    
+    const length = this.radius * 0.8;
+    const rotationMatrix = this.getModelRotationMatrix();
+    
+    const createAxis = (axis, color) => {
+      let localOffset;
+      if (axis === 'X') {
+        localOffset = new Cesium.Cartesian3(length, 0, 0);
+      } else if (axis === 'Y') {
+        localOffset = new Cesium.Cartesian3(0, length, 0);
+      } else {
+        localOffset = new Cesium.Cartesian3(0, 0, length);
+      }
+      
+      const worldOffset = Cesium.Matrix3.multiplyByVector(rotationMatrix, localOffset, new Cesium.Cartesian3());
+      
+      const points = [
+        new Cesium.Cartesian3(this.center.x, this.center.y, this.center.z),
+        new Cesium.Cartesian3(
+          this.center.x + worldOffset.x,
+          this.center.y + worldOffset.y,
+          this.center.z + worldOffset.z
+        )
+      ];
+      
+      const primitive = this.primitivesCollection.add(new Cesium.Primitive({
+        geometryInstances: new Cesium.GeometryInstance({
+          geometry: new Cesium.PolylineGeometry({
+            positions: points,
+            vertexFormat: Cesium.VertexFormat.POSITION_AND_COLOR
+          }),
+          attributes: {
+            color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
+          }
+        }),
+        appearance: new Cesium.PolylineColorAppearance()
+      }));
+      
+      this.primitives.push(primitive);
+      this.moveAxes.push({ primitive, axis, color });
+      return primitive;
+    };
+    
+    createAxis('X', Cesium.Color.RED);
+    createAxis('Y', Cesium.Color.GREEN);
+    createAxis('Z', Cesium.Color.BLUE);
+  }
+  
+  createAxisPlane() {
+    if (!this.center) return;
+    
+    const size = this.radius * 0.8;
+    const halfSize = size / 2;
+    
+    const createPlane = (axis1, axis2, color) => {
+      let points = [];
+      
+      if (axis1 === 'X' && axis2 === 'Y') {
+        points = [
+          new Cesium.Cartesian3(-halfSize, -halfSize, 0),
+          new Cesium.Cartesian3(halfSize, -halfSize, 0),
+          new Cesium.Cartesian3(halfSize, halfSize, 0),
+          new Cesium.Cartesian3(-halfSize, halfSize, 0)
+        ];
+      } else if (axis1 === 'X' && axis2 === 'Z') {
+        points = [
+          new Cesium.Cartesian3(-halfSize, 0, -halfSize),
+          new Cesium.Cartesian3(halfSize, 0, -halfSize),
+          new Cesium.Cartesian3(halfSize, 0, halfSize),
+          new Cesium.Cartesian3(-halfSize, 0, halfSize)
+        ];
+      } else if (axis1 === 'Y' && axis2 === 'Z') {
+        points = [
+          new Cesium.Cartesian3(0, -halfSize, -halfSize),
+          new Cesium.Cartesian3(0, halfSize, -halfSize),
+          new Cesium.Cartesian3(0, halfSize, halfSize),
+          new Cesium.Cartesian3(0, -halfSize, halfSize)
+        ];
+      }
+      
+      points = points.map(p => new Cesium.Cartesian3(
+        this.center.x + p.x,
+        this.center.y + p.y,
+        this.center.z + p.z
+      ));
+      
+      const primitive = this.primitivesCollection.add(new Cesium.Primitive({
+        geometryInstances: new Cesium.GeometryInstance({
+          geometry: new Cesium.PolygonGeometry({
+            polygonHierarchy: new Cesium.PolygonHierarchy(points),
+            extrudedHeight: 0.1
+          }),
+          attributes: {
+            color: Cesium.ColorGeometryInstanceAttribute.fromColor(color.withAlpha(0.3))
+          }
+        }),
+        appearance: new Cesium.PerInstanceColorAppearance({
+          flat: true
+        })
+      }));
+      
+      this.primitives.push(primitive);
+      this.axisPlanes.push({ primitive, axis1, axis2, color });
+    };
+    
+    createPlane('X', 'Y', Cesium.Color.fromCssColorString('#ffff00'));
+    createPlane('X', 'Z', Cesium.Color.fromCssColorString('#ff00ff'));
+    createPlane('Y', 'Z', Cesium.Color.fromCssColorString('#00ffff'));
+  }
+  
+  createRotateAxis() {
+    if (!this.center) return;
+    
+    const ringRadius = this.radius * 1.0;
+    const rotationMatrix = this.getModelRotationMatrix();
+    
+    const createRotateAxis = (axis, color) => {
+      const points = [];
+      const segments = 64;
+      
+      for (let i = 0; i <= segments; i++) {
+        const angle = (i / segments) * Math.PI * 2;
+        let localX, localY, localZ;
+        
+        if (axis === 'X') {
+          localX = 0;
+          localY = Math.cos(angle) * ringRadius;
+          localZ = Math.sin(angle) * ringRadius;
+        } else if (axis === 'Y') {
+          localX = Math.cos(angle) * ringRadius;
+          localY = 0;
+          localZ = Math.sin(angle) * ringRadius;
+        } else {
+          localX = Math.cos(angle) * ringRadius;
+          localY = Math.sin(angle) * ringRadius;
+          localZ = 0;
+        }
+        
+        const worldOffset = Cesium.Matrix3.multiplyByVector(
+          rotationMatrix,
+          new Cesium.Cartesian3(localX, localY, localZ),
+          new Cesium.Cartesian3()
+        );
+        
+        points.push(new Cesium.Cartesian3(
+          this.center.x + worldOffset.x,
+          this.center.y + worldOffset.y,
+          this.center.z + worldOffset.z
+        ));
+      }
+      
+      const primitive = this.primitivesCollection.add(new Cesium.Primitive({
+        geometryInstances: new Cesium.GeometryInstance({
+          geometry: new Cesium.PolylineGeometry({
+            positions: points,
+            vertexFormat: Cesium.VertexFormat.POSITION_AND_COLOR
+          }),
+          attributes: {
+            color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
+          }
+        }),
+        appearance: new Cesium.PolylineColorAppearance()
+      }));
+      
+      this.primitives.push(primitive);
+      this.rotateAxes.push({ primitive, axis, color });
+      return primitive;
+    };
+    
+    createRotateAxis('X', Cesium.Color.RED);
+    createRotateAxis('Y', Cesium.Color.GREEN);
+    createRotateAxis('Z', Cesium.Color.BLUE);
+  }
+  
+  createScaleAxis() {
+    if (!this.center) return;
+    
+    const length = this.radius * 0.8;
+    const rotationMatrix = this.getModelRotationMatrix();
+    
+    const createScalePoint = (axis, color) => {
+      let localOffset;
+      if (axis === 'X') {
+        localOffset = new Cesium.Cartesian3(length, 0, 0);
+      } else if (axis === 'Y') {
+        localOffset = new Cesium.Cartesian3(0, length, 0);
+      } else {
+        localOffset = new Cesium.Cartesian3(0, 0, length);
+      }
+      
+      const worldOffset = Cesium.Matrix3.multiplyByVector(rotationMatrix, localOffset, new Cesium.Cartesian3());
+      const size = Math.max(this.radius * 0.08, 0.15);
+      
+      const primitive = this.primitivesCollection.add(new Cesium.Primitive({
+        geometryInstances: new Cesium.GeometryInstance({
+          geometry: new Cesium.BoxGeometry({
+            vertexFormat: Cesium.VertexFormat.POSITION_AND_COLOR,
+            maximum: new Cesium.Cartesian3(
+              this.center.x + worldOffset.x + size,
+              this.center.y + worldOffset.y + size,
+              this.center.z + worldOffset.z + size
+            ),
+            minimum: new Cesium.Cartesian3(
+              this.center.x + worldOffset.x - size,
+              this.center.y + worldOffset.y - size,
+              this.center.z + worldOffset.z - size
+            )
+          }),
+          attributes: {
+            color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
+          }
+        }),
+        appearance: new Cesium.PerInstanceColorAppearance({
+          flat: true
+        })
+      }));
+      
+      this.primitives.push(primitive);
+      this.scalePoints.push({ primitive, axis, color });
+      return primitive;
+    };
+    
+    createScalePoint('X', Cesium.Color.RED);
+    createScalePoint('Y', Cesium.Color.GREEN);
+    createScalePoint('Z', Cesium.Color.BLUE);
+  }
+  
+  addEventListener() {
+    const handler = this.handler;
+    
+    handler.setInputAction((event) => {
+      const picked = this.viewer.scene.pick(event.position);
+      
+      if (Cesium.defined(picked) && Cesium.defined(picked.primitive)) {
+        this.activePrimitive = picked.primitive;
+        this.startMousePosition = event.position;
+        
+        if (this.originPoint === picked.primitive) {
+          this.dragMode = 'translate';
+          this.activeAxis = 'XYZ';
+        } else if (this.moveAxes.some(a => a.primitive === picked.primitive)) {
+          const axisInfo = this.moveAxes.find(a => a.primitive === picked.primitive);
+          this.dragMode = 'translate';
+          this.activeAxis = axisInfo.axis;
+        } else if (this.axisPlanes.some(p => p.primitive === picked.primitive)) {
+          const planeInfo = this.axisPlanes.find(p => p.primitive === picked.primitive);
+          this.dragMode = 'translate';
+          this.activeAxis = planeInfo.axis1 + planeInfo.axis2;
+        } else if (this.rotateAxes.some(r => r.primitive === picked.primitive)) {
+          const axisInfo = this.rotateAxes.find(r => r.primitive === picked.primitive);
+          this.dragMode = 'rotate';
+          this.activeAxis = axisInfo.axis;
+        } else if (this.scalePoints.some(s => s.primitive === picked.primitive)) {
+          const axisInfo = this.scalePoints.find(s => s.primitive === picked.primitive);
+          this.dragMode = 'scale';
+          this.activeAxis = axisInfo.axis;
+        }
+        
+        if (this.dragMode) {
+          this.startMouseCartesian = this.pickCartesianFromMouse(event.position);
+        }
+      }
+    }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
+    
+    handler.setInputAction((event) => {
+      if (!this.dragMode || !this.activePrimitive) return;
+      
+      const currentMouseCartesian = this.pickCartesianFromMouse(event.position);
+      
+      if (!this.startMouseCartesian || !currentMouseCartesian) return;
+      
+      if (this.dragMode === 'translate') {
+        this.translate(currentMouseCartesian);
+      } else if (this.dragMode === 'rotate') {
+        this.rotate(event.position);
+      } else if (this.dragMode === 'scale') {
+        this.scale(currentMouseCartesian);
+      }
+      
+      this.startMouseCartesian = currentMouseCartesian;
+    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
+    
+    handler.setInputAction(() => {
+      this.activePrimitive = null;
+      this.dragMode = null;
+      this.startMousePosition = null;
+      this.startMouseCartesian = null;
+    }, Cesium.ScreenSpaceEventType.LEFT_UP);
+  }
+  
+  pickCartesianFromMouse(mousePosition) {
+    const ray = this.viewer.camera.getRay(mousePosition);
+    const cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene);
+    
+    if (!cartesian) {
+      // 如果没有与地面相交,则使用相机前方一定距离的点
+      const position = this.viewer.camera.position;
+      const direction = this.viewer.camera.direction;
+      const distance = 100;
+      cartesian = new Cesium.Cartesian3(
+        position.x + direction.x * distance,
+        position.y + direction.y * distance,
+        position.z + direction.z * distance
+      );
+    }
+    
+    return cartesian;
+  }
+  
+  translate(currentMouseCartesian) {
+    if (!this.startMouseCartesian || !currentMouseCartesian) return;
+    
+    const offset = new Cesium.Cartesian3(
+      currentMouseCartesian.x - this.startMouseCartesian.x,
+      currentMouseCartesian.y - this.startMouseCartesian.y,
+      currentMouseCartesian.z - this.startMouseCartesian.z
+    );
+    
+    // 根据选中轴过滤偏移量
+    if (this.activeAxis.indexOf('X') === -1) offset.x = 0;
+    if (this.activeAxis.indexOf('Y') === -1) offset.y = 0;
+    if (this.activeAxis.indexOf('Z') === -1) offset.z = 0;
+    
+    // 更新实体位置
+    if (this.entity.position) {
+      const currentPosition = this.entity.position.getValue(this.viewer.clock.currentTime);
+      if (currentPosition) {
+        const newPosition = new Cesium.Cartesian3(
+          currentPosition.x + offset.x,
+          currentPosition.y + offset.y,
+          currentPosition.z + offset.z
+        );
+        this.entity.position = newPosition;
+      }
+    }
+    
+    // 更新中心点
+    this.center = this.getCenter();
+    
+    // 重新创建枢轴
+    this.init();
+  }
+  
+  rotate(mousePosition) {
+    if (!this.startMousePosition || !mousePosition) return;
+    
+    const deltaX = mousePosition.x - this.startMousePosition.x;
+    const angle = deltaX * 0.01;
+    
+    let axis = new Cesium.Cartesian3(0, 0, 1);
+    if (this.activeAxis === 'X') {
+      axis = new Cesium.Cartesian3(1, 0, 0);
+    } else if (this.activeAxis === 'Y') {
+      axis = new Cesium.Cartesian3(0, 1, 0);
+    } else if (this.activeAxis === 'Z') {
+      axis = new Cesium.Cartesian3(0, 0, 1);
+    }
+    
+    // 创建旋转矩阵
+    const quaternion = Cesium.Quaternion.fromAxisAngle(axis, angle);
+    const rotationMatrix = Cesium.Matrix3.fromQuaternion(quaternion);
+    
+    // 获取当前位置
+    if (this.entity.position) {
+      const currentPosition = this.entity.position.getValue(this.viewer.clock.currentTime);
+      if (currentPosition) {
+        // 计算新位置(绕中心点旋转)
+        const translation = Cesium.Matrix4.fromTranslation(currentPosition);
+        const rotation = Cesium.Matrix4.fromTranslationRotationScale(
+          Cesium.Vector3.ZERO,
+          quaternion,
+          new Cesium.Cartesian3(1, 1, 1)
+        );
+        
+        Cesium.Matrix4.multiply(translation, rotation, translation);
+        
+        const newPosition = Cesium.Matrix4.getTranslation(translation, new Cesium.Cartesian3());
+        this.entity.position = newPosition;
+      }
+    }
+    
+    // 更新中心点
+    this.center = this.getCenter();
+    
+    // 重新创建枢轴
+    this.init();
+    
+    this.startMousePosition = mousePosition;
+  }
+  
+  scale(currentMouseCartesian) {
+    if (!this.startMouseCartesian || !currentMouseCartesian) return;
+    
+    const offset = new Cesium.Cartesian3(
+      currentMouseCartesian.x - this.startMouseCartesian.x,
+      currentMouseCartesian.y - this.startMouseCartesian.y,
+      currentMouseCartesian.z - this.startMouseCartesian.z
+    );
+    
+    // 根据选中轴计算缩放比例
+    let scaleFactor = 1.0;
+    if (this.activeAxis === 'X' && offset.x !== 0) {
+      scaleFactor = 1 + offset.x * 0.01;
+    } else if (this.activeAxis === 'Y' && offset.y !== 0) {
+      scaleFactor = 1 + offset.y * 0.01;
+    } else if (this.activeAxis === 'Z' && offset.z !== 0) {
+      scaleFactor = 1 + offset.z * 0.01;
+    }
+    
+    // 更新模型缩放
+    if (this.entity.model) {
+      const currentScale = this.entity.model.scale || 1.0;
+      this.entity.model.scale = currentScale * scaleFactor;
+    }
+    
+    this.startMouseCartesian = currentMouseCartesian;
+  }
+  
+  destroy() {
+    // 清理事件监听器
+    if (this.handler) {
+      this.handler.destroy();
+      this.handler = null;
+    }
+    
+    // 清理 primitives
+    if (this.entity.model && this.entity.model.definitionChanged) {
+      this.entity.model.definitionChanged.removeEventListener(() => {
+        if (this.entity.model.boundingSphere) {
+          this.center = this.getCenter();
+          this.radius = this.getRadius();
+          this.init();
+        }
+      });
+    }
+    
+    this.clearPrimitives();
+    
+    this.activePrimitive = null;
+    this.activeAxis = null;
+    this.dragMode = null;
+    this.startMouseCartesian = null;
+  }
+}
+
+export default ModelEditor;

+ 50 - 29
RuoYi-Vue3/src/supermap-cesium-module/components/draw/draw-solid-figure/draw-solid-figure.js

@@ -52,52 +52,69 @@ class DrawSolidFigure {
    * @param {number} opacity - 透明度
    */
   createSolid(position, type, color, opacity) {
+    // 使用 Cesium.Model.fromGltfAsync 加载 glTF 模型
+    // 由于没有实际的 glTF 文件,我们使用 Cesium 的 BoxPrimitive、CylinderPrimitive 等
+    // 这些 Primitive 可以通过 Cesium.Model 包装后被 TransformGizmo 支持
+    
     const cesiumColor = Cesium.Color.fromCssColorString(color).withAlpha(opacity);
-
+    
+    let primitive;
     switch (type) {
       case 'box':
-        this.currentEntity = this.viewer.entities.add({
+        primitive = this.viewer.primitives.add(new Cesium.BoxPrimitive({
+          dimensions: new Cesium.Cartesian3(100, 100, 100),
+          material: cesiumColor,
           position: position,
-          box: {
-            dimensions: new Cesium.Cartesian3(100, 100, 100),
-            material: cesiumColor
-          }
-        });
+          ellipsoid: this.viewer.scene.globe.ellipsoid
+        }));
         break;
       case 'cylinder':
-        this.currentEntity = this.viewer.entities.add({
+        primitive = this.viewer.primitives.add(new Cesium.CylinderPrimitive({
+          length: 200,
+          topRadius: 50,
+          bottomRadius: 50,
+          material: cesiumColor,
           position: position,
-          cylinder: {
-            length: 200,
-            topRadius: 50,
-            bottomRadius: 50,
-            material: cesiumColor
-          }
-        });
+          ellipsoid: this.viewer.scene.globe.ellipsoid
+        }));
         break;
       case 'sphere':
-        this.currentEntity = this.viewer.entities.add({
+        primitive = this.viewer.primitives.add(new Cesium.EllipsoidPrimitive({
+          radii: new Cesium.Cartesian3(100, 100, 100),
+          material: cesiumColor,
           position: position,
-          ellipsoid: {
-            radii: new Cesium.Cartesian3(100, 100, 100),
-            material: cesiumColor
-          }
-        });
+          ellipsoid: this.viewer.scene.globe.ellipsoid
+        }));
         break;
       case 'cone':
-        this.currentEntity = this.viewer.entities.add({
+        primitive = this.viewer.primitives.add(new Cesium.CylinderPrimitive({
+          length: 200,
+          topRadius: 0,
+          bottomRadius: 100,
+          material: cesiumColor,
           position: position,
-          cylinder: {
-            length: 200,
-            topRadius: 0,
-            bottomRadius: 100,
-            material: cesiumColor
-          }
-        });
+          ellipsoid: this.viewer.scene.globe.ellipsoid
+        }));
         break;
       default:
         break;
     }
+    
+    // 创建一个虚拟的 entity 来存储元数据
+    this.currentEntity = this.viewer.entities.add({
+      position: position,
+      modelType: type,
+      modelName: type === 'cylinder' ? '圆柱体' : type === 'cone' ? '圆锥体' : type === 'sphere' ? '球体' : '立方体',
+      originalColor: color,
+      originalOpacity: opacity,
+      _primitive: primitive
+    });
+    
+    // 将 primitive 存储在 entity 上,供 TransformGizmo 使用
+    this.currentEntity.model = {
+      _primitive: primitive,
+      boundingSphere: primitive.boundingSphere
+    };
 
     return this.currentEntity;
   }
@@ -111,6 +128,10 @@ class DrawSolidFigure {
       this.handler.clear();
     }
     if (this.currentEntity) {
+      // 清除关联的 primitive
+      if (this.currentEntity.model && this.currentEntity.model._primitive) {
+        this.viewer.primitives.remove(this.currentEntity.model._primitive);
+      }
       this.viewer.entities.remove(this.currentEntity);
       this.currentEntity = null;
     }

+ 56 - 11
RuoYi-Vue3/src/supermap-cesium-module/components/draw/draw-solid-figure/draw-solid-figure.vue

@@ -63,7 +63,6 @@
       <div class="boxchild">
         <button class="tbtn" type="button" v-on:click="startDraw">{{ isDrawing ? '停止绘制' : '绘制' }}</button>
         <button class="tbtn tbtn-margin-left" type="button" v-on:click="clear">清除</button>
-        <button class="tbtn tbtn-margin-left" type="button" v-on:click="close">关闭</button>
       </div>
     </div>
   </div>
@@ -72,6 +71,7 @@
 <script>
 import { ref, onMounted, onUnmounted, reactive, watch } from 'vue';
 import { ElMessage } from 'element-plus';
+import ModelEditor from './ModelEditor';
 
 export default {
   name: 'Sm3dDrawSolidFigure',
@@ -152,7 +152,9 @@ export default {
         if (selectedEntity.value) {
           removeHighlight(selectedEntity.value);
           if (currentEditor) {
-            currentEditor.deactivate();
+            if (currentEditor.destroy) {
+              currentEditor.destroy();
+            }
             currentEditor = null;
           }
           selectedEntity.value = null;
@@ -173,14 +175,18 @@ export default {
     // 清除所有模型
     const clear = () => {
       if (viewer) {
+        // 清除编辑器
+        if (currentEditor) {
+          if (currentEditor.destroy) {
+            currentEditor.destroy();
+          }
+          currentEditor = null;
+        }
+        
         viewer.entities.removeAll();
         selectedEntity.value = null;
         selectedModel.value = null;
         isDrawing.value = false;
-        if (currentEditor) {
-          currentEditor.deactivate();
-          currentEditor = null;
-        }
         ElMessage.success('已清除所有模型');
       }
     };
@@ -296,7 +302,9 @@ export default {
       }
       document.removeEventListener('keydown', handleKeyDown);
       if (currentEditor) {
-        currentEditor.deactivate();
+        if (currentEditor.destroy) {
+          currentEditor.destroy();
+        }
         currentEditor = null;
       }
       isDrawing.value = false;
@@ -308,7 +316,9 @@ export default {
       if (selectedEntity.value) {
         removeHighlight(selectedEntity.value);
         if (currentEditor) {
-          currentEditor.deactivate();
+          if (currentEditor.destroy) {
+            currentEditor.destroy();
+          }
           currentEditor = null;
         }
       }
@@ -323,7 +333,9 @@ export default {
       if (selectedEntity.value) {
         removeHighlight(selectedEntity.value);
         if (currentEditor) {
-          currentEditor.deactivate();
+          if (currentEditor.destroy) {
+            currentEditor.destroy();
+          }
           currentEditor = null;
         }
         selectedEntity.value = null;
@@ -389,13 +401,46 @@ export default {
 
     // 添加操作轴编辑器
     const addEditor = (entity) => {
-      if (!entity) return;
+      if (!entity || !viewer) return;
 
       // 清除之前的编辑器
       if (currentEditor) {
-        currentEditor.deactivate();
+        if (currentEditor.destroy) {
+          currentEditor.destroy();
+        }
         currentEditor = null;
       }
+      
+      console.log('Attempting to create ModelEditor for entity:', entity);
+      
+      // 检查实体是否有 model 属性
+      if (entity.model && entity.model.boundingSphere) {
+        try {
+          console.log('Creating ModelEditor with entity:', entity);
+          
+          currentEditor = new ModelEditor(viewer, entity);
+          
+          console.log('ModelEditor created successfully');
+        } catch (error) {
+          console.error('Failed to create ModelEditor:', error);
+          currentEditor = {
+            destroy: function() {
+              this.active = false;
+              viewer._element.style.cursor = 'default';
+            }
+          };
+        }
+      } else {
+        console.warn('Entity does not have model for ModelEditor');
+        currentEditor = {
+          destroy: function() {
+            this.active = false;
+            viewer._element.style.cursor = 'default';
+          }
+        };
+      }
+      
+      console.log('Editor created for entity:', entity);
     };
 
     // 创建几何体

+ 212 - 4
RuoYi-Vue3/src/supermap-cesium-module/views/layout/aside.vue

@@ -276,6 +276,7 @@ import { ElMessage } from 'element-plus';
 import { getToken } from '@/utils/auth'
 import axios from 'axios'
 import { onMounted, watch, computed } from 'vue'
+import ModelEditor from "../../components/draw/draw-solid-figure/ModelEditor";  //模型编辑器
 
 export default {
   name: "layout-aside",
@@ -315,6 +316,8 @@ export default {
       modelConfig: {},  //模型菜单配置
       modelData: [],  //模型数据
       loadedModelEntities: [],  //已加载的模型实体
+      currentModelEditor: null,  //当前模型编辑器
+      lastHighlightedModel: null,  //最后高亮的模型
       saveConfigTimer: null,  //保存配置的防抖定时器
       lastSaveTime: 0,  //上次保存时间戳
       showTyphoon: false,  //是否显示台风可视化
@@ -525,6 +528,18 @@ export default {
       console.log('已加载的模型实体:', this.loadedModelEntities)
       console.log('isModelLoaded结果:', this.isModelLoaded(model.id))
 
+      // 清除之前的模型编辑器和高亮模型
+      if (this.currentModelEditor) {
+        if (this.currentModelEditor.destroy) {
+          this.currentModelEditor.destroy();
+        }
+        this.currentModelEditor = null;
+      }
+      if (this.lastHighlightedModel) {
+        this.restoreModelStyle(this.lastHighlightedModel);
+        this.lastHighlightedModel = null;
+      }
+
       if (this.isModelLoaded(model.id)) {
         console.log('模型已加载,先卸载再重新加载')
         this.removeModel(model)
@@ -680,6 +695,9 @@ export default {
 
         this.saveLoadedServices()
 
+        // 为模型添加点击事件,激活编辑器
+        this.addModelClickHandler(entity)
+
         // ElMessage.success(`模型 "${model.name}" 加载成功`)
         console.log('========== 模型加载完成 ==========')
       } catch (error) {
@@ -710,9 +728,33 @@ export default {
       const index = this.loadedModelEntities.findIndex(entity => entity.name === model.id)
       if (index !== -1) {
         const entity = this.loadedModelEntities[index]
+        
+        // 恢复模型样式
+        this.restoreModelStyle(entity);
+        
+        // 清理点击事件 handler
+        if (entity._clickHandler) {
+          entity._clickHandler.destroy();
+          entity._clickHandler = null;
+        }
+        
         viewer.entities.remove(entity)
         this.loadedModelEntities.splice(index, 1)
         this.saveLoadedServices()
+        
+        // 如果当前编辑器是这个模型,清理编辑器
+        if (this.currentModelEditor && this.currentModelEditor.entity === entity) {
+          if (this.currentModelEditor.destroy) {
+            this.currentModelEditor.destroy();
+          }
+          this.currentModelEditor = null;
+        }
+        
+        // 如果最后高亮的模型是这个模型,清除引用
+        if (this.lastHighlightedModel === entity) {
+          this.lastHighlightedModel = null;
+        }
+        
         // ElMessage.success(`模型 "${model.name}" 已删除`)
       }
     },
@@ -1729,6 +1771,167 @@ export default {
       localStorage.removeItem('cesium_loaded_services');
       localStorage.removeItem('cesium_loaded_models');
       console.log('持久化数据已清除');
+    },
+    
+    // 为模型添加点击事件,激活编辑器
+    addModelClickHandler(entity) {
+      if (!entity || !window.viewer) return;
+      
+      const handler = new Cesium.ScreenSpaceEventHandler(window.viewer.canvas);
+      
+      handler.setInputAction((movement) => {
+        const picked = window.viewer.scene.pick(movement.position);
+        
+        console.log('pick结果:', picked);
+        console.log('picked.id:', picked ? picked.id : null);
+        console.log('picked.id === entity:', picked ? (picked.id === entity) : false);
+        
+        if (Cesium.defined(picked) && picked.id === entity) {
+          console.log('点击了模型:', entity);
+          console.log('entity.model:', entity.model);
+          console.log('entity类型:', entity.constructor.name);
+          
+          // 先恢复上一个模型的样式
+          if (this.lastHighlightedModel && this.lastHighlightedModel !== entity) {
+            this.restoreModelStyle(this.lastHighlightedModel);
+          }
+          
+          // 高亮显示模型
+          this.highlightModel(entity);
+          this.lastHighlightedModel = entity;
+          
+          // 激活编辑器
+          this.activateModelEditor(entity);
+          
+          // 标记已选中模型
+          this._isModelSelected = true;
+        }
+      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
+      
+      // 保存 handler,以便后续清理
+      if (!entity._clickHandler) {
+        entity._clickHandler = handler;
+      }
+    },
+    
+    // 全局点击处理器 - 检测空白区域点击取消选中
+    initGlobalClickHandler() {
+      if (!window.viewer || this._globalClickHandlerInitialized) return;
+      
+      const handler = new Cesium.ScreenSpaceEventHandler(window.viewer.canvas);
+      
+      handler.setInputAction((movement) => {
+        const picked = window.viewer.scene.pick(movement.position);
+        
+        console.log('全局点击 - picked:', picked);
+        
+        // 检查是否点击了空白区域(不是模型)
+        // 如果没有 pick 到任何东西,或者 pick 到的东西不是当前选中的模型
+        let isClickingOnSelectedModel = false;
+        
+        if (picked && picked.id && this.lastHighlightedModel) {
+          // 检查是否点击的是当前选中的模型
+          if (picked.id === this.lastHighlightedModel || 
+              (typeof picked.id === 'string' && picked.id === this.lastHighlightedModel._id)) {
+            isClickingOnSelectedModel = true;
+          }
+        }
+        
+        // 如果点击的不是当前选中的模型,且有选中的模型,则取消选中
+        if (!isClickingOnSelectedModel && this.lastHighlightedModel) {
+          console.log('取消选中模型');
+          this.restoreModelStyle(this.lastHighlightedModel);
+          
+          // 销毁编辑器
+          if (this.currentModelEditor) {
+            this.currentModelEditor.destroy();
+            this.currentModelEditor = null;
+          }
+          
+          this.lastHighlightedModel = null;
+          this._isModelSelected = false;
+        }
+      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
+      
+      this._globalClickHandlerInitialized = true;
+      console.log('全局点击处理器已初始化');
+    },
+    
+    // 恢复模型样式
+    restoreModelStyle(entity) {
+      if (!entity) return;
+      
+      if (entity._originalShow !== undefined) {
+        entity.show = entity._originalShow;
+      }
+      if (entity.model) {
+        entity.model.color = entity._originalColor;
+        entity.model.colorBlendMode = entity._originalColorBlendMode;
+        entity.model.colorBlendAmount = entity._originalColorBlendAmount;
+      }
+    },
+    
+    // 高亮显示模型
+    highlightModel(entity) {
+      if (!entity) return;
+      
+      // 保存原始状态
+      if (!entity._originalShow) {
+        entity._originalShow = entity.show;
+      }
+      if (!entity._originalColor) {
+        entity._originalColor = entity.model ? entity.model.color : undefined;
+      }
+      if (!entity._originalColorBlendMode) {
+        entity._originalColorBlendMode = entity.model ? entity.model.colorBlendMode : undefined;
+      }
+      if (!entity._originalColorBlendAmount) {
+        entity._originalColorBlendAmount = entity.model ? entity.model.colorBlendAmount : undefined;
+      }
+      
+      // 高亮显示模型 - 使用透明模式
+      if (entity.model) {
+        console.log('模型存在,设置透明');
+        // 尝试直接设置颜色
+        entity.model.color = Cesium.Color.YELLOW;
+      } else {
+        console.log('模型不存在,实体结构:', entity);
+      }
+    },
+    
+    // 激活模型编辑器
+    activateModelEditor(entity) {
+      if (!entity || !window.viewer) return;
+      
+      // 清除之前的编辑器
+      if (this.currentModelEditor) {
+        if (this.currentModelEditor.destroy) {
+          this.currentModelEditor.destroy();
+        }
+        this.currentModelEditor = null;
+      }
+      
+      // 创建新的编辑器
+      try {
+        console.log('创建 ModelEditor for entity:', entity);
+        console.log('window.viewer:', window.viewer);
+        console.log('window.viewer.primitives:', window.viewer ? window.viewer.primitives : undefined);
+        this.currentModelEditor = new ModelEditor(window.viewer, entity);
+        
+        // 等待模型加载完成
+        if (this.currentModelEditor.waitForModelLoad) {
+          this.currentModelEditor.waitForModelLoad().then(() => {
+            console.log('模型加载完成,编辑器初始化成功');
+          }).catch((error) => {
+            console.error('模型加载或编辑器初始化失败:', error);
+          });
+        }
+        
+        console.log('ModelEditor 创建成功');
+      } catch (error) {
+        console.error('创建 ModelEditor 失败:', error);
+        this.currentModelEditor = null;
+      }
     }
   },
   
@@ -1746,15 +1949,20 @@ export default {
     // 直接初始化 viewer,不使用 window.onload
     this.$nextTick(() => {
       this.initViewer = "Sm3dViewer";
-      // 获取 viewer 实例
-      if (window.viewer) {
-        this.viewer = window.viewer;
-      }
       // 获取模型数据
       this.fetchModels();
       // 从数据库加载自定义服务
       this.fetchCustomServices();
     });
+    
+    // 延迟初始化全局点击处理器,等待 viewer 准备好
+    setTimeout(() => {
+      console.log('延迟初始化全局点击处理器');
+      if (window.viewer) {
+        this.viewer = window.viewer;
+        this.initGlobalClickHandler();
+      }
+    }, 3000);
   }
 };
 </script>

+ 3 - 3
ruoyi-admin/src/main/resources/application.yml

@@ -57,10 +57,10 @@ spring:
   # 文件上传
   servlet:
     multipart:
-      # 单个文件大小(三维模型文件通常较大,设置为100MB)
-      max-file-size: 100MB
+      # 单个文件大小(三维模型文件通常较大,设置为1GB)
+      max-file-size: 1GB
       # 设置总上传的文件大小
-      max-request-size: 200MB
+      max-request-size: 2GB
   # 服务模块
   devtools:
     restart:

+ 3 - 3
ruoyi-admin/target/classes/application.yml

@@ -57,10 +57,10 @@ spring:
   # 文件上传
   servlet:
     multipart:
-      # 单个文件大小(三维模型文件通常较大,设置为100MB)
-      max-file-size: 100MB
+      # 单个文件大小(三维模型文件通常较大,设置为1GB)
+      max-file-size: 1GB
       # 设置总上传的文件大小
-      max-request-size: 200MB
+      max-request-size: 2GB
   # 服务模块
   devtools:
     restart:

BIN
ruoyi-admin/target/classes/com/ruoyi/RuoYiApplication.class


BIN
ruoyi-admin/target/classes/com/ruoyi/RuoYiServletInitializer.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/cesium/CesiumMapConfigController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CaptchaController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CommonController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/CacheController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/ServerController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysLogininforController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysOperlogController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysUserOnlineController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysConfigController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDeptController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictDataController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictTypeController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysIndexController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysLoginController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysMenuController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysNoticeController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysPostController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysProfileController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRegisterController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRoleController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysUserController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/TestController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/UserEntity.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/watershed/WatershedModelController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/watershed/WatershedServiceController.class


BIN
ruoyi-admin/target/classes/com/ruoyi/web/core/config/SwaggerConfig.class


BIN
ruoyi-admin/target/ruoyi-admin.jar


BIN
ruoyi-admin/target/ruoyi-admin.jar.original


BIN
ruoyi-admin/uploads/models/2026/04/09/启闭机_20260409151853A006.glb


BIN
ruoyi-admin/uploads/models/2026/04/09/启闭机_20260409162511A009.glb


BIN
ruoyi-admin/uploads/models/2026/04/09/平望场景_20260409162416A008.glb


BIN
ruoyi-admin/uploads/models/2026/04/09/平望场景_20260409165637A001.glb


BIN
ruoyi-admin/uploads/models/2026/04/10/平望场景_20260410134234A003.glb


+ 2 - 2
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java

@@ -24,9 +24,9 @@ import com.ruoyi.common.utils.uuid.Seq;
 public class FileUploadUtils
 {
     /**
-     * 默认大小 50M
+     * 默认大小 1G
      */
-    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L;
+    public static final long DEFAULT_MAX_SIZE = 1024 * 1024 * 1024L;
 
     /**
      * 默认的文件名最大长度 100

BIN
ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$ColumnType.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$Type.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/config/RuoYiConfig.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/constant/CacheConstants.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/constant/Constants.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/constant/GenConstants.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/constant/HttpStatus.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants$Status.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/constant/UserConstants.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController$1.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/AjaxResult.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/BaseEntity.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/R.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeEntity.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeSelect.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDept.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictData.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictType.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysMenu.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysRole.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysUser.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginBody.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginUser.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/RegisterBody.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/page/PageDomain.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/page/TableDataInfo.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/page/TableSupport.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/redis/RedisCache.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/text/CharsetKit.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/text/Convert.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/core/text/StrFormatter.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessStatus.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessType.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/enums/DataSourceType.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/enums/DesensitizedType.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/enums/HttpMethod.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/enums/LimitType.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/enums/OperatorType.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/enums/UserStatus.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/DemoModeException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/GlobalException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/ServiceException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/UtilException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/base/BaseException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileSizeLimitExceededException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileUploadException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidImageExtensionException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class


BIN
ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException.class


Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä