瀏覽代碼

添加水闸场景

WQQ 13 小時之前
父節點
當前提交
b820455b66
共有 73 個文件被更改,包括 2307 次插入92 次删除
  1. 二進制
      RuoYi-Vue3/public/models/莒口水闸3.glb
  2. 633 0
      RuoYi-Vue3/src/supermap-cesium-module/business-scenes/JuKouShuiZhaScene.js
  3. 508 0
      RuoYi-Vue3/src/supermap-cesium-module/components/gate-control/GateControl.vue
  4. 6 0
      RuoYi-Vue3/src/supermap-cesium-module/components/model-transform/model-transform-panel.vue
  5. 60 0
      RuoYi-Vue3/src/supermap-cesium-module/config/fixedScenes.js
  6. 673 92
      RuoYi-Vue3/src/supermap-cesium-module/views/layout/aside.vue
  7. 7 0
      ruoyi-admin/pom.xml
  8. 59 0
      ruoyi-admin/src/main/resources/application-h2.yml
  9. 361 0
      ruoyi-admin/src/main/resources/sql/schema-h2.sql
  10. 二進制
      ruoyi-admin/target/classes/com/ruoyi/RuoYiApplication.class
  11. 二進制
      ruoyi-admin/target/classes/com/ruoyi/RuoYiServletInitializer.class
  12. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/cesium/CesiumMapConfigController.class
  13. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CaptchaController.class
  14. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CommonController.class
  15. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/CacheController.class
  16. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/ServerController.class
  17. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysLogininforController.class
  18. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysOperlogController.class
  19. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysUserOnlineController.class
  20. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysConfigController.class
  21. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDeptController.class
  22. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictDataController.class
  23. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictTypeController.class
  24. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysIndexController.class
  25. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysLoginController.class
  26. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysMenuController.class
  27. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysNoticeController.class
  28. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysPostController.class
  29. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysProfileController.class
  30. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRegisterController.class
  31. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRoleController.class
  32. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysUserController.class
  33. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/TestController.class
  34. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/UserEntity.class
  35. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/watershed/WatershedModelController.class
  36. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/controller/watershed/WatershedServiceController.class
  37. 二進制
      ruoyi-admin/target/classes/com/ruoyi/web/core/config/SwaggerConfig.class
  38. 二進制
      ruoyi-admin/uploads/models/2026/06/12/xinjiangdiban_20260612184229A001.glb
  39. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/CesiumGeojson.class
  40. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/CesiumMapConfig.class
  41. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/SysCache.class
  42. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/SysConfig.class
  43. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/SysLogininfor.class
  44. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/SysNotice.class
  45. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/SysOperLog.class
  46. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/SysPost.class
  47. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleDept.class
  48. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleMenu.class
  49. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserOnline.class
  50. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserPost.class
  51. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserRole.class
  52. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/WatershedEquipment.class
  53. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/WatershedFacility.class
  54. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/WatershedService.class
  55. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/vo/MetaVo.class
  56. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/domain/vo/RouterVo.class
  57. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/CesiumGeojsonServiceImpl.class
  58. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/CesiumMapConfigServiceImpl.class
  59. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysConfigServiceImpl.class
  60. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDeptServiceImpl.class
  61. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictDataServiceImpl.class
  62. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.class
  63. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysLogininforServiceImpl.class
  64. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysMenuServiceImpl.class
  65. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysNoticeServiceImpl.class
  66. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysOperLogServiceImpl.class
  67. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysPostServiceImpl.class
  68. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysRoleServiceImpl.class
  69. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.class
  70. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserServiceImpl.class
  71. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/WatershedEquipmentServiceImpl.class
  72. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/WatershedModelServiceImpl.class
  73. 二進制
      ruoyi-system/target/classes/com/ruoyi/system/service/impl/WatershedServiceServiceImpl.class

二進制
ruoyi-admin/uploads/models/2026/05/07/场景1_20260507165507A004.glb → RuoYi-Vue3/public/models/莒口水闸3.glb


+ 633 - 0
RuoYi-Vue3/src/supermap-cesium-module/business-scenes/JuKouShuiZhaScene.js

@@ -0,0 +1,633 @@
+/**
+ * 莒口水闸业务场景模块
+ * 包含闸门子节点提取、闸门动画控制等核心功能
+ */
+
+// 闸门子节点名称列表
+export const GATE_NODE_NAMES = [
+  'gate_01', 'gate_02', 'gate_03', 'gate_04', 'gate_05',
+  'gate_06', 'gate_07', 'gate_08', 'gate_09', 'gate_10',
+  'gate_11', 'gate_12', 'gate_13', 'gate_14', 'gate_15'
+];
+
+// 最大闸门高度(米)
+const MAX_GATE_HEIGHT = 5;
+
+/**
+ * 提取闸门子节点
+ * @param {Object} modelPrimitive - Cesium模型图元
+ * @param {Array} gateNames - 闸门名称列表
+ * @returns {Object} - 闸门节点映射 { gateName: node }
+ */
+export function extractGateNodes(modelPrimitive, gateNames = GATE_NODE_NAMES) {
+  const gateNodes = {};
+  const allNodes = {};
+  
+  console.log('========== 开始解析莒口水闸模型组成部分 ==========');
+  console.log('传入的 modelPrimitive:', modelPrimitive);
+  console.log('modelPrimitive 类型:', modelPrimitive?.constructor?.name);
+  
+  if (!modelPrimitive) {
+    console.error('❌ 模型图元未定义');
+    console.log('====================================================');
+    return gateNodes;
+  }
+
+  console.log('模型对象结构检查:');
+  console.log('  _gltf:', modelPrimitive._gltf !== undefined);
+  if (modelPrimitive._gltf) {
+    console.log('  _gltf.nodes:', modelPrimitive._gltf.nodes !== undefined);
+    console.log('  _gltf.nodes 数量:', modelPrimitive._gltf.nodes ? Object.keys(modelPrimitive._gltf.nodes).length : 0);
+  }
+  console.log('  _model:', modelPrimitive._model !== undefined);
+  if (modelPrimitive._model) {
+    console.log('  _model._gltf:', modelPrimitive._model._gltf !== undefined);
+    if (modelPrimitive._model._gltf) {
+      console.log('  _model._gltf.nodes:', modelPrimitive._model._gltf.nodes !== undefined);
+      console.log('  _model._gltf.nodes 数量:', modelPrimitive._model._gltf.nodes ? Object.keys(modelPrimitive._model._gltf.nodes).length : 0);
+    }
+  }
+  console.log('  _runtimeNodes:', modelPrimitive._runtimeNodes !== undefined);
+  console.log('  _nodeRuntimeReferences:', modelPrimitive._nodeRuntimeReferences !== undefined);
+  
+  // 获取 GLTF 数据(尝试多个位置)
+  let gltf = modelPrimitive._gltf;
+  
+  // 如果当前对象没有,尝试从 _model 获取
+  if (!gltf && modelPrimitive._model) {
+    console.log('🔍 尝试从 modelPrimitive._model 获取 GLTF');
+    gltf = modelPrimitive._model._gltf;
+  }
+  
+  // 如果还是没有,尝试从内部结构获取
+  if (!gltf && modelPrimitive._sourceChapters) {
+    console.log('🔍 尝试从 modelPrimitive._sourceChapters 获取');
+    for (const chapter of modelPrimitive._sourceChapters) {
+      if (chapter._gltf) {
+        gltf = chapter._gltf;
+        break;
+      }
+    }
+  }
+  
+  console.log('最终 gltf:', gltf !== undefined);
+  if (gltf) {
+    console.log('最终 gltf.nodes:', gltf.nodes !== undefined);
+    console.log('最终 gltf.nodes 数量:', gltf.nodes ? Object.keys(gltf.nodes).length : 0);
+  }
+  
+  // 方法1: 从运行时节点获取(优先使用,因为包含 nodeIndex,用于 setNodeMatrix)
+  const runtimeNodes = modelPrimitive._runtimeNodes || (modelPrimitive._model && modelPrimitive._model._runtimeNodes);
+  if (runtimeNodes && Array.isArray(runtimeNodes)) {
+    console.log('✅ 优先从运行时节点获取信息...');
+    for (let i = 0; i < runtimeNodes.length; i++) {
+      const node = runtimeNodes[i];
+      if (node && node.name) {
+        allNodes[node.name] = { node, path: node.name, modelPrimitive, nodeIndex: i };
+        console.log(`  ├─ 运行时节点: ${node.name} (索引: ${i})`);
+        if (node.name.startsWith('gate_')) {
+          gateNodes[node.name] = { node, path: node.name, modelPrimitive, nodeIndex: i };
+          console.log(`     └─ ✅ 识别为闸门节点`);
+        }
+      }
+    }
+  }
+
+  // 方法2: 从 gltf.nodes 遍历(补充获取路径信息)
+  if (gltf && gltf.nodes) {
+    console.log('✅ 从 GLTF 数据补充节点路径信息...');
+    console.log('GLTF 节点总数:', Object.keys(gltf.nodes).length);
+    
+    const nodeMap = new Map();
+    for (const key in gltf.nodes) {
+      const node = gltf.nodes[key];
+      nodeMap.set(key, node);
+      if (node.name) {
+        nodeMap.set(node.name, node);
+      }
+    }
+    
+    const traverseGltfNode = (node, parentPath = '') => {
+      const nodePath = parentPath ? `${parentPath}/${node.name || 'unnamed'}` : (node.name || 'unnamed');
+      
+      if (node.name) {
+        // 如果已经有运行时节点数据,补充路径信息
+        if (allNodes[node.name]) {
+          allNodes[node.name].path = nodePath;
+          if (gateNodes[node.name]) {
+            gateNodes[node.name].path = nodePath;
+          }
+        } else {
+          allNodes[node.name] = { node, path: nodePath };
+        }
+        console.log(`  ├─ 节点: ${nodePath}`);
+        
+        // 如果运行时节点中没有找到闸门,使用 GLTF 节点作为后备
+        if (node.name.startsWith('gate_') && !gateNodes[node.name]) {
+          gateNodes[node.name] = { node, path: nodePath, modelPrimitive };
+          if (node.translation) {
+            node.originOffset = [...node.translation];
+          }
+        }
+      }
+      
+      if (node.children && node.children.length > 0) {
+        node.children.forEach(childKey => {
+          const child = typeof childKey === 'number' ? gltf.nodes[childKey] : nodeMap.get(childKey);
+          if (child) {
+            traverseGltfNode(child, nodePath);
+          }
+        });
+      }
+    };
+    
+    if (gltf.scenes && gltf.scenes[0] && gltf.scenes[0].nodes && gltf.scenes[0].nodes.length > 0) {
+      console.log('从场景根节点开始遍历...');
+      gltf.scenes[0].nodes.forEach(rootNodeKey => {
+        const rootNode = gltf.nodes[rootNodeKey];
+        if (rootNode) {
+          traverseGltfNode(rootNode);
+        }
+      });
+    }
+  } else {
+    console.log('⚠️ 未找到 GLTF 数据');
+  }
+
+  // 方法3: 从 _nodeRuntimeReferences 获取
+  const nodeRefs = modelPrimitive._nodeRuntimeReferences || (modelPrimitive._model && modelPrimitive._model._nodeRuntimeReferences);
+  if (nodeRefs) {
+    console.log('✅ 尝试从 _nodeRuntimeReferences 获取信息...');
+    if (nodeRefs.forEach) {
+      nodeRefs.forEach((runtimeNode, nodeName) => {
+        if (!allNodes[nodeName]) {
+          allNodes[nodeName] = { node: runtimeNode, path: nodeName };
+          console.log(`  ├─ 引用节点: ${nodeName}`);
+          if (!gateNodes[nodeName] && nodeName.startsWith('gate_')) {
+            gateNodes[nodeName] = { node: runtimeNode, path: nodeName };
+          }
+        }
+      });
+    }
+  }
+
+  // 方法4: 尝试从 model 实例的其他属性获取
+  if (modelPrimitive.nodes) {
+    console.log('✅ 尝试从 model.nodes 获取信息...');
+    for (const key in modelPrimitive.nodes) {
+      const node = modelPrimitive.nodes[key];
+      if (node && node.name && !allNodes[node.name]) {
+        allNodes[node.name] = { node, path: node.name };
+        console.log(`  ├─ 模型节点: ${node.name}`);
+      }
+    }
+  }
+
+  // 输出最终统计结果
+  const allNodeNames = Object.keys(allNodes);
+  const gateNodeNames = Object.keys(gateNodes);
+  
+  console.log('');
+  console.log('========== 模型组成部分统计 ==========');
+  console.log(`总节点数: ${allNodeNames.length}`);
+  console.log(`闸门节点数: ${gateNodeNames.length}`);
+  console.log('--------------------------------------');
+  console.log('所有节点名称:');
+  if (allNodeNames.length > 0) {
+    allNodeNames.forEach((name, index) => {
+      const isGate = name.startsWith('gate_') ? ' 🚪' : '';
+      console.log(`  ${index + 1}. ${name}${isGate}`);
+    });
+  } else {
+    console.log('  (未找到任何节点)');
+  }
+  console.log('--------------------------------------');
+  if (gateNodeNames.length > 0) {
+    console.log('闸门节点(gate_开头):');
+    gateNodeNames.forEach((name, index) => {
+      console.log(`  ${index + 1}. ${name}`);
+    });
+  }
+  console.log('======================================');
+
+  if (allNodeNames.length === 0) {
+    console.warn('⚠️ 未找到任何节点,返回模拟数据用于测试');
+    console.log('   模型图元类型:', modelPrimitive?.constructor?.name);
+    console.log('   runtimeNodes 存在:', runtimeNodes !== undefined);
+    console.log('   runtimeNodes 是数组:', Array.isArray(runtimeNodes));
+    console.log('   gltf.nodes 存在:', gltf?.nodes !== undefined);
+    console.log('   gltf.nodes 数量:', gltf?.nodes ? Object.keys(gltf.nodes).length : 0);
+    
+    const mockNodes = {};
+    GATE_NODE_NAMES.forEach((name, index) => {
+      mockNodes[name] = { 
+        node: { 
+          name, 
+          translation: [0, 0, 0], 
+          matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
+          _modelMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
+        },
+        path: name,
+        nodeIndex: index,
+        modelPrimitive: modelPrimitive
+      };
+    });
+    console.log('返回的模拟闸门节点:', Object.keys(mockNodes));
+    return mockNodes;
+  }
+
+  return Object.keys(gateNodes).length > 0 ? gateNodes : allNodes;
+}
+
+/**
+ * 动画单个闸门节点
+ * @param {Object} gateNode - 闸门节点
+ * @param {Number} position - 开度百分比 (0-100)
+ */
+export function animateGateNode(gateNode, position) {
+  if (!gateNode) return;
+
+  // 用户需求:开100%时在最高位置,关0%时向下移动5米
+  // 所以 position=100% 时,height=0(不移动)
+  // position=0% 时,height=5米(向下移动5米)
+  const height = ((100 - position) / 100) * MAX_GATE_HEIGHT;
+  console.log(`🎢 移动闸门 ${gateNode.name || gateNode.node?.name} 到位置 ${position}% (向下移动: ${height}米)`);
+
+  // 获取实际的节点对象
+  const actualNode = gateNode.node || gateNode;
+  
+  // 方式1: 优先使用 Cesium Model 的 setNodeMatrix 方法(唯一能真正触发渲染更新的方式)
+  if (gateNode.modelPrimitive && gateNode.nodeIndex !== undefined) {
+    const model = gateNode.modelPrimitive;
+    console.log(`   方式1: 尝试使用 model.setNodeMatrix - nodeIndex: ${gateNode.nodeIndex}`);
+    
+    if (typeof model.getNodeMatrix === 'function' && typeof model.setNodeMatrix === 'function') {
+      if (!gateNode.originNodeMatrix) {
+        gateNode.originNodeMatrix = model.getNodeMatrix(gateNode.nodeIndex);
+        console.log(`   初始化 originNodeMatrix from model`);
+      }
+      
+      const newMatrix = Cesium.Matrix4.clone(gateNode.originNodeMatrix);
+      const translation = Cesium.Cartesian3.fromArray([0, 0, height]);
+      Cesium.Matrix4.multiplyByTranslation(newMatrix, translation, newMatrix);
+      model.setNodeMatrix(gateNode.nodeIndex, newMatrix);
+      console.log(`   ✅ 使用 model.setNodeMatrix 移动成功,Z轴偏移: ${height}米`);
+      
+      // 强制触发场景渲染
+      if (window.viewer && window.viewer.scene) {
+        window.viewer.scene.requestRender();
+      }
+      return;
+    } else {
+      console.log(`   ❌ model 没有 getNodeMatrix/setNodeMatrix 方法,尝试其他方式`);
+    }
+  } else {
+    console.log(`   ⚠️ modelPrimitive 或 nodeIndex 缺失,尝试其他方式`);
+    console.log(`   gateNode.modelPrimitive:`, gateNode.modelPrimitive);
+    console.log(`   gateNode.nodeIndex:`, gateNode.nodeIndex);
+  }
+
+  // 方式2: 尝试使用运行时节点的 _modelMatrix 属性
+  if (actualNode._modelMatrix) {
+    if (!gateNode.originModelMatrix) {
+      gateNode.originModelMatrix = [...actualNode._modelMatrix];
+      console.log(`   初始化 originModelMatrix`);
+    }
+    const newMatrix = [...gateNode.originModelMatrix];
+    newMatrix[14] = gateNode.originModelMatrix[14] + height;
+    actualNode._modelMatrix = newMatrix;
+    console.log(`   ✅ 使用 _modelMatrix 属性移动`);
+    
+    // 强制触发渲染更新
+    if (gateNode.modelPrimitive && gateNode.modelPrimitive._runtimeNodes) {
+      gateNode.modelPrimitive._runtimeNodes[gateNode.nodeIndex] = actualNode;
+    }
+    
+    // 强制触发场景渲染
+    if (window.viewer && window.viewer.scene) {
+      window.viewer.scene.requestRender();
+    }
+    return;
+  }
+
+  // 方式3: 尝试使用运行时节点的 matrix 属性
+  if (actualNode.matrix) {
+    if (!gateNode.originMatrix) {
+      gateNode.originMatrix = [...actualNode.matrix];
+      console.log(`   初始化 originMatrix`);
+    }
+    const newMatrix = [...gateNode.originMatrix];
+    newMatrix[14] = gateNode.originMatrix[14] + height; // 修改 Z 分量
+    actualNode.matrix = newMatrix;
+    console.log(`   ✅ 使用 matrix 属性移动`);
+    
+    // 强制触发场景渲染
+    if (window.viewer && window.viewer.scene) {
+      window.viewer.scene.requestRender();
+    }
+    return;
+  }
+
+  // 方式4: 尝试使用 GLTF 节点的 translation 属性(后备方案)
+  if (actualNode.translation && Array.isArray(actualNode.translation)) {
+    if (!gateNode.originOffset) {
+      gateNode.originOffset = [...actualNode.translation];
+      console.log(`   初始化 originOffset:`, gateNode.originOffset);
+    }
+    
+    actualNode.translation = [
+      gateNode.originOffset[0],
+      gateNode.originOffset[1],
+      gateNode.originOffset[2] + height
+    ];
+    console.log(`   ⚠️ 使用 translation 属性移动(可能无法实时渲染更新)`);
+    
+    // 强制触发场景渲染
+    if (window.viewer && window.viewer.scene) {
+      window.viewer.scene.requestRender();
+    }
+    return;
+  }
+
+  console.warn('⚠️ 未找到可用的位置控制属性:', gateNode.name || gateNode.node?.name);
+  console.log('   gateNode 结构:', JSON.stringify(Object.keys(gateNode)));
+  if (gateNode.node) {
+    console.log('   gateNode.node 结构:', JSON.stringify(Object.keys(gateNode.node)));
+  }
+}
+
+// 测试函数:检查闸门节点结构
+export function testGateNodeStructure(gateNode) {
+  console.log('🔍 测试闸门节点结构:', gateNode.name);
+  console.log('   gateNode:', gateNode);
+  console.log('   gateNode.node:', gateNode.node);
+  console.log('   gateNode.translation:', gateNode.translation);
+  console.log('   gateNode.matrix:', gateNode.matrix);
+  console.log('   gateNode._modelMatrix:', gateNode._modelMatrix);
+  console.log('   gateNode.modelPrimitive:', gateNode.modelPrimitive);
+  console.log('   gateNode.nodeIndex:', gateNode.nodeIndex);
+}
+
+/**
+ * 动画所有闸门节点
+ * @param {Object} gateNodes - 闸门节点映射
+ * @param {Number} position - 开度百分比 (0-100)
+ */
+export function animateAllGateNodes(gateNodes, position) {
+  for (const gateName of Object.keys(gateNodes)) {
+    const gateNode = gateNodes[gateName];
+    animateGateNode(gateNode, position);
+  }
+}
+
+export function animateSingleGate(gateNodes, gateName, position) {
+  if (!gateNodes || !gateNodes[gateName]) {
+    console.warn('未找到闸门:', gateName);
+    return;
+  }
+  animateGateNode(gateNodes[gateName], position);
+}
+
+/**
+ * 场景配置
+ */
+export const JuKouSceneConfig = {
+  sceneId: 'fixed_jushui_gate',
+  sceneName: '莒口水闸',
+  disableModelTransform: true,
+  gateNodePrefix: 'gate_',
+  gateNodeNames: GATE_NODE_NAMES,
+  initialGatePosition: 0
+};
+
+/**
+ * 场景初始化钩子
+ * 在场景加载完成后调用
+ * @param {Object} entity - 场景实体
+ * @param {Function} onGateNodesReady - 闸门节点就绪回调
+ */
+export function onSceneLoaded(entity, onGateNodesReady) {
+  console.log('========== onSceneLoaded 被调用 ==========');
+  
+  if (!entity) {
+    console.error('❌ 场景实体未定义');
+    console.log('============================================');
+    return;
+  }
+
+  console.log('场景实体类型:', entity.constructor?.name);
+  console.log('场景实体名称:', entity.name);
+  
+  const Cesium = window.Cesium;
+  let modelPrimitive = null;
+  
+  // 方式1: 从 entity.model 获取(Property 对象)
+  if (entity.model) {
+    console.log('🔍 尝试方式1: entity.model');
+    if (typeof entity.model.getValue === 'function') {
+      console.log('   → entity.model 是 Property 对象,尝试获取实际值');
+      const time = Cesium?.JulianDate.now?.();
+      const actualModel = entity.model.getValue(time);
+      if (actualModel) {
+        modelPrimitive = actualModel;
+        console.log('   ✅ 获取到实际模型:', actualModel.constructor?.name);
+        console.log('   model._model:', actualModel._model !== undefined);
+        console.log('   model._gltf:', actualModel._gltf !== undefined);
+      }
+    } else if (entity.model._model) {
+      modelPrimitive = entity.model._model;
+      console.log('   ✅ 从 entity.model._model 获取:', modelPrimitive.constructor?.name);
+    }
+  }
+  
+  // 方式2: 从 entity._model 获取
+  if (!modelPrimitive && entity._model) {
+    console.log('🔍 尝试方式2: entity._model');
+    modelPrimitive = entity._model;
+    console.log('   ✅ 获取到模型:', modelPrimitive.constructor?.name);
+  }
+  
+  // 方式3: 检查 modelPrimitive._model(Cesium Model 的内部结构)
+  if (modelPrimitive && modelPrimitive._model && !modelPrimitive._gltf) {
+    console.log('🔍 尝试方式3: modelPrimitive._model');
+    modelPrimitive = modelPrimitive._model;
+    console.log('   ✅ 使用内部 _model 对象:', modelPrimitive.constructor?.name);
+  }
+  
+  // 方式4: 从 viewer.scene.primitives 查找(使用不同的方法)
+  if (!modelPrimitive || !modelPrimitive._gltf) {
+    console.log('🔍 尝试方式4: 遍历 viewer.scene.primitives');
+    if (window.viewer && window.viewer.scene && window.viewer.scene.primitives) {
+      const primitives = window.viewer.scene.primitives;
+      console.log('   primitives 数量:', primitives.length);
+      
+      // 获取模型文件名用于匹配
+      let targetFileName = '莒口水闸3';
+      
+      for (let i = 0; i < primitives.length; i++) {
+        const primitive = primitives.get(i);
+        
+        // 检查是否是模型
+        if (primitive && primitive.constructor && primitive.constructor.name) {
+          console.log(`   [${i}] ${primitive.constructor.name}: ready=${primitive.ready}`);
+          
+          // 详细检查 primitive 的所有属性
+          console.log(`   [${i}] 检查属性:`);
+          console.log(`       _gltf: ${primitive._gltf !== undefined}`);
+          console.log(`       _model: ${primitive._model !== undefined}`);
+          console.log(`       _model._gltf: ${primitive._model && primitive._model._gltf !== undefined}`);
+          console.log(`       _runtimeNodes: ${primitive._runtimeNodes !== undefined}`);
+          console.log(`       _nodeRuntimeReferences: ${primitive._nodeRuntimeReferences !== undefined}`);
+          
+          // 尝试获取 GLTF 数据
+          let gltfData = primitive._gltf || (primitive._model && primitive._model._gltf);
+          
+          // 如果还没找到,尝试深度搜索
+          if (!gltfData) {
+            const findGltfInObj = (obj, depth = 0) => {
+              if (depth > 3) return null;
+              if (!obj || typeof obj !== 'object') return null;
+              
+              if (obj._gltf) return obj._gltf;
+              if (obj.nodes && typeof obj.nodes === 'object') return { nodes: obj.nodes };
+              
+              for (const key of Object.keys(obj)) {
+                if (key.startsWith('_') && typeof obj[key] === 'object') {
+                  const result = findGltfInObj(obj[key], depth + 1);
+                  if (result) return result;
+                }
+              }
+              return null;
+            };
+            gltfData = findGltfInObj(primitive);
+          }
+          
+          if (gltfData) {
+            const url = primitive._url || primitive._uri || (primitive._model && primitive._model._url);
+            console.log(`   [${i}] ✅ 发现 GLTF 数据!URL:`, url ? url.split('/').pop() : '未知');
+            console.log(`   [${i}] 节点数:`, gltfData.nodes ? Object.keys(gltfData.nodes).length : 0);
+            
+            if (!url || (url && url.includes(targetFileName))) {
+              modelPrimitive = primitive._model || primitive;
+              // 如果 primitive 本身没有 _gltf,但找到了 gltfData,挂载上去
+              if (!modelPrimitive._gltf && gltfData) {
+                modelPrimitive._gltf = gltfData;
+              }
+              console.log('   ✅ 找到匹配的模型!');
+              break;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  // 方式5: 尝试从 entity 的其他属性获取
+  if (!modelPrimitive || !modelPrimitive._gltf) {
+    console.log('🔍 尝试方式5: 检查 entity 的其他属性');
+    console.log('   entity._properties:', entity._properties !== undefined);
+    console.log('   entity._primitive:', entity._primitive !== undefined);
+    if (entity._primitive) {
+      modelPrimitive = entity._primitive;
+      console.log('   ✅ 使用 entity._primitive');
+    }
+  }
+
+  if (!modelPrimitive) {
+    console.error('❌ 无法获取模型图元');
+    console.log('============================================');
+    return;
+  }
+  
+  console.log('✅ 成功获取模型图元');
+  console.log('模型类型:', modelPrimitive.constructor?.name);
+  console.log('ready状态:', modelPrimitive.ready);
+  console.log('_gltf已加载:', modelPrimitive._gltf !== undefined);
+  console.log('_model._gltf已加载:', modelPrimitive._model && modelPrimitive._model._gltf !== undefined);
+  
+  // 如果 modelPrimitive 本身没有 _gltf,但 _model 有,则使用 _model
+  if (!modelPrimitive._gltf && modelPrimitive._model && modelPrimitive._model._gltf) {
+    console.log('🔄 切换到 modelPrimitive._model 获取 GLTF 数据');
+    modelPrimitive = modelPrimitive._model;
+  }
+  
+  // 方法6: 尝试深度搜索 GLTF 数据(调试用)
+  if (!modelPrimitive._gltf) {
+    console.log('🔍 尝试深度搜索 GLTF 数据...');
+    const findGltfRecursively = (obj, path = 'obj', depth = 0) => {
+      if (depth > 5) return null;
+      if (!obj || typeof obj !== 'object') return null;
+      
+      // 检查当前对象是否有 _gltf 或 nodes
+      if (obj._gltf) {
+        console.log(`   ✅ 在 ${path}._gltf 找到 GLTF 数据!`);
+        return obj._gltf;
+      }
+      if (obj.nodes && typeof obj.nodes === 'object') {
+        console.log(`   ✅ 在 ${path}.nodes 找到节点数据!`);
+        return { nodes: obj.nodes };
+      }
+      
+      // 递归搜索
+      for (const key of Object.keys(obj)) {
+        if (key.startsWith('_') || typeof obj[key] === 'object') {
+          const result = findGltfRecursively(obj[key], `${path}.${key}`, depth + 1);
+          if (result) return result;
+        }
+      }
+      return null;
+    };
+    
+    const foundGltf = findGltfRecursively(modelPrimitive, 'modelPrimitive');
+    if (foundGltf) {
+      // 将找到的 GLTF 数据挂载到 modelPrimitive 上以便后续使用
+      modelPrimitive._gltf = foundGltf;
+      console.log('✅ 成功挂载 GLTF 数据到 modelPrimitive._gltf');
+    } else {
+      console.log('❌ 深度搜索也未找到 GLTF 数据');
+    }
+  }
+  
+  const checkModelReady = () => {
+    const hasGltf = modelPrimitive._gltf !== undefined;
+    const hasNodes = modelPrimitive._gltf && modelPrimitive._gltf.nodes !== undefined;
+    const isReady = modelPrimitive.ready === true;
+    
+    console.log(`⏳ 检查模型状态: ready=${isReady}, _gltf=${hasGltf}, hasNodes=${hasNodes}`);
+    
+    if (hasNodes) {
+      console.log('✅ 模型已就绪,开始提取闸门节点...');
+      const gateNodes = extractGateNodes(modelPrimitive, GATE_NODE_NAMES);
+      entity.gateNodes = gateNodes;
+      
+      if (typeof onGateNodesReady === 'function') {
+        onGateNodesReady(gateNodes);
+      }
+      
+    } else if (isReady && !hasGltf) {
+      console.log('⏳ 模型就绪但 _gltf 未加载,等待中...');
+      setTimeout(checkModelReady, 200);
+      
+    } else if (hasGltf && !hasNodes) {
+      console.log('⏳ _gltf 存在但 nodes 未加载,等待中...');
+      setTimeout(checkModelReady, 200);
+      
+    } else {
+      console.log('⏳ 模型未就绪,等待中...');
+      setTimeout(checkModelReady, 200);
+    }
+  };
+  
+  checkModelReady();
+}
+
+/**
+ * 场景卸载钩子
+ * 在场景卸载前调用
+ * @param {Object} entity - 场景实体
+ */
+export function onSceneUnloaded(entity) {
+  if (entity && entity.gateNodes) {
+    delete entity.gateNodes;
+  }
+}

+ 508 - 0
RuoYi-Vue3/src/supermap-cesium-module/components/gate-control/GateControl.vue

@@ -0,0 +1,508 @@
+<template>
+  <div v-if="visible" id="gate-control-panel" class="sm-panel gate-control-panel">
+    <div class="sm-panel-header">
+      <span class="panel-title">闸门启闭控制</span>
+      <el-icon 
+        class="close-icon" 
+        @click="$emit('close')"
+        title="关闭"
+      >
+        <CircleClose />
+      </el-icon>
+    </div>
+
+    <div class="gate-control-content">
+      <!-- 全部控制 -->
+      <div class="all-control-section">
+        <div class="all-control-label">全部闸门控制</div>
+        <div class="all-control-buttons">
+          <el-button 
+            type="primary" 
+            size="small"
+            :disabled="isAnyAnimating"
+            @click="openAllGates"
+            class="all-control-btn"
+          >
+            <el-icon><ArrowUp /></el-icon>
+            全部开启
+          </el-button>
+          <el-button 
+            type="danger" 
+            size="small"
+            :disabled="isAnyAnimating"
+            @click="closeAllGates"
+            class="all-control-btn"
+          >
+            <el-icon><ArrowDown /></el-icon>
+            全部关闭
+          </el-button>
+        </div>
+      </div>
+
+      <!-- 闸门列表 -->
+      <div class="gates-section">
+        <div class="gates-label">闸门列表 (共 {{ gates.length }} 个)</div>
+        <div v-if="gates.length === 0" style="color: #999; padding: 20px; text-align: center;">
+          暂无闸门数据
+        </div>
+        <div class="gates-list">
+          <div 
+            v-for="gate in gates" 
+            :key="gate.name" 
+            class="gate-item"
+          >
+            <div class="gate-header">
+              <span class="gate-name">{{ gate.name }}</span>
+              <span class="gate-status" :class="getGateStatusClass(gate.position)">
+                {{ getGateStatusText(gate.position) }}
+              </span>
+            </div>
+            <div class="gate-position">
+              <span class="gate-position-value">{{ gate.position }}%</span>
+            </div>
+            <el-slider
+              v-model="gate.sliderValue"
+              :min="0"
+              :max="100"
+              :step="1"
+              :disabled="gate.isAnimating"
+              @change="(value) => handleGateSliderChange(gate, value)"
+              class="gate-slider"
+            />
+            <div class="gate-controls">
+              <el-button 
+                size="small"
+                :disabled="gate.isAnimating || gate.position >= 100"
+                @click="openGate(gate)"
+                icon="ArrowUp"
+              />
+              <el-button 
+                size="small"
+                :disabled="gate.isAnimating || gate.position <= 0"
+                @click="closeGate(gate)"
+                icon="ArrowDown"
+              />
+              <el-button 
+                size="small"
+                :disabled="gate.isAnimating"
+                @click="setGatePosition(gate, 50)"
+              >50%</el-button>
+            </div>
+            <div v-if="gate.isAnimating" class="gate-progress">
+              <el-progress 
+                :percentage="gate.animationProgress" 
+                :stroke-width="4"
+                :color="gate.position < gate.targetPosition ? '#67c23a' : '#f56c6c'"
+              />
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 快捷操作 -->
+      <div class="quick-section">
+        <div class="quick-label">全部快捷操作</div>
+        <div class="quick-buttons">
+          <el-button 
+            size="small" 
+            :disabled="isAnyAnimating"
+            @click="setAllPositions(25)"
+          >25%</el-button>
+          <el-button 
+            size="small" 
+            :disabled="isAnyAnimating"
+            @click="setAllPositions(50)"
+          >50%</el-button>
+          <el-button 
+            size="small" 
+            :disabled="isAnyAnimating"
+            @click="setAllPositions(75)"
+          >75%</el-button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { ref, computed, onMounted, onUnmounted } from 'vue'
+import { CircleClose, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
+
+export default {
+  name: 'GateControl',
+  components: {
+    CircleClose,
+    ArrowUp,
+    ArrowDown
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    initialPosition: {
+      type: Number,
+      default: 0,
+      validator: (value) => value >= 0 && value <= 100
+    }
+  },
+  emits: ['close', 'positionChange'],
+  setup(props, { emit }) {
+    // 将组件实例注册到全局变量
+    onMounted(() => {
+      window.gateControlInstance = {
+        updateGateList: updateGateList
+      };
+      console.log('🚪 GateControl 组件已挂载,全局实例已注册');
+    });
+    
+    onUnmounted(() => {
+      // 清理全局实例
+      if (window.gateControlInstance) {
+        window.gateControlInstance = null;
+      }
+    });
+    // 闸门列表
+    const gates = ref([])
+
+    // 检查是否有任何闸门正在动画
+    const isAnyAnimating = computed(() => {
+      return gates.value.some(gate => gate.isAnimating)
+    })
+
+    // 更新闸门列表
+    const updateGateList = (gateNames) => {
+      console.log('🔄 GateControl - 更新闸门列表:', gateNames)
+      console.log('🔄 GateControl - 闸门数量:', gateNames?.length || 0)
+      console.log('🔄 GateControl - 当前 gates.value:', gates.value)
+      
+      if (!gateNames || gateNames.length === 0) {
+        console.warn('⚠️ GateControl - 闸门名称列表为空')
+        return
+      }
+      
+      gates.value = gateNames.map(name => ({
+        name: name,
+        position: props.initialPosition,
+        sliderValue: props.initialPosition,
+        isAnimating: false,
+        animationProgress: 0,
+        targetPosition: props.initialPosition,
+        animationFrame: null
+      }))
+      
+      console.log('✅ GateControl - 闸门列表已更新,共', gates.value.length, '个闸门')
+    }
+
+    // 获取闸门状态样式类
+    const getGateStatusClass = (position) => {
+      if (position === 0) return 'closed'
+      if (position === 100) return 'opened'
+      return 'opening'
+    }
+
+    // 获取闸门状态文本
+    const getGateStatusText = (position) => {
+      if (position === 0) return '关闭'
+      if (position === 100) return '全开'
+      return '调节中'
+    }
+
+    // 缓动函数
+    const easeInOutCubic = (t) => {
+      return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2
+    }
+
+    // 动画单个闸门
+    const animateGate = (gate, toPosition) => {
+      if (gate.isAnimating) return
+      if (gate.position === toPosition) return
+
+      gate.isAnimating = true
+      const startPosition = gate.position
+      gate.targetPosition = toPosition
+      const startTime = performance.now()
+      const duration = 3000 // 3秒动画
+
+      const animate = (currentTime) => {
+        const elapsed = currentTime - startTime
+        const progress = Math.min(elapsed / duration, 1)
+        
+        const easeProgress = easeInOutCubic(progress)
+        
+        gate.position = Math.round(
+          startPosition + (toPosition - startPosition) * easeProgress
+        )
+        
+        gate.sliderValue = gate.position
+        gate.animationProgress = Math.round(progress * 100)
+
+        // 实时通知父组件闸门位置变化(动画过程中持续更新)
+        emit('positionChange', {
+          gateName: gate.name,
+          position: gate.position
+        })
+
+        if (progress < 1) {
+          gate.animationFrame = requestAnimationFrame(animate)
+        } else {
+          gate.isAnimating = false
+        }
+      }
+
+      gate.animationFrame = requestAnimationFrame(animate)
+    }
+
+    // 开启单个闸门
+    const openGate = (gate) => {
+      animateGate(gate, 100)
+    }
+
+    // 关闭单个闸门
+    const closeGate = (gate) => {
+      animateGate(gate, 0)
+    }
+
+    // 设置单个闸门位置
+    const setGatePosition = (gate, position) => {
+      animateGate(gate, position)
+    }
+
+    // 处理滑块变化
+    const handleGateSliderChange = (gate, value) => {
+      animateGate(gate, value)
+    }
+
+    // 开启所有闸门
+    const openAllGates = () => {
+      gates.value.forEach(gate => {
+        if (!gate.isAnimating) {
+          animateGate(gate, 100)
+        }
+      })
+    }
+
+    // 关闭所有闸门
+    const closeAllGates = () => {
+      gates.value.forEach(gate => {
+        if (!gate.isAnimating) {
+          animateGate(gate, 0)
+        }
+      })
+    }
+
+    // 设置所有闸门位置
+    const setAllPositions = (position) => {
+      gates.value.forEach(gate => {
+        if (!gate.isAnimating) {
+          animateGate(gate, position)
+        }
+      })
+    }
+
+    // 组件卸载时清理动画
+    onUnmounted(() => {
+      gates.value.forEach(gate => {
+        if (gate.animationFrame) {
+          cancelAnimationFrame(gate.animationFrame)
+        }
+      })
+    })
+
+    return {
+      gates,
+      isAnyAnimating,
+      updateGateList,
+      getGateStatusClass,
+      getGateStatusText,
+      openGate,
+      closeGate,
+      setGatePosition,
+      handleGateSliderChange,
+      openAllGates,
+      closeAllGates,
+      setAllPositions
+    }
+  }
+}
+</script>
+
+<style scoped>
+.gate-control-panel {
+  width: 320px;
+  max-height: 80vh;  /* 使用视口高度的80%作为最大高度 */
+  top: 100px;
+  right: 20px;
+  padding: 15px;
+  overflow-y: auto;
+}
+
+.sm-panel-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+  padding-bottom: 10px;
+  border-bottom: 1px solid #eee;
+}
+
+.panel-title {
+  font-size: 16px;
+  font-weight: bold;
+  color: #303133;
+}
+
+.close-icon {
+  cursor: pointer;
+  color: #999;
+  font-size: 18px;
+  transition: color 0.2s;
+}
+
+.close-icon:hover {
+  color: #666;
+}
+
+.gate-control-content {
+  display: flex;
+  flex-direction: column;
+  gap: 15px;
+}
+
+/* 全部控制 */
+.all-control-section {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 10px;
+  background-color: #f8f9fa;
+  border-radius: 4px;
+}
+
+.all-control-label {
+  font-size: 14px;
+  color: #606266;
+}
+
+.all-control-buttons {
+  display: flex;
+  gap: 8px;
+}
+
+.all-control-btn {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+/* 闸门列表 */
+.gates-section {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.gates-label {
+  font-size: 14px;
+  color: #606266;
+  font-weight: 500;
+}
+
+.gates-list {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  max-height: calc(80vh - 200px);  /* 根据面板高度减去其他部分的高度 */
+  overflow-y: auto;
+}
+
+.gate-item {
+  padding: 12px;
+  background-color: #fafafa;
+  border-radius: 6px;
+  border: 1px solid #e4e7ed;
+}
+
+.gate-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 8px;
+}
+
+.gate-name {
+  font-size: 14px;
+  font-weight: 500;
+  color: #303133;
+}
+
+.gate-status {
+  font-size: 12px;
+  padding: 2px 8px;
+  border-radius: 10px;
+}
+
+.gate-status.closed {
+  background-color: #fef0f0;
+  color: #f56c6c;
+}
+
+.gate-status.opened {
+  background-color: #f0f9eb;
+  color: #67c23a;
+}
+
+.gate-status.opening {
+  background-color: #fdf6ec;
+  color: #e6a23c;
+}
+
+.gate-position {
+  text-align: right;
+  margin-bottom: 8px;
+}
+
+.gate-position-value {
+  font-size: 18px;
+  font-weight: bold;
+  color: #409eff;
+}
+
+.gate-slider {
+  margin-bottom: 8px;
+}
+
+.gate-controls {
+  display: flex;
+  gap: 6px;
+}
+
+.gate-controls el-button {
+  flex: 1;
+}
+
+.gate-progress {
+  margin-top: 8px;
+}
+
+/* 快捷操作 */
+.quick-section {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.quick-label {
+  font-size: 14px;
+  color: #606266;
+}
+
+.quick-buttons {
+  display: flex;
+  gap: 10px;
+}
+
+.quick-buttons el-button {
+  flex: 1;
+}
+</style>

+ 6 - 0
RuoYi-Vue3/src/supermap-cesium-module/components/model-transform/model-transform-panel.vue

@@ -456,6 +456,7 @@ export default {
         const entity = pickedObject.id;
         console.log('[model-transform-panel] entity:', entity);
         console.log('[model-transform-panel] entity.isGeoJsonPoint:', entity.isGeoJsonPoint);
+        console.log('[model-transform-panel] entity.disableTransform:', entity.disableTransform);
         
         if (entity.isGeoJsonPoint) {
           console.log('[model-transform-panel] 调用 handleGeoJsonPointClick');
@@ -464,6 +465,11 @@ export default {
         }
         
         if (entity.model) {
+          // 检查模型是否禁用变换面板
+          if (entity.disableTransform) {
+            console.log('[model-transform-panel] 模型已禁用变换面板,跳过');
+            return;
+          }
           selectEntity(entity);
         } else {
           clearSelection();

+ 60 - 0
RuoYi-Vue3/src/supermap-cesium-module/config/fixedScenes.js

@@ -0,0 +1,60 @@
+/**
+ * 固定业务场景配置
+ * 这些场景是系统内置的,不能被删除,只能加载和卸载
+ */
+import { GATE_NODE_NAMES, JuKouSceneConfig } from '../business-scenes/JuKouShuiZhaScene';
+
+export const fixedScenes = [
+  {
+    sceneId: 'fixed_jushui_gate',
+    sceneName: '莒口水闸',
+    sceneType: 'fixed',
+    description: '莒口水闸固定场景,包含闸门启闭控制功能',
+    // 第三人称相机位置(Cartesian3坐标)
+    thirdPersonCameraPos: '[-2794762.0047350973, 5012854.3612919515, 2782682.393722253]',
+    thirdPersonCameraTarget: '[-2794762.0047350973, 5012854.3612919515, 0]',
+    // 相机方向向量
+    cameraDirection: '[-0.5190347522307343, -0.8239501952633861, 0.22739613387700425]',
+    // 相机上向量
+    cameraUp: '[-0.6385171678739079, 0.5506184719962913, 0.5376942668717541]',
+    // 需要加载的模型
+    loadedModels: '[{ "name": "莒口水闸3", "filePath": "/models/莒口水闸3.glb", "coordinates": "119.144159,25.867745,23", "rotation": "0,0,260", "transparency": 0.8 }]',
+    // 需要加载的数据服务
+    dataServices: '[]',
+    // POI点
+    poiPoints: '[]',
+    // 是否启用闸门控制
+    enableGateControl: true,
+    // 闸门初始位置(0-100)
+    initialGatePosition: 0,
+    // 是否禁用模型变换弹框(点击模型不显示变换面板)
+    disableModelTransform: JuKouSceneConfig.disableModelTransform,
+    // 闸门子节点名称前缀(用于识别模型中的闸门子节点)
+    gateNodePrefix: JuKouSceneConfig.gateNodePrefix,
+    // 闸门子节点名称列表(从业务场景模块引入)
+    gateNodeNames: GATE_NODE_NAMES,
+    // 业务场景处理器路径
+    sceneHandler: '../business-scenes/JuKouShuiZhaScene.js'
+  }
+]
+
+/**
+ * 获取固定场景列表
+ */
+export function getFixedScenes() {
+  return fixedScenes
+}
+
+/**
+ * 根据场景ID获取固定场景
+ */
+export function getFixedSceneById(sceneId) {
+  return fixedScenes.find(scene => scene.sceneId === sceneId)
+}
+
+/**
+ * 检查是否为固定场景
+ */
+export function isFixedScene(sceneId) {
+  return fixedScenes.some(scene => scene.sceneId === sceneId)
+}

文件差異過大導致無法顯示
+ 673 - 92
RuoYi-Vue3/src/supermap-cesium-module/views/layout/aside.vue


+ 7 - 0
ruoyi-admin/pom.xml

@@ -68,6 +68,13 @@
             <systemPath>${project.basedir}/lib/DmDialect-for-hibernate5.3-8.1.2.192.jar</systemPath>
         </dependency>
 
+        <!-- H2内存数据库(用于临时测试) -->
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
         <!-- Spring Data JPA支持 -->
         <dependency>
             <groupId>org.springframework.boot</groupId>

+ 59 - 0
ruoyi-admin/src/main/resources/application-h2.yml

@@ -0,0 +1,59 @@
+# H2内存数据库配置(临时使用)
+spring:
+    datasource:
+        type: com.alibaba.druid.pool.DruidDataSource
+        driverClassName: org.h2.Driver
+        url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=Oracle
+        username: sa
+        password: 
+        druid:
+            # 初始连接数
+            initialSize: 5
+            # 最小连接池数量
+            minIdle: 10
+            # 最大连接池数量
+            maxActive: 20
+            # 配置获取连接等待超时的时间
+            maxWait: 60000
+            # 配置连接超时时间
+            connectTimeout: 30000
+            # 配置网络超时时间
+            socketTimeout: 60000
+            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+            timeBetweenEvictionRunsMillis: 60000
+            # 配置一个连接在池中最小生存的时间,单位是毫秒
+            minEvictableIdleTimeMillis: 300000
+            # 配置一个连接在池中最大生存的时间,单位是毫秒
+            maxEvictableIdleTimeMillis: 900000
+            # 配置检测连接是否有效
+            validationQuery: SELECT 1
+            testWhileIdle: true
+            testOnBorrow: false
+            testOnReturn: false
+            webStatFilter: 
+                enabled: true
+            statViewServlet:
+                enabled: true
+                allow:
+                url-pattern: /druid/*
+                login-username: ruoyi
+                login-password: 123456
+            filter:
+                stat:
+                    enabled: true
+                    log-slow-sql: true
+                    slow-sql-millis: 1000
+                    merge-sql: true
+                wall:
+                    config:
+                        multi-statement-allow: true
+    # SQL初始化配置
+    sql:
+        init:
+            mode: always
+            schema-locations: classpath:sql/schema-h2.sql
+    # H2控制台配置
+    h2:
+        console:
+            enabled: true
+            path: /h2-console

+ 361 - 0
ruoyi-admin/src/main/resources/sql/schema-h2.sql

@@ -0,0 +1,361 @@
+-- H2数据库初始化脚本 - 兼容若依系统
+
+-- 1、部门表
+CREATE TABLE IF NOT EXISTS sys_dept (
+    dept_id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    parent_id BIGINT DEFAULT 0,
+    ancestors VARCHAR(50) DEFAULT '',
+    dept_name VARCHAR(30) DEFAULT '',
+    order_num INT DEFAULT 0,
+    leader VARCHAR(20),
+    phone VARCHAR(11),
+    email VARCHAR(50),
+    status CHAR(1) DEFAULT '0',
+    del_flag CHAR(1) DEFAULT '0',
+    create_by VARCHAR(64) DEFAULT '',
+    create_time TIMESTAMP,
+    update_by VARCHAR(64) DEFAULT '',
+    update_time TIMESTAMP
+);
+
+INSERT INTO sys_dept VALUES(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL);
+INSERT INTO sys_dept VALUES(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL);
+INSERT INTO sys_dept VALUES(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL);
+INSERT INTO sys_dept VALUES(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL);
+
+-- 2、用户信息表
+CREATE TABLE IF NOT EXISTS sys_user (
+    user_id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    dept_id BIGINT,
+    user_name VARCHAR(30) NOT NULL,
+    nick_name VARCHAR(30) NOT NULL,
+    user_type VARCHAR(2) DEFAULT '00',
+    email VARCHAR(50) DEFAULT '',
+    phonenumber VARCHAR(11) DEFAULT '',
+    sex CHAR(1) DEFAULT '0',
+    avatar VARCHAR(100) DEFAULT '',
+    password VARCHAR(100) DEFAULT '',
+    status CHAR(1) DEFAULT '0',
+    del_flag CHAR(1) DEFAULT '0',
+    login_ip VARCHAR(128) DEFAULT '',
+    login_date TIMESTAMP,
+    pwd_update_date TIMESTAMP,
+    create_by VARCHAR(64) DEFAULT '',
+    create_time TIMESTAMP,
+    update_by VARCHAR(64) DEFAULT '',
+    update_time TIMESTAMP,
+    remark VARCHAR(500)
+);
+
+INSERT INTO sys_user VALUES(1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), 'admin', CURRENT_TIMESTAMP(), '', NULL, '管理员');
+INSERT INTO sys_user VALUES(2, 105, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), 'admin', CURRENT_TIMESTAMP(), '', NULL, '测试员');
+
+-- 3、岗位信息表
+CREATE TABLE IF NOT EXISTS sys_post (
+    post_id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    post_code VARCHAR(64) NOT NULL,
+    post_name VARCHAR(50) NOT NULL,
+    post_sort INT NOT NULL,
+    status CHAR(1) NOT NULL,
+    create_by VARCHAR(64) DEFAULT '',
+    create_time TIMESTAMP,
+    update_by VARCHAR(64) DEFAULT '',
+    update_time TIMESTAMP,
+    remark VARCHAR(500)
+);
+
+INSERT INTO sys_post VALUES(1, 'ceo', '董事长', 1, '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '');
+INSERT INTO sys_post VALUES(2, 'se', '项目经理', 2, '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '');
+INSERT INTO sys_post VALUES(4, 'user', '普通员工', 4, '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '');
+
+-- 4、角色信息表
+CREATE TABLE IF NOT EXISTS sys_role (
+    role_id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    role_name VARCHAR(30) NOT NULL,
+    role_key VARCHAR(100) NOT NULL,
+    role_sort INT NOT NULL,
+    data_scope CHAR(1) DEFAULT '1',
+    menu_check_strictly INT DEFAULT 1,
+    dept_check_strictly INT DEFAULT 1,
+    status CHAR(1) NOT NULL,
+    del_flag CHAR(1) DEFAULT '0',
+    create_by VARCHAR(64) DEFAULT '',
+    create_time TIMESTAMP,
+    update_by VARCHAR(64) DEFAULT '',
+    update_time TIMESTAMP,
+    remark VARCHAR(500)
+);
+
+INSERT INTO sys_role VALUES(1, '超级管理员', 'admin', 1, '1', 1, 1, '0', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '超级管理员');
+INSERT INTO sys_role VALUES(2, '普通角色', 'common', 2, '2', 1, 1, '0', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '普通角色');
+
+-- 5、菜单权限表
+CREATE TABLE IF NOT EXISTS sys_menu (
+    menu_id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    menu_name VARCHAR(50) NOT NULL,
+    parent_id BIGINT DEFAULT 0,
+    order_num INT DEFAULT 0,
+    path VARCHAR(200) DEFAULT '',
+    component VARCHAR(255),
+    query VARCHAR(255),
+    route_name VARCHAR(50) DEFAULT '',
+    is_frame INT DEFAULT 1,
+    is_cache INT DEFAULT 0,
+    menu_type CHAR(1) DEFAULT '',
+    visible CHAR(1) DEFAULT '0',
+    status CHAR(1) DEFAULT '0',
+    perms VARCHAR(100),
+    icon VARCHAR(100) DEFAULT '#',
+    create_by VARCHAR(64) DEFAULT '',
+    create_time TIMESTAMP,
+    update_by VARCHAR(64) DEFAULT '',
+    update_time TIMESTAMP,
+    remark VARCHAR(500) DEFAULT ''
+);
+
+INSERT INTO sys_menu VALUES(1, '系统管理', 0, 1, 'system', NULL, '', '', 1, 0, 'M', '0', '0', '', 'system', 'admin', CURRENT_TIMESTAMP(), '', NULL, '系统管理目录');
+INSERT INTO sys_menu VALUES(2, '系统监控', 0, 2, 'monitor', NULL, '', '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', CURRENT_TIMESTAMP(), '', NULL, '系统监控目录');
+INSERT INTO sys_menu VALUES(3, '系统工具', 0, 3, 'tool', NULL, '', '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', CURRENT_TIMESTAMP(), '', NULL, '系统工具目录');
+INSERT INTO sys_menu VALUES(100, '用户管理', 1, 1, 'user', 'system/user/index', '', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', CURRENT_TIMESTAMP(), '', NULL, '用户管理菜单');
+INSERT INTO sys_menu VALUES(101, '角色管理', 1, 2, 'role', 'system/role/index', '', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', CURRENT_TIMESTAMP(), '', NULL, '角色管理菜单');
+INSERT INTO sys_menu VALUES(102, '菜单管理', 1, 3, 'menu', 'system/menu/index', '', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', CURRENT_TIMESTAMP(), '', NULL, '菜单管理菜单');
+INSERT INTO sys_menu VALUES(103, '部门管理', 1, 4, 'dept', 'system/dept/index', '', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 'admin', CURRENT_TIMESTAMP(), '', NULL, '部门管理菜单');
+INSERT INTO sys_menu VALUES(104, '岗位管理', 1, 5, 'post', 'system/post/index', '', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 'admin', CURRENT_TIMESTAMP(), '', NULL, '岗位管理菜单');
+INSERT INTO sys_menu VALUES(105, '字典管理', 1, 6, 'dict', 'system/dict/index', '', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 'admin', CURRENT_TIMESTAMP(), '', NULL, '字典管理菜单');
+INSERT INTO sys_menu VALUES(106, '参数设置', 1, 7, 'config', 'system/config/index', '', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 'admin', CURRENT_TIMESTAMP(), '', NULL, '参数设置菜单');
+INSERT INTO sys_menu VALUES(107, '通知公告', 1, 8, 'notice', 'system/notice/index', '', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 'admin', CURRENT_TIMESTAMP(), '', NULL, '通知公告菜单');
+INSERT INTO sys_menu VALUES(109, '在线用户', 2, 1, 'online', 'monitor/online/index', '', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 'admin', CURRENT_TIMESTAMP(), '', NULL, '在线用户菜单');
+INSERT INTO sys_menu VALUES(110, '定时任务', 2, 2, 'job', 'monitor/job/index', '', '', 1, 0, 'C', '0', '0', 'monitor:job:list', 'job', 'admin', CURRENT_TIMESTAMP(), '', NULL, '定时任务菜单');
+INSERT INTO sys_menu VALUES(111, '数据监控', 2, 3, 'druid', 'monitor/druid/index', '', '', 1, 0, 'C', '0', '0', 'monitor:druid:list', 'druid', 'admin', CURRENT_TIMESTAMP(), '', NULL, '数据监控菜单');
+INSERT INTO sys_menu VALUES(112, '服务监控', 2, 4, 'server', 'monitor/server/index', '', '', 1, 0, 'C', '0', '0', 'monitor:server:list', 'server', 'admin', CURRENT_TIMESTAMP(), '', NULL, '服务监控菜单');
+INSERT INTO sys_menu VALUES(113, '缓存监控', 2, 5, 'cache', 'monitor/cache/index', '', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 'admin', CURRENT_TIMESTAMP(), '', NULL, '缓存监控菜单');
+INSERT INTO sys_menu VALUES(115, '表单构建', 3, 1, 'build', 'tool/build/index', '', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 'admin', CURRENT_TIMESTAMP(), '', NULL, '表单构建菜单');
+INSERT INTO sys_menu VALUES(116, '代码生成', 3, 2, 'gen', 'tool/gen/index', '', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 'admin', CURRENT_TIMESTAMP(), '', NULL, '代码生成菜单');
+INSERT INTO sys_menu VALUES(117, '系统接口', 3, 3, 'swagger', 'tool/swagger/index', '', '', 1, 0, 'C', '0', '0', 'tool:swagger:list', 'swagger', 'admin', CURRENT_TIMESTAMP(), '', NULL, '系统接口菜单');
+
+-- 用户管理按钮
+INSERT INTO sys_menu VALUES(1000, '用户查询', 100, 1, '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 'admin', CURRENT_TIMESTAMP(), '', NULL, '');
+INSERT INTO sys_menu VALUES(1001, '用户新增', 100, 2, '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 'admin', CURRENT_TIMESTAMP(), '', NULL, '');
+INSERT INTO sys_menu VALUES(1002, '用户修改', 100, 3, '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 'admin', CURRENT_TIMESTAMP(), '', NULL, '');
+INSERT INTO sys_menu VALUES(1003, '用户删除', 100, 4, '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 'admin', CURRENT_TIMESTAMP(), '', NULL, '');
+INSERT INTO sys_menu VALUES(1004, '用户导出', 100, 5, '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 'admin', CURRENT_TIMESTAMP(), '', NULL, '');
+INSERT INTO sys_menu VALUES(1005, '用户导入', 100, 6, '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 'admin', CURRENT_TIMESTAMP(), '', NULL, '');
+INSERT INTO sys_menu VALUES(1006, '重置密码', 100, 7, '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', CURRENT_TIMESTAMP(), '', NULL, '');
+
+-- 6、用户和角色关联表
+CREATE TABLE IF NOT EXISTS sys_user_role (
+    user_id BIGINT NOT NULL,
+    role_id BIGINT NOT NULL,
+    PRIMARY KEY (user_id, role_id)
+);
+
+INSERT INTO sys_user_role VALUES (1, 1);
+INSERT INTO sys_user_role VALUES (2, 2);
+
+-- 7、角色和菜单关联表
+CREATE TABLE IF NOT EXISTS sys_role_menu (
+    role_id BIGINT NOT NULL,
+    menu_id BIGINT NOT NULL,
+    PRIMARY KEY (role_id, menu_id)
+);
+
+INSERT INTO sys_role_menu VALUES (2, 1);
+INSERT INTO sys_role_menu VALUES (2, 2);
+INSERT INTO sys_role_menu VALUES (2, 3);
+INSERT INTO sys_role_menu VALUES (2, 100);
+INSERT INTO sys_role_menu VALUES (2, 101);
+INSERT INTO sys_role_menu VALUES (2, 102);
+INSERT INTO sys_role_menu VALUES (2, 103);
+INSERT INTO sys_role_menu VALUES (2, 104);
+INSERT INTO sys_role_menu VALUES (2, 105);
+INSERT INTO sys_role_menu VALUES (2, 106);
+INSERT INTO sys_role_menu VALUES (2, 107);
+INSERT INTO sys_role_menu VALUES (2, 109);
+INSERT INTO sys_role_menu VALUES (2, 110);
+INSERT INTO sys_role_menu VALUES (2, 111);
+INSERT INTO sys_role_menu VALUES (2, 112);
+INSERT INTO sys_role_menu VALUES (2, 113);
+INSERT INTO sys_role_menu VALUES (2, 115);
+INSERT INTO sys_role_menu VALUES (2, 116);
+INSERT INTO sys_role_menu VALUES (2, 117);
+INSERT INTO sys_role_menu VALUES (2, 1000);
+INSERT INTO sys_role_menu VALUES (2, 1001);
+INSERT INTO sys_role_menu VALUES (2, 1002);
+INSERT INTO sys_role_menu VALUES (2, 1003);
+INSERT INTO sys_role_menu VALUES (2, 1004);
+INSERT INTO sys_role_menu VALUES (2, 1005);
+INSERT INTO sys_role_menu VALUES (2, 1006);
+
+-- 8、角色和部门关联表
+CREATE TABLE IF NOT EXISTS sys_role_dept (
+    role_id BIGINT NOT NULL,
+    dept_id BIGINT NOT NULL,
+    PRIMARY KEY (role_id, dept_id)
+);
+
+INSERT INTO sys_role_dept VALUES (2, 100);
+INSERT INTO sys_role_dept VALUES (2, 101);
+INSERT INTO sys_role_dept VALUES (2, 105);
+
+-- 9、用户与岗位关联表
+CREATE TABLE IF NOT EXISTS sys_user_post (
+    user_id BIGINT NOT NULL,
+    post_id BIGINT NOT NULL,
+    PRIMARY KEY (user_id, post_id)
+);
+
+INSERT INTO sys_user_post VALUES (1, 1);
+INSERT INTO sys_user_post VALUES (2, 2);
+
+-- 10、操作日志记录
+CREATE TABLE IF NOT EXISTS sys_oper_log (
+    oper_id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    title VARCHAR(50) DEFAULT '',
+    business_type INT DEFAULT 0,
+    method VARCHAR(200) DEFAULT '',
+    request_method VARCHAR(10) DEFAULT '',
+    operator_type INT DEFAULT 0,
+    oper_name VARCHAR(50) DEFAULT '',
+    dept_name VARCHAR(50) DEFAULT '',
+    oper_url VARCHAR(255) DEFAULT '',
+    oper_ip VARCHAR(128) DEFAULT '',
+    oper_location VARCHAR(255) DEFAULT '',
+    oper_param VARCHAR(2000) DEFAULT '',
+    json_result VARCHAR(2000) DEFAULT '',
+    status INT DEFAULT 0,
+    error_msg VARCHAR(2000) DEFAULT '',
+    oper_time TIMESTAMP,
+    cost_time BIGINT DEFAULT 0
+);
+
+-- 11、字典类型表
+CREATE TABLE IF NOT EXISTS sys_dict_type (
+    dict_id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    dict_name VARCHAR(100) DEFAULT '',
+    dict_type VARCHAR(100) DEFAULT '',
+    status CHAR(1) DEFAULT '0',
+    create_by VARCHAR(64) DEFAULT '',
+    create_time TIMESTAMP,
+    update_by VARCHAR(64) DEFAULT '',
+    update_time TIMESTAMP,
+    remark VARCHAR(500)
+);
+
+INSERT INTO sys_dict_type VALUES(1, '用户性别', 'sys_user_sex', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '用户性别列表');
+INSERT INTO sys_dict_type VALUES(2, '菜单状态', 'sys_show_hide', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '菜单状态列表');
+INSERT INTO sys_dict_type VALUES(3, '系统开关', 'sys_normal_disable', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '系统开关列表');
+INSERT INTO sys_dict_type VALUES(4, '任务状态', 'sys_job_status', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '任务状态列表');
+INSERT INTO sys_dict_type VALUES(5, '任务分组', 'sys_job_group', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '任务分组列表');
+INSERT INTO sys_dict_type VALUES(6, '系统是否', 'sys_yes_no', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '系统是否列表');
+INSERT INTO sys_dict_type VALUES(7, '通知类型', 'sys_notice_type', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '通知类型列表');
+INSERT INTO sys_dict_type VALUES(8, '通知状态', 'sys_notice_status', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '通知状态列表');
+INSERT INTO sys_dict_type VALUES(9, '操作类型', 'sys_oper_type', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '操作类型列表');
+INSERT INTO sys_dict_type VALUES(10, '系统状态', 'sys_common_status', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '登录状态列表');
+
+-- 12、字典数据表
+CREATE TABLE IF NOT EXISTS sys_dict_data (
+    dict_code BIGINT AUTO_INCREMENT PRIMARY KEY,
+    dict_sort INT DEFAULT 0,
+    dict_label VARCHAR(100) DEFAULT '',
+    dict_value VARCHAR(100) DEFAULT '',
+    dict_type VARCHAR(100) DEFAULT '',
+    css_class VARCHAR(100),
+    list_class VARCHAR(100),
+    is_default CHAR(1) DEFAULT 'N',
+    status CHAR(1) DEFAULT '0',
+    create_by VARCHAR(64) DEFAULT '',
+    create_time TIMESTAMP,
+    update_by VARCHAR(64) DEFAULT '',
+    update_time TIMESTAMP,
+    remark VARCHAR(500)
+);
+
+INSERT INTO sys_dict_data VALUES(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '性别男');
+INSERT INTO sys_dict_data VALUES(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '性别女');
+INSERT INTO sys_dict_data VALUES(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '性别未知');
+INSERT INTO sys_dict_data VALUES(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '显示菜单');
+INSERT INTO sys_dict_data VALUES(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '隐藏菜单');
+INSERT INTO sys_dict_data VALUES(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '正常状态');
+INSERT INTO sys_dict_data VALUES(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '停用状态');
+INSERT INTO sys_dict_data VALUES(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '系统默认是');
+INSERT INTO sys_dict_data VALUES(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP(), '', NULL, '系统默认否');
+
+-- 13、参数配置表
+CREATE TABLE IF NOT EXISTS sys_config (
+    config_id INT AUTO_INCREMENT PRIMARY KEY,
+    config_name VARCHAR(100) DEFAULT '',
+    config_key VARCHAR(100) DEFAULT '',
+    config_value VARCHAR(500) DEFAULT '',
+    config_type CHAR(1) DEFAULT 'N',
+    create_by VARCHAR(64) DEFAULT '',
+    create_time TIMESTAMP,
+    update_by VARCHAR(64) DEFAULT '',
+    update_time TIMESTAMP,
+    remark VARCHAR(500)
+);
+
+INSERT INTO sys_config VALUES(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', CURRENT_TIMESTAMP(), '', NULL, '蓝色 skin-blue、绿色 skin-green');
+INSERT INTO sys_config VALUES(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', CURRENT_TIMESTAMP(), '', NULL, '初始化密码 123456');
+INSERT INTO sys_config VALUES(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', CURRENT_TIMESTAMP(), '', NULL, '深色主题theme-dark');
+INSERT INTO sys_config VALUES(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', CURRENT_TIMESTAMP(), '', NULL, '是否开启验证码功能');
+INSERT INTO sys_config VALUES(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', CURRENT_TIMESTAMP(), '', NULL, '是否开启注册用户功能');
+
+-- 14、系统访问记录
+CREATE TABLE IF NOT EXISTS sys_logininfor (
+    info_id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    user_name VARCHAR(50) DEFAULT '',
+    ipaddr VARCHAR(128) DEFAULT '',
+    login_location VARCHAR(255) DEFAULT '',
+    browser VARCHAR(50) DEFAULT '',
+    os VARCHAR(50) DEFAULT '',
+    status CHAR(1) DEFAULT '0',
+    msg VARCHAR(255) DEFAULT '',
+    login_time TIMESTAMP
+);
+
+-- 15、定时任务调度表
+CREATE TABLE IF NOT EXISTS sys_job (
+    job_id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    job_name VARCHAR(64) DEFAULT '',
+    job_group VARCHAR(64) DEFAULT 'DEFAULT',
+    invoke_target VARCHAR(500) NOT NULL,
+    cron_expression VARCHAR(255) DEFAULT '',
+    misfire_policy VARCHAR(20) DEFAULT '3',
+    concurrent CHAR(1) DEFAULT '1',
+    status CHAR(1) DEFAULT '0',
+    create_by VARCHAR(64) DEFAULT '',
+    create_time TIMESTAMP,
+    update_by VARCHAR(64) DEFAULT '',
+    update_time TIMESTAMP,
+    remark VARCHAR(500) DEFAULT ''
+);
+
+-- 16、通知公告表
+CREATE TABLE IF NOT EXISTS sys_notice (
+    notice_id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    notice_title VARCHAR(50) NOT NULL,
+    notice_type CHAR(1) DEFAULT '1',
+    notice_content TEXT,
+    status CHAR(1) DEFAULT '0',
+    create_by VARCHAR(64) DEFAULT '',
+    create_time TIMESTAMP,
+    update_by VARCHAR(64) DEFAULT '',
+    update_time TIMESTAMP
+);
+
+-- 水利工程模型表
+CREATE TABLE IF NOT EXISTS watershed_model (
+    model_id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    model_name VARCHAR(100) NOT NULL,
+    model_type VARCHAR(50) NOT NULL,
+    model_format VARCHAR(20) NOT NULL,
+    file_path VARCHAR(255),
+    file_size BIGINT,
+    upload_unit VARCHAR(100),
+    status CHAR(1) DEFAULT '0',
+    create_by VARCHAR(64) DEFAULT '',
+    create_time TIMESTAMP,
+    update_by VARCHAR(64) DEFAULT '',
+    update_time TIMESTAMP,
+    remark VARCHAR(500)
+);

二進制
ruoyi-admin/target/classes/com/ruoyi/RuoYiApplication.class


二進制
ruoyi-admin/target/classes/com/ruoyi/RuoYiServletInitializer.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/cesium/CesiumMapConfigController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CaptchaController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CommonController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/CacheController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/ServerController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysLogininforController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysOperlogController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysUserOnlineController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysConfigController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDeptController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictDataController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictTypeController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysIndexController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysLoginController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysMenuController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysNoticeController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysPostController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysProfileController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRegisterController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRoleController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysUserController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/TestController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/UserEntity.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/watershed/WatershedModelController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/controller/watershed/WatershedServiceController.class


二進制
ruoyi-admin/target/classes/com/ruoyi/web/core/config/SwaggerConfig.class


二進制
ruoyi-admin/uploads/models/2026/06/12/xinjiangdiban_20260612184229A001.glb


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/CesiumGeojson.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/CesiumMapConfig.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/SysCache.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/SysConfig.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/SysLogininfor.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/SysNotice.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/SysOperLog.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/SysPost.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleDept.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleMenu.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserOnline.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserPost.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserRole.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/WatershedEquipment.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/WatershedFacility.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/WatershedService.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/vo/MetaVo.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/domain/vo/RouterVo.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/CesiumGeojsonServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/CesiumMapConfigServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysConfigServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDeptServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictDataServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysLogininforServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysMenuServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysNoticeServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysOperLogServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysPostServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysRoleServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/WatershedEquipmentServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/WatershedModelServiceImpl.class


二進制
ruoyi-system/target/classes/com/ruoyi/system/service/impl/WatershedServiceServiceImpl.class


部分文件因文件數量過多而無法顯示