bill пре 3 година
родитељ
комит
bf0a92a34c

Разлика између датотеке није приказан због своје велике величине
+ 1 - 23
src/api/guide.ts


+ 6 - 5
src/api/tagging.ts

@@ -8,6 +8,10 @@ import {
 
 import type { Model } from './model'
 
+export interface TaggingPosition {
+  modelId: Model['id']
+  localPos: SceneLocalPos
+}
 export interface Tagging {
   id: string
   styleId: string
@@ -17,10 +21,7 @@ export interface Tagging {
   method: string
   principal: string
   images: string[],
-  positions: {
-    modelId: Model['id']
-    localPos: SceneLocalPos
-  }[]
+  positions: TaggingPosition[]
 }
 
 export type Taggings = Tagging[]
@@ -43,7 +44,7 @@ export const fetchTaggings = async () => {
       ],
       positions: [
         { 
-          modelId: '123',
+          modelId: '124',
           localPos: { x: 1, y: 1, z: 1 }
         }
       ]

+ 0 - 1
src/components/bill-ui/components/message/index.js

@@ -38,7 +38,6 @@ Message.use = function use(app) {
     const existsShows = []
     const oneShow = config => {
         const key = config.type + config.msg
-        console.log(existsShows)
         if (!existsShows.includes(key)) {
             const index = existsShows.length
             existsShows[index] = key

+ 6 - 3
src/components/tagging/list.vue

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

+ 24 - 15
src/components/tagging/sign.vue

@@ -1,5 +1,6 @@
 <template>
   <div 
+    v-if="posStyle"
     class="hot-item pc" 
     :style="posStyle" 
     @mouseenter="isHover = true"
@@ -9,7 +10,7 @@
     <div @click.stop>
       <UIBubble
         class="hot-bubble pc" 
-        :show="!~pullIndex && (isHover || show)" 
+        :show="showContent" 
         type="left" 
         level="center"
       >
@@ -38,36 +39,44 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, ref } from 'vue'
+import { computed, ref, watchEffect } 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, modelsLoaded } from '@/store';
+import { Tagging, getTaggingStyle } from '@/store';
 import { getFileUrl } from '@/utils'
 import { sdk } from '@/sdk'
+import { custom, showTaggingPositionsStack } from '@/env'
 
 export type SignProps = { tagging: Tagging, scenePos: Tagging['positions'][number], show?: boolean }
 
 
 const props = defineProps<SignProps>()
-
-const posStyle = computed(() => {
-  const screenPos = {
-    x: 700,
-    y: 400
-  } 
-  console.error(modelsLoaded.value)
-  console.error(sdk.getScreenByPosition(props.scenePos.localPos, props.scenePos.modelId))
-  return {
-    left: screenPos.x + 'px',
-    top: screenPos.y + 'px',
+const posStyle = ref<null | { left: string, top: string}>(null)
+const updatePosStyle = () => {
+  const screenPos = sdk.getScreenByPosition(props.scenePos.localPos, props.scenePos.modelId)
+  if (!screenPos) {
+    posStyle.value = null
+  } else {
+    posStyle.value = {
+      left: screenPos.pos.x + 'px',
+      top: screenPos.pos.y + 'px',
+    }
   }
+}
+watchEffect(updatePosStyle)
+const showContent = computed(() => {
+  return !~pullIndex.value 
+    && (
+      isHover.value || show.value 
+      || custom.showTaggingPositions.has(props.scenePos)
+    )
 })
+sdk.sceneBus.on('cameraChange', updatePosStyle)
 
 const taggingStyle = getTaggingStyle(props.tagging.styleId)
 
 const pullIndex = ref(-1)
-
 const isHover = ref(false)
 const show = ref(false)
 

+ 5 - 2
src/env/index.ts

@@ -1,7 +1,7 @@
 import { stackFactory, flatStacksValue } from '@/utils'
 import { ref } from 'vue'
 
-import type { Model } from '@/store'
+import type { Model, TaggingPosition } from '@/store'
 
 export const viewModeStack = stackFactory(ref<'full' | 'auto'>('auto'))
 export const showToolbarStack = stackFactory(ref<boolean>(false))
@@ -13,6 +13,8 @@ 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 showTaggingPositionsStack = stackFactory(ref<WeakSet<TaggingPosition>>(new WeakSet()))
+// export const showModelsChangeStoreStack = stackFactory
 
 export const custom = flatStacksValue({
   viewMode: viewModeStack,
@@ -24,6 +26,7 @@ export const custom = flatStacksValue({
   showTaggings: showTaggingsStack,
   currentModel: currentModelStack,
   showModelsMap: showModelsMapStack,
-  showModelsChangeStore: showModelsChangeStoreStack
+  showModelsChangeStore: showModelsChangeStoreStack,
+  showTaggingPositions: showTaggingPositionsStack
 })
 

+ 9 - 4
src/sdk/index.ts

@@ -41,16 +41,21 @@ export interface SceneGuide {
 }
 
 export type ScenePos = { localPos: SceneLocalPos, modelId: Model['id'] }
-export type ScreenPos = { screenPos: ScreenLocalPos, modelId: Model['id'] }
+export type ScreenPos = { 
+  trueSide: boolean,
+  pos: ScreenLocalPos, 
+  modelId: Model['id'] 
+}
 
 export interface SDK {
   layout: HTMLDivElement,
+  sceneBus: Emitter<{ 'cameraChange': void }>
   addModel: (props: AddModelProps) => SceneModel
-  getPositionByScreen: (screenPos: ScreenPos['screenPos'], modelId?: Model['id']) => ScenePos | null
-  getScreenByPosition: (localPos: ScenePos['localPos'], modelId?: Model['id']) => ScreenLocalPos | null
+  getPositionByScreen: (screenPos: ScreenLocalPos, modelId?: Model['id']) => ScenePos | null
+  getScreenByPosition: (localPos: SceneLocalPos, modelId?: Model['id']) => ScreenPos | null
   screenshot: (width: number, height: number) => Promise<string>
   getPose: () => { position: SceneLocalPos, target: SceneLocalPos }
-  comeTo: (pos: { position: SceneLocalPos; target: SceneLocalPos; dur?: number }) => void
+  comeTo: (pos: { position: SceneLocalPos; target?: SceneLocalPos; dur?: number, modelId?: Model['id'] }) => void
   enterSceneGuide: (data: SceneGuidePath[]) => SceneGuide
 }
   

+ 4 - 4
src/store/guide.ts

@@ -1,5 +1,5 @@
 import { ref } from 'vue'
-import { TemploraryID } from './sys'
+import { createTemploraryID } from './sys'
 import { autoSetModeCallback } from './sys'
 import { 
   fetchGuides, 
@@ -29,15 +29,15 @@ export type Guides = Guide[]
 export const guides = ref<Guides>([])
 
 export const createGuide = (guide: Partial<Guide> = {}): Guide => ({
-  id: TemploraryID,
-  title: '',
+  id: createTemploraryID(),
+  title: `路径${guides.value.length + 1}`,
   cover: '',
   paths: [],
   ...guide
 })
 
 export const createGuidePath = (path: Partial<GuidePath> = {}): GuidePath => ({
-  id: TemploraryID,
+  id: createTemploraryID(),
   cover: '',
   time: 1,
   speed: 1,

+ 4 - 1
src/store/sys.ts

@@ -19,7 +19,10 @@ export const isLogin = computed(() => !!(mode.value & Flags.LOGIN))
 export const isOld = computed(() => !(mode.value & Flags.NOW))
 export const isNow = computed(() => !!(mode.value & Flags.NOW))
 export const title = '融合平台'
-export const TemploraryID = '-1'
+
+let currentTempIndex = 0
+export const isTemploraryID = (id: string) => id.includes('__currentTempIndex__')
+export const createTemploraryID = () => `__currentTempIndex__${currentTempIndex++}`
 
 
 export const sysBus = asyncBusFactory<{ save: void; leave: void }>()

+ 4 - 3
src/store/tagging.ts

@@ -1,5 +1,5 @@
 import { ref } from 'vue'
-import { autoSetModeCallback, TemploraryID } from './sys'
+import { autoSetModeCallback, createTemploraryID } from './sys'
 import { 
   fetchTaggings, 
   postAddTagging,
@@ -21,12 +21,13 @@ import type { Tagging as STagging } from '@/api'
 
 export type Tagging = LocalMode<STagging, 'images'>
 export type Taggings = Tagging[]
+export type { TaggingPosition } from '@/api'
 
 export const taggings = ref<Taggings>([])
 
 export const createTagging = (tagging: Partial<Tagging> = {}): Tagging => ({
-  id: TemploraryID,
-  title: '',
+  id: createTemploraryID(),
+  title: ``,
   styleId: '',
   desc: '',
   part: '',

+ 3 - 0
src/utils/index.ts

@@ -42,6 +42,9 @@ export const getFileUrl = (file: LocalFile | string) =>
     ? file
     : file.url
 
+
+export const asyncTimeout = (mis: number = 0) => new Promise(resolve => setTimeout(resolve, mis))
+
 export * from './store-help'
 export * from "./stack";
 export * from "./loading";

+ 6 - 2
src/views/guide/edit-paths.vue

@@ -56,7 +56,7 @@
 <script setup lang="ts">
 import { loadPack, togetherCallback, getFileUrl } from '@/utils'
 import { sdk, playSceneGuide, pauseSceneGuide, isScenePlayIng } from '@/sdk'
-import { createGuidePath, TemploraryID, useAutoSetMode, guides, enterOld } from '@/store'
+import { createGuidePath, isTemploraryID, useAutoSetMode, guides, enterOld } from '@/store'
 import { Dialog } from 'bill/index'
 import { useViewStack } from '@/hook'
 import { nextTick, ref, toRaw, watchEffect } from 'vue'
@@ -80,7 +80,11 @@ useViewStack(() =>
 useAutoSetMode(paths, {
   save() {
     props.data.paths = paths.value
-    if (props.data.id === TemploraryID) {
+    if (paths.value.length) {
+      props.data.cover = paths.value[0].cover
+    }
+    if (isTemploraryID(props.data.id)) {
+      console.log(JSON.stringify(props.data))
       guides.value.push(props.data)
     }
   }

+ 63 - 4
src/views/tagging/index.vue

@@ -31,6 +31,7 @@
         @edit="editTagging = tagging"
         @delete="deleteTagging(tagging)"
         @select="selectTagging = tagging"
+        @flyPositions="flyTaggingPositions(tagging)"
       />
     </ui-group>
   </RightFillPano>
@@ -50,11 +51,20 @@ import { Message } from 'bill/index'
 import { RightFillPano } from '@/layout'
 import { togetherCallback } from '@/utils'
 import { useViewStack } from '@/hook'
-import { taggings, TemploraryID, Tagging, autoSaveTaggings, createTagging, models, enterEdit } from '@/store'
 import { ref, watch } from 'vue';
 import { sdk } from '@/sdk'
 import { 
+  taggings, 
+  isTemploraryID, 
+  Tagging, 
+  autoSaveTaggings, 
+  createTagging,
+  enterEdit, 
+Model
+} from '@/store'
+import { 
   custom, 
+  showTaggingPositionsStack,
   showLeftCtrlPanoStack, 
   showLeftPanoStack,
   currentModelStack,
@@ -66,7 +76,7 @@ import {
 const editTagging = ref<Tagging | null>(null)
 const saveHandler = (tagging: Tagging) => {
   if (!editTagging.value) return;
-  if (editTagging.value.id === TemploraryID) {
+  if (isTemploraryID(editTagging.value.id)) {
     taggings.value.push(tagging)
   } else {
     Object.assign(editTagging.value, tagging)
@@ -79,18 +89,67 @@ const deleteTagging = (tagging: Tagging) => {
   taggings.value.splice(index, 1)
 }
 
+let stopFlyTaggingPositions: () => void
+const flyTaggingPositions = (tagging: Tagging) => {
+  stopFlyTaggingPositions && stopFlyTaggingPositions()
+
+  let isStop = false
+
+  const flyIndex = (i: number) => {
+    if (isStop || i >= tagging.positions.length) {
+      return;
+    }
+    const position = tagging.positions[i]
+    const pop = showTaggingPositionsStack.push(ref(new WeakSet([position])))
+    sdk.comeTo({ 
+      position: position.localPos, 
+      modelId: position.modelId,
+      dur: 300
+    })
+    
+    console.log('改变了', custom.showTaggingPositions.has(position))
+    setTimeout(() => {
+      pop()
+      flyIndex(i + 1)
+    }, 2000)
+  }
+  flyIndex(0)
+  stopFlyTaggingPositions = () => isStop = true
+}
+
+const stopFlyKeyupHandler = (ev: KeyboardEvent) => {
+  ev.code === 'Escape' && stopFlyTaggingPositions && stopFlyTaggingPositions()
+}
+useViewStack(() => {
+  document.documentElement.addEventListener('keyup', stopFlyKeyupHandler, false)
+  return () => document.documentElement.removeEventListener('keydown', stopFlyKeyupHandler, false)
+})
 
 const selectTagging = ref<Tagging | null>(null)
 watch(selectTagging, (a, b, onCleanup) => {
   if (selectTagging.value) {
+    const leave = () => selectTagging.value = null
+
+    let currentModel: Model | null = custom.currentModel
+    if (!currentModel) {
+      for (const [model, show] of custom.showModelsMap.entries()) {
+        show && (currentModel = model)
+      }
+      if (!currentModel) {
+        Message.error('请显示要添加热点的模型') 
+        leave()
+        return
+      }
+    }
+
     const pop = togetherCallback([
       showLeftCtrlPanoStack.push(ref(true)), 
       showLeftPanoStack.push(ref(true)),
-      currentModelStack.push(ref(custom.currentModel || models.value[0])),
+      currentModelStack.push(ref(currentModel)),
       showRightCtrlPanoStack.push(ref(false)),
       showRightPanoStack.push(ref(false))
     ])
-    const leave = () => selectTagging.value = null
+
     const clickHandler = (ev: MouseEvent) => {
       const position = sdk.getPositionByScreen({
         x: ev.clientX,

+ 6 - 3
src/views/tagging/sign.vue

@@ -4,7 +4,7 @@
       <img :src="getFileUrl(tagging.images[0])">
       <div>
         <p>{{ tagging.title }}</p>
-        <a>放置:{{ tagging.positions.length }}</a>
+        <a @click.stop="$emit('flyPositions')">放置:{{ tagging.positions.length }}</a>
       </div>
     </div>
     <div class="actions" @click.stop>
@@ -20,15 +20,17 @@
 
 <script setup lang="ts">
 import { Tagging } from '@/store'
-import { getFileUrl } from '@/utils'
+import { getFileUrl, asyncTimeout } from '@/utils'
+import { sdk } from '@/sdk'
 
 
-defineProps<{ tagging: Tagging, selected?: boolean }>()
+const props = defineProps<{ tagging: Tagging, selected?: boolean }>()
 
 const emit = defineEmits<{ 
   (e: 'delete'): void 
   (e: 'edit'): void
   (e: 'select'): void
+  (e: 'flyPositions'): void
 }>()
 
 const menus = [
@@ -40,6 +42,7 @@ const actions = {
   delete: () => emit('delete')
 }
 
+
 </script>
 
 <style lang="scss" scoped src="./style.scss"></style>

+ 2 - 2
src/views/tagging/styles.vue

@@ -64,7 +64,7 @@
 
 <script setup lang="ts">
 import { TaggingStyle, TaggingStyles } from '@/store'
-import { TemploraryID } from '@/store'
+import { createTemploraryID } from '@/store'
 import { ref, computed, defineEmits } from 'vue'
 import { Cropper } from 'bill/index'
 
@@ -102,7 +102,7 @@ const iconUpload = async ({ file, preview }: LocalTaggingStyle['icon']) => {
   const data = await Cropper.open(preview)
   if (data) {
     const item = {
-      id: TemploraryID,
+      id: createTemploraryID(),
       icon: { file: data[0], preview: data[1] },
       name: file.name,
       default: false,