@yangyongtao/gaea 1.1.22 → 1.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -137,7 +137,7 @@ const defaultConfig = {
137
137
  /** 天地图 token(在线模式使用) */
138
138
  tiandituToken: '63201cfd7df1e1bff7d88bb8baa32151',
139
139
  /** 天地图最小/最大层级 */
140
- tiandituLevelRange: [0, 18],
140
+ tiandituLevelRange: [0, 21],
141
141
  /** 默认加载的影像图层(服务平台模式生效) */
142
142
  defaultLayers: [
143
143
  { layerName: 'earthland', forMat: 'image/png', min: 0, max: 18, transparency: 0.3 },
@@ -164,6 +164,8 @@ const defaultConfig = {
164
164
  dataHubApiUrl: '',
165
165
  /** 是否显示图层树按钮(右上角重置按钮旁边) */
166
166
  showLayerTreeBtn: false,
167
+ /** 默认隐藏的分类 key 列表,如 ['rainStations', 'rescue'] */
168
+ defaultHiddenCategories: [],
167
169
  /** 图层树分类数据(默认数据,接口数据会合并进来) */
168
170
  layerCategories: [
169
171
  { key: 'seawall', text: '海塘', visible: true, children: [
@@ -401,6 +403,14 @@ export class GaeaApp {
401
403
  // 8. 初始化屏幕图标
402
404
  this._initScreenIcons();
403
405
 
406
+ // 应用默认隐藏的分类
407
+ const hiddenCats = this.config.defaultHiddenCategories || []
408
+ hiddenCats.forEach(key => {
409
+ const cat = this.layerCategories.find(c => c.key === key)
410
+ if (cat) cat.visible = false
411
+ this.setCategoryVisible(key, false)
412
+ })
413
+
404
414
  console.log('[GaeaApp] 后台资源加载完成');
405
415
  } catch (e) {
406
416
  console.error('[GaeaApp] 后台资源加载出错:', e);
@@ -603,13 +613,8 @@ export class GaeaApp {
603
613
  { name: 'YPDP0+750', position: { x: 27.220289, y: 120.459753, z: -44 }, rotate: [0, 226, 0], size: [1, 1, 1], show: true, url: 'gltf/大坝断面750.gltf' },
604
614
  { name: 'YPDP0+100', position: { x: 27.223027, y: 120.454653, z: -44 }, rotate: [0, 222, 0], size: [1, 1, 1], show: true, url: 'gltf/大坝断面100.gltf' },
605
615
  { name: 'YPDP0+200', position: { x: 27.222541, y: 120.455453, z: -44 }, rotate: [0, 246, 0], size: [1, 1, 1], show: true, url: 'gltf/大坝断面200.gltf' },
606
- { name: '沿浦水闸', position: { x: 27.222866, y: 120.453113, z: 13.25 }, rotate: [0, -30, 0], size: [1, 1, 1], show: false, url: 'gltf/沿浦水闸.gltf', enablePick: true },
607
616
  { name: '沿浦泵站', position: { x: 27.222001, y: 120.452509, z: 17 }, rotate: [0, -38, 0], size: [1, 1, 1], show: true, url: 'gltf/沿浦泵站.gltf', enablePick: true },
608
- { name: '下在水闸', position: { x: 27.221816, y: 120.445182, z: 20 }, rotate: [0, 0, 0], size: [1, 1, 1], show: false, url: 'gltf/下在水闸.gltf', enablePick: true },
609
617
  { name: '下在海塘纳排闸', position: { x: 27.219978, y: 120.445672, z: 15 }, rotate: [0, 0, 0], size: [1, 1, 1], show: true, url: 'gltf/下在海塘纳排闸.gltf', enablePick: true },
610
- { name: '联盟水闸', position: { x: 27.200462, y: 120.465635, z: 20 }, rotate: [0, 0, 0], size: [1, 1, 1], show: false, url: 'gltf/联盟水闸.gltf', enablePick: true },
611
- { name: '岭尾水闸', position: { x: 27.215762, y: 120.462235, z: 30 }, rotate: [0, -63, 0], size: [1, 1, 1], show: false, url: 'gltf/岭尾水闸.gltf', enablePick: true },
612
- { name: '岭尾新闸', position: { x: 27.210369, y: 120.458532, z: 0.5 }, rotate: [0, -153, 0], size: [1, 1, 1], show: false, url: 'gltf/岭尾新闸.gltf' },
613
618
  { name: '盒子模型', position: { x: 27.241836, y: 120.462750, z: 50 }, rotate: [0, 90, 0], size: [10, 10, 10], show: true, url: 'gltf/box.gltf' },
614
619
  { name: 'YPDP0+600游览模型', position: { x: 27.217433, y: 120.461448, z: 32 }, rotate: [0, 90, 0], size: [1, 1, 1], show: true, url: 'gltf/box.gltf' },
615
620
  { name: 'YPDP0+750游览模型', position: { x: 27.220072, y: 120.459602, z: 30 }, rotate: [0, 90, 0], size: [1, 1, 1], show: true, url: 'gltf/box.gltf' },
@@ -1488,7 +1493,14 @@ export class GaeaApp {
1488
1493
  position: { x: lat, y: lng, z },
1489
1494
  method,
1490
1495
  offset,
1491
- onClick,
1496
+ onClick: (clickedName, data) => {
1497
+ // 派发全局自定义事件,父组件可通过 document.addEventListener 监听
1498
+ document.dispatchEvent(new CustomEvent('gaea-marker-click', {
1499
+ detail: { name: clickedName, text: data.text, position: data.position, category },
1500
+ bubbles: true
1501
+ }))
1502
+ if (onClick) onClick(clickedName, data)
1503
+ },
1492
1504
  category,
1493
1505
  type: 'marker'
1494
1506
  })
@@ -2033,6 +2045,141 @@ export class GaeaApp {
2033
2045
  if (el) el.style.display = 'none'
2034
2046
  })
2035
2047
  };
2048
+
2049
+ // ---- 水面加载 ----
2050
+
2051
+ /**
2052
+ * 加载水面
2053
+ * @param {Object} opts
2054
+ * @param {string} opts.gltfUrl - GLTF 模型路径
2055
+ * @param {Object} opts.Position - { x: 纬度, y: 经度, z: 高程 }
2056
+ * @param {Array<number>} opts.rotate - [tilt, heading, bank] 旋转角度
2057
+ * @param {Array<number>} opts.scale - [sx, sy, sz] 缩放
2058
+ * @param {Array<number>} [opts.colorDeep=[0,157,187]] - 深水区颜色 [r, g, b](0-255)
2059
+ * @param {number} [opts.flowSpeed=0.1] - 流速
2060
+ */
2061
+ addWaterHandle = async ({
2062
+ gltfUrl, Position, rotate, scale,
2063
+ colorDeep = [0, 157, 187],
2064
+ flowSpeed = 0.1
2065
+ }) => {
2066
+ const waterMtl = Gaea.GaeaResourceLoader.Instance.LoadLoaclResource('res://assets/material/river.tres')
2067
+ const material = Gaea.ShaderMaterial.ConvertBy(waterMtl)
2068
+
2069
+ const element = new Gaea.GLTFElement()
2070
+ element.ResourceName = gltfUrl
2071
+ element.GeographyPosition = new Gaea.Vector3(Position.x, Position.y, Position.z)
2072
+
2073
+ element.OnModelInit(() => {
2074
+ const n = element.GetChildCount()
2075
+ for (let i = 0; i < n; i++) {
2076
+ const meshInstance = Gaea.MeshInstance.ConvertBy(element.Root.GetChild(i))
2077
+ const count = meshInstance.Mesh.GetSurfaceCount()
2078
+ for (let j = 0; j < count; j++) {
2079
+ meshInstance.Mesh.SurfaceSetMaterial(j, material)
2080
+ }
2081
+ }
2082
+ element.Scale = new Gaea.Vector3(scale[0], scale[1], scale[2])
2083
+ element.TiltHeadingBank = new Gaea.Vector3(rotate[0], rotate[1], rotate[2])
2084
+
2085
+ Gaea.World.Instance.SetMtlVector3(material, 'uv_scale', new Gaea.Vector3(-7, -7, 1))
2086
+ Gaea.World.Instance.SetMtlFloat(material, 'flow_speed', flowSpeed)
2087
+ Gaea.World.Instance.SetMtlColor(material, 'color_shallow', new Gaea.Color(0 / 255, 157 / 255, 187 / 255, 1))
2088
+ Gaea.World.Instance.SetMtlColor(material, 'color_deep', new Gaea.Color(colorDeep[0] / 255, colorDeep[1] / 255, colorDeep[2] / 255, 1))
2089
+ Gaea.World.Instance.SetMtlFloat(material, 'transparency_clarity', 3)
2090
+ Gaea.World.Instance.SetMtlFloat(material, 'foam_amount', 0.9)
2091
+ })
2092
+
2093
+ Gaea.World.Instance.RenderableObjectList.AddLast(element)
2094
+ return element
2095
+ };
2096
+
2097
+ // ---- 开门动画 ----
2098
+
2099
+ /**
2100
+ * 模型缓动动画(缩放/位移/旋转)
2101
+ * @param {Object} element - GLTF 元素
2102
+ * @param {Object} Scale - 目标缩放 { x, y, z }
2103
+ * @param {Object} Position - 目标位置 { x, y, z }
2104
+ * @param {Object} translation - 模型相对位移 { x, y, z }
2105
+ * @param {number} time - 动画时间(ms)
2106
+ * @param {Object} Door - 门旋转角度 { x, y, z }
2107
+ * @param {number} floor - 门在模型树中的层级索引
2108
+ */
2109
+ tweentest = (element, Scale = {}, Position = {}, translation = {}, time, Door = {}, floor = 0) => {
2110
+ const { Tween, Easing } = window.TWEEN
2111
+
2112
+ if (Object.values(Scale).length) {
2113
+ const scale = { x: element.Scale.x, y: element.Scale.y, z: element.Scale.z }
2114
+ const tweenScale = new Tween(scale)
2115
+ .to(Scale, time)
2116
+ .easing(Easing.Quadratic.InOut)
2117
+ .onUpdate(() => {
2118
+ element.Scale = new Gaea.Vector3(scale.x, scale.y, scale.z)
2119
+ })
2120
+ .start()
2121
+ .onStart(() => { if (Scale.x !== 0.1) element.Visible = true })
2122
+ .onComplete(() => { if (Scale.x === 0.1) element.Visible = false; this.tweenGroup.remove(tweenScale) })
2123
+ this.tweenGroup.add(tweenScale)
2124
+ }
2125
+
2126
+ if (Object.values(Position).length) {
2127
+ const pos = { x: element.GeographyPosition.x, y: element.GeographyPosition.y, z: element.GeographyPosition.z }
2128
+ const tweenPos = new Tween(pos)
2129
+ .to(Position, time)
2130
+ .easing(Easing.Quadratic.InOut)
2131
+ .onUpdate(() => { element.GeographyPosition = new Gaea.Vector3(pos.x, pos.y, pos.z) })
2132
+ .start()
2133
+ .onComplete(() => { this.tweenGroup.remove(tweenPos) })
2134
+ this.tweenGroup.add(tweenPos)
2135
+ }
2136
+
2137
+ if (Object.values(Door).length) {
2138
+ const doorMesh = element.Root.GetChild(floor)
2139
+ const meshInstance = new Gaea.MeshInstance()
2140
+ meshInstance.InstanceID = doorMesh.InstanceID
2141
+ const doorAngle = { x: meshInstance.RotationDegrees.x, y: meshInstance.RotationDegrees.y, z: meshInstance.RotationDegrees.z }
2142
+ const tweenDoor = new Tween(doorAngle)
2143
+ .to(Door, time)
2144
+ .easing(Easing.Quadratic.InOut)
2145
+ .onUpdate(() => { meshInstance.RotationDegrees = new Gaea.Vector3(doorAngle.x, doorAngle.y, doorAngle.z) })
2146
+ .start()
2147
+ .onComplete(() => { this.tweenGroup.remove(tweenDoor) })
2148
+ this.tweenGroup.add(tweenDoor)
2149
+ }
2150
+
2151
+ if (Object.values(translation).length) {
2152
+ const trans = element.Root
2153
+ ? { x: element.Root.Translation.x, y: element.Root.Translation.y, z: element.Root.Translation.z }
2154
+ : { x: element.Translation.x, y: element.Translation.y, z: element.Translation.z }
2155
+ const tweenTrans = new Tween(trans)
2156
+ .to(translation, time)
2157
+ .easing(Easing.Quadratic.InOut)
2158
+ .onUpdate(() => {
2159
+ if (element.Root) element.Root.Translation = new Gaea.Vector3(trans.x, trans.y, trans.z)
2160
+ else element.Translation = new Gaea.Vector3(trans.x, trans.y, trans.z)
2161
+ })
2162
+ .start()
2163
+ .onComplete(() => { this.tweenGroup.remove(tweenTrans) })
2164
+ this.tweenGroup.add(tweenTrans)
2165
+ }
2166
+ };
2167
+
2168
+ // ---- 模型内置动画 ----
2169
+
2170
+ /**
2171
+ * 执行模型内置动画(如开闸/关闸动画)
2172
+ * @param {Object} opts
2173
+ * @param {string} opts.animationPath - 动画路径,如 '沿浦水闸/AnimationPlayer'
2174
+ * @param {string} opts.animationName - 动画名称,如 '开闸'
2175
+ * @param {number} [opts.speed=1.0] - 动画速度
2176
+ * @param {boolean} [opts.isReversePlay=false] - 是否反向播放
2177
+ */
2178
+ executeModelAnimation = ({ animationPath, animationName, speed = 1.0, isReversePlay = false }) => {
2179
+ const node = Gaea.World.Instance.GetNode(animationPath)
2180
+ const nodeAnimate = Gaea.AnimationPlayer.ConvertBy(node)
2181
+ nodeAnimate.Play(animationName, -1, speed, isReversePlay)
2182
+ };
2036
2183
  }
2037
2184
 
2038
2185
  export default GaeaApp;
@@ -44,6 +44,8 @@ import vDrag from '../directives/vDrag.js';
44
44
 
45
45
  const props = defineProps({
46
46
  appInstance: { type: Object, required: true },
47
+ /** 需要隐藏的分类 key 列表 */
48
+ hiddenCategories: { type: Array, default: () => [] }
47
49
  });
48
50
 
49
51
  const emit = defineEmits(['close', 'node-click']);
@@ -78,7 +80,10 @@ function onCategoryToggle(categoryKey, checked) {
78
80
 
79
81
  const filteredData = computed(() => {
80
82
  const kw = searchKey.value.trim().toLowerCase();
81
- return categories.value.map(rootNode => {
83
+ const hidden = new Set(props.hiddenCategories);
84
+ return categories.value
85
+ .filter(rootNode => rootNode.visible !== false && !hidden.has(rootNode.key))
86
+ .map(rootNode => {
82
87
  if (!kw) {
83
88
  rootNode.filteredChildren = rootNode.children || [];
84
89
  return rootNode;
@@ -41,13 +41,19 @@ export default function gaeaVitePlugin() {
41
41
  name: 'vite-plugin-gaea',
42
42
  enforce: 'pre',
43
43
 
44
- config() {
45
- return {
46
- server: { headers: COEP_HEADERS },
47
- };
48
- },
49
-
50
44
  configureServer(server) {
45
+ // COEP/COOP 头只对非 WebSocket 请求设置,避免破坏 Vite HMR
46
+ server.middlewares.stack.unshift({
47
+ route: '',
48
+ handle: function coepMiddleware(req, res, next) {
49
+ if (req.headers.upgrade) return next(); // 跳过 WebSocket 升级请求
50
+ for (const [key, val] of Object.entries(COEP_HEADERS)) {
51
+ res.setHeader(key, val);
52
+ }
53
+ next();
54
+ },
55
+ });
56
+
51
57
  server.middlewares.stack.unshift({
52
58
  route: '',
53
59
  handle: function gaeaServe(req, res, next) {