bill vor 3 Jahren
Ursprung
Commit
04a013cf11

+ 2 - 2
src/api/guide.ts

@@ -8,8 +8,8 @@ import {
 
 export interface GuidePath {
   id: string,
-  position: ScenePos
-  target: ScenePos
+  position: SceneLocalPos
+  target: SceneLocalPos
   time: number
   speed: number
   cover: string

+ 1 - 1
src/api/model.ts

@@ -54,7 +54,7 @@ export const fetchModels = async () => {
       position: { x: 1, y: 1, z: 1},
       opacity: 0.1,
       bottom: 1,
-      show: true
+      show: false
     },
     {
       id: '124',

+ 9 - 7
src/components/tagging/list.vue

@@ -1,20 +1,22 @@
 <template>
   <template v-if="custom.showTaggings">
-    <Sign 
-      v-for="(pos, index) in tagging.positions" 
-      :key="index"
-      :tagging="tagging"
-      :scene-pos="pos"
-    />
+    <template v-for="(pos, index) in tagging.positions" :key="pos.id">
+      <Sign 
+        v-if="getModel(pos.modelId)?.loaded"
+        :tagging="tagging"
+        :scene-pos="pos"
+      />
+    </template>
   </template>
 </template>
 
 <script lang="ts" setup>
-import { Tagging } from '@/store';
+import { Tagging, models } from '@/store';
 import Sign from './sign.vue'
 import { custom } from '@/env'
 
 defineProps<{ tagging: Tagging }>()
 
+const getModel = (modelId: string) => models.value.find(model => model.id === modelId)
 
 </script>

+ 3 - 2
src/components/tagging/sign.vue

@@ -42,7 +42,7 @@ import { computed, ref } from 'vue'
 import UIBubble from 'bill/components/bubble/index.vue'
 import Images from '@/views/tagging/images.vue'
 import Preview, { MediaType } from '../static-preview/index.vue'
-import { Tagging, getTaggingStyle } from '@/store';
+import { Tagging, getTaggingStyle, modelsLoaded } from '@/store';
 import { getFileUrl } from '@/utils'
 import { sdk } from '@/sdk'
 
@@ -56,7 +56,8 @@ const posStyle = computed(() => {
     x: 700,
     y: 400
   } 
-  console.log(sdk.getScreenByPosition(props.scenePos.localPos, props.scenePos.modelId))
+  console.error(modelsLoaded.value)
+  console.error(sdk.getScreenByPosition(props.scenePos.localPos, props.scenePos.modelId))
   return {
     left: screenPos.x + 'px',
     top: screenPos.y + 'px',

+ 5 - 1
src/env/index.ts

@@ -11,6 +11,8 @@ export const showLeftCtrlPanoStack = stackFactory(ref<boolean>(true))
 export const showRightCtrlPanoStack = stackFactory(ref<boolean>(true))
 export const showTaggingsStack = stackFactory(ref<boolean>(true))
 export const currentModelStack = stackFactory(ref<Model | null>(null))
+export const showModelsMapStack = stackFactory(ref<Map<Model, boolean>>(new Map))
+export const showModelsChangeStoreStack = stackFactory(ref<boolean>(false))
 
 export const custom = flatStacksValue({
   viewMode: viewModeStack,
@@ -20,6 +22,8 @@ export const custom = flatStacksValue({
   showLeftCtrlPano: showLeftCtrlPanoStack,
   shwoRightCtrlPano: showRightCtrlPanoStack,
   showTaggings: showTaggingsStack,
-  currentModel: currentModelStack
+  currentModel: currentModelStack,
+  showModelsMap: showModelsMapStack,
+  showModelsChangeStore: showModelsChangeStoreStack
 })
 

+ 15 - 9
src/layout/model-list/index.vue

@@ -17,9 +17,9 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, ref, toRaw } from 'vue'
+import { computed, watchEffect } from 'vue'
 import { LeftPano } from '@/layout'
-import { models } from '@/store'
+import { models, getModelShowVariable } from '@/store'
 import { custom } from '@/env'
 import { getSceneModel } from '@/sdk'
 import List from '@/components/list/index.vue'
@@ -35,16 +35,22 @@ const modelList = computed(() =>
 )
 
 const modelChangeSelect = (model: Model) => {
-  if (custom.currentModel) {
-    getSceneModel(custom.currentModel)?.changeSelect(false)
+  if (getModelShowVariable(model).value) {
+    if (custom.currentModel !== model) {
+      getSceneModel(model)?.changeSelect(true)
+      custom.currentModel = model
+    } else {
+      getSceneModel(custom.currentModel)?.changeSelect(false)
+      custom.currentModel = null
+    }
   }
-  if (toRaw(custom.currentModel) !== toRaw(model)) {
-    getSceneModel(model)?.changeSelect(true)
-    custom.currentModel = model
-  } else {
+}
+
+watchEffect(() => {
+  if (custom.currentModel && !getModelShowVariable(custom.currentModel).value) {
     custom.currentModel = null
   }
-}
+})
 
 const modelDelete = (model: Model) => {
   const index = models.value.indexOf(model)

+ 6 - 2
src/layout/model-list/sign.vue

@@ -2,7 +2,7 @@
   <div class="model-header">
     <p>{{ model.title }}</p>
     <div class="model-action" @click.stop>
-      <ui-input type="checkbox" v-model="model.show"/>
+      <ui-input type="checkbox" v-model="show" />
       <ui-icon type="del" ctrl @click="$emit('delete')" />
     </div>
   </div>
@@ -14,10 +14,12 @@
 </template>
 
 <script lang="ts" setup>
+import { getModelShowVariable } from '@/store'
+
 import type { Model } from '@/store'
 
 type ModelProps = { model: Model }
-defineProps<ModelProps>()
+const props = defineProps<ModelProps>()
 
 type ModelEmits = {
   (e: 'changeSelect', selected: boolean): void
@@ -25,6 +27,8 @@ type ModelEmits = {
 }
 defineEmits<ModelEmits>();
 
+const show = getModelShowVariable(props.model)
+
 </script>
 
 <style lang="scss" scoped src="./style.scss"></style>

+ 27 - 8
src/sdk/association.ts

@@ -1,6 +1,6 @@
-import { models, taggings, isEdit, sysBus } from '@/store'
+import { models, taggings, isEdit, sysBus, getModelShowVariable } from '@/store'
 import { toRaw, watchEffect, ref, watch } from 'vue'
-import { viewModeStack } from '@/env'
+import { viewModeStack, custom } from '@/env'
 import { 
   mount, 
   diffArrayChange, 
@@ -29,8 +29,18 @@ const associationModels = (sdk: SDK) => {
       const sceneModel = sdk.addModel(itemRaw)
       sceneModelMap.set(itemRaw, sceneModel)
 
-      sceneModel.bus.on('position', pos => item.position = pos)
-      sceneModel.bus.on('rotation', rot => item.rotation = rot)
+      sceneModel.bus.on('transformChanged', transform => {
+        Object.assign(item, transform)
+      })
+      sceneModel.bus.on('changeSelect', select => {
+        if (custom.currentModel === item && !select) {
+          custom.currentModel = null
+        } else if (custom.currentModel !== item && select) {
+          custom.currentModel = item
+        }
+      })
+      sceneModel.bus.on('loadDone', () => item.loaded = true)
+      sceneModel.bus.on('loadProgress', progress => item.progress = progress)
     }
     for (const item of deleted) {
       getSceneModel(item)?.destroy()
@@ -38,10 +48,19 @@ const associationModels = (sdk: SDK) => {
   })
   
   arrayChildEffectScope(getModels, item => {
-    watchEffect(() => getSceneModel(item)?.changeBottom(item.bottom))
-    watchEffect(() => getSceneModel(item)?.changeOpacity(item.opacity))
-    watchEffect(() => getSceneModel(item)?.changeScale(item.scale))
-    watchEffect(() => getSceneModel(item)?.changeShow(item.show))
+    const stopLoadedWatch = watch(
+      () => item.loaded,
+      (loaded) => {
+        if (loaded) {
+          const modelShow = getModelShowVariable(item)
+          watchEffect(() => getSceneModel(item)?.changeBottom(item.bottom))
+          watchEffect(() => getSceneModel(item)?.changeOpacity(item.opacity))
+          watchEffect(() => getSceneModel(item)?.changeScale(item.scale))
+          watchEffect(() => getSceneModel(item)?.changeShow(modelShow.value))
+          stopLoadedWatch()
+        }
+      }
+    )
   })
 }
 

+ 18 - 4
src/sdk/index.ts

@@ -9,12 +9,24 @@ import type { Emitter } from 'mitt'
 type SceneModelAttrs = ModelAttrs & { select: boolean }
 export type SceneModel = ToChangeAPI<Omit<SceneModelAttrs, 'position' | 'rotation'>>
   & { 
-    bus: Emitter<Pick<SceneModelAttrs, 'position' | 'rotation' | 'select'>> 
+    bus: Emitter<
+      Pick<SceneModelAttrs, 'select'> & 
+      { 
+        loadDone: void, 
+        loadProgress: number,
+        changeSelect: boolean,
+        transformChanged: {
+          position?: SceneLocalPos,
+          scale?: number,
+          rotation?: SceneLocalPos,
+          bottom?: number
+        }
+      }
+    > 
     destroy: () => void 
     enterRotateMode: () => void
-    leaveRotateMode: () => void
     enterMoveMode: () => void
-    leaveMoveMode: () => void
+    leaveTransform: () => void
   }
 
 
@@ -80,7 +92,9 @@ export const initialSDK = async (props: InialSDKProps) => {
   await Promise.all(libs.map(loadLib))
   await loadLib(`/lib/potree/potree.js`)
 
-  sdk = cover(props.layout) as unknown as SDK
+  const localSdk = cover(props.layout) as unknown as SDK
+
+  sdk = localSdk
   sdk.layout = props.layout
   setup(sdk, presetViewElement(props.layout))
 }

+ 46 - 4
src/store/model.ts

@@ -1,5 +1,6 @@
-import { ref } from 'vue'
+import { computed, ref, watchPostEffect } from 'vue'
 import { autoSetModeCallback } from './sys'
+import { showModelsMapStack, custom } from '@/env'
 import { 
   fetchModels, 
   postAddModel, 
@@ -15,10 +16,32 @@ import {
   recoverStoreItems
 } from '@/utils'
 
-import type { Models } from '@/api'
+import type { Model as SModel } from '@/api'
+
+export type Model = SModel & { loaded: boolean, progress: number }
+export type Models = Model[]
 
 export const models = ref<Models>([])
 
+export const getModelShowVariable = (model: Model) => 
+  computed({
+    get: () => custom.showModelsChangeStore 
+      ? model.show 
+      : custom.showModelsMap.get(model) || false,
+    set: (show: boolean) => {
+      if (custom.showModelsChangeStore) {
+        model.show = show
+      } else {
+        custom.showModelsMap.set(model, show)
+      }
+    }
+  })
+  
+export const modelsLoaded = ref(false)
+watchPostEffect(() => {
+  modelsLoaded.value = models.value.every(model => model.loaded === true)
+})
+
 let bcModels: Models = []
 export const getBackupModels = () => bcModels
 export const backupModels = () => {
@@ -33,7 +56,23 @@ export const recoverModels = recoverStoreItems(models, getBackupModels)
 export const addModel = addStoreItem(models, postAddModel)
 export const updateModel = updateStoreItem(models, postUpdateModels)
 export const deleteModel = deleteStoreItem(models, model => postDeleteModel(model.id))
-export const initialModels = fetchStoreItems(models, fetchModels, backupModels)
+export const initialModels = fetchStoreItems(
+  models, 
+  fetchModels, 
+  () => {
+    const showModelsMap = new Map<Model, boolean>()
+    for (const model of models.value) {
+      showModelsMap.set(model, model.show)
+    }
+    showModelsMapStack.push(ref(showModelsMap))
+    backupModels()
+  }, 
+  smodels => smodels.map(model => ({ 
+    ...model, 
+    loaded: false, 
+    progress: 0,
+  })),
+)
 export const saveModels = saveStoreItems(
   models,
   getBackupModels,
@@ -43,11 +82,14 @@ export const saveModels = saveStoreItems(
     delete: deleteModel,
   }
 )
+
+
 export const autoSaveModels = autoSetModeCallback(models, {
   backup: backupModels,
   recovery: recoverModels,
   save: saveModels,
+  isUpdate: () => modelsLoaded.value,
 })
 
 export { ModelType, ModelTypeDesc } from '@/api'
-export type { Model, Models, ModelAttrs } from '@/api'
+export type { ModelAttrs } from '@/api'

+ 5 - 3
src/store/sys.ts

@@ -3,6 +3,8 @@ import { asyncBusFactory } from '@/utils'
 import { Dialog } from 'bill/index'
 import { useViewStack } from '@/hook'
 
+import type { UnwrapRef } from 'vue'
+
 const Flags = {
   EDIT: 0b10,
   // 已经保存,是最新的
@@ -65,7 +67,7 @@ export const leave = async () => {
 export type AutoSetModeSetting<T> = {
   save: () => any
   leave?: () => any
-  isUpdate?: (newCurrent: T, oldCurrent: T) => boolean
+  isUpdate?: (newCurrent: UnwrapRef<T>, oldCurrent?: UnwrapRef<T>) => boolean
   auto?: boolean
   backup?: () => void
   recovery?: () => void
@@ -89,7 +91,7 @@ export const autoSetModeCallback = <T extends object>(current: T, setting: AutoS
     isSave = false
   }
 
-  const handler = (newv: T, oldv: T) => {
+  const handler = (newv: UnwrapRef<T>, oldv?: UnwrapRef<T>) => {
     if (isSave) return
     if (!setting.isUpdate || setting.isUpdate(newv, oldv)) {
       isEdit.value || enterEdit()
@@ -101,7 +103,7 @@ export const autoSetModeCallback = <T extends object>(current: T, setting: AutoS
 
   return () => {
     setting.backup && setting.backup()
-    return watch(current, handler, { deep: true })
+    return watch(current as UnwrapRef<T>, handler, { deep: true })
   }
 }
 

+ 25 - 3
src/utils/store-help.ts

@@ -101,11 +101,33 @@ export function deleteStoreItem <T extends {id: any}, K extends {id: any} = T>(
     storeSecurityDelete(items.value, item)
   }
 }
-
-export const fetchStoreItems = <T extends {id: any}>(items: Ref<T[]>, fetchAction: () => Promise<T[]>, callback?: () => void) => {
+export function fetchStoreItems <T extends {id: any}, K extends {id: any} = T>(
+  items: Ref<T[]>, 
+  fetchAction: () => Promise<T[]>, 
+  callback: (() => void) | null,
+): (item: T) => Promise<void>
+export function fetchStoreItems <T extends {id: any}, K extends {id: any} = T>(
+  items: Ref<T[]>, 
+  fetchAction: () => Promise<K[]>, 
+  callback: (() => void) | null,
+  transform: (items: K[]) => Promise<T[]> | T[],
+): (item: T) => Promise<void>
+export function fetchStoreItems <T extends {id: any}, K extends {id: any} = T>(
+  items: Ref<T[]>, 
+  fetchAction: () => Promise<K[]>, 
+  callback: (() => void) | null,
+  transform?: (items: K[]) => Promise<T[]> | T[],
+) {
   return async () => {
     const fetchItems = await fetchAction()
-    items.value = fetchItems
+
+    let actionData: T[]
+    if (transform) {
+      actionData = await transform(fetchItems)
+    } else {
+      actionData = fetchItems as unknown as T[]
+    }
+    items.value = actionData
     callback && callback()
   }
 }

+ 9 - 12
src/views/merge/index.vue

@@ -29,10 +29,11 @@
 <script lang="ts" setup>
 import { RightPano } from '@/layout'
 import { autoSaveModels } from '@/store'
+import { togetherCallback } from '@/utils'
 import Actions from '@/components/actions/index.vue'
 import { getSceneModel } from '@/sdk'
 import { useViewStack } from '@/hook'
-import { showLeftCtrlPanoStack, showLeftPanoStack, custom } from '@/env'
+import { showLeftCtrlPanoStack, showLeftPanoStack, showModelsChangeStoreStack, custom } from '@/env'
 import { ref } from 'vue'
 
 import type { ActionsProps } from '@/components/actions/index.vue'
@@ -46,7 +47,7 @@ const actionItems: ActionsProps['items'] = [
     text: '移动',
     action: () => {
       getSceneModel(custom.currentModel)?.enterMoveMode()
-      return () => getSceneModel(custom.currentModel)?.leaveMoveMode()
+      return () => getSceneModel(custom.currentModel)?.leaveTransform()
     }
   },
   {
@@ -54,20 +55,16 @@ const actionItems: ActionsProps['items'] = [
     text: '旋转',
     action: () => {
       getSceneModel(custom.currentModel)?.enterRotateMode()
-      return () => getSceneModel(custom.currentModel)?.leaveRotateMode()
+      return () => getSceneModel(custom.currentModel)?.leaveTransform()
     }
   },
 ]
 
-useViewStack(() => {
-  const pops = [
-    showLeftCtrlPanoStack.push(ref(false)),
-    showLeftPanoStack.push(ref(true))
-  ]
-  return () => {
-    pops.forEach(pop => pop())
-  }
-})
+useViewStack(() => togetherCallback([
+  showLeftCtrlPanoStack.push(ref(false)),
+  showLeftPanoStack.push(ref(true)),
+  showModelsChangeStoreStack.push(ref(true))
+]))
 useViewStack(autoSaveModels)
 
 </script>