bill il y a 3 ans
Parent
commit
caa8755787

+ 2 - 1
src/api/constant.ts

@@ -36,11 +36,12 @@ export const DELETE_TAGGING_POINT = `/laser/caseTagPoint/${params.m}/delete`
 // 标注样式类型列表
 export const TAGGING_STYLE_LIST = ''
 
-// 路径
+// 导览
 export const GUIDE_LIST = `/laser/fusionGuide/${params.m}/list/${params.fushId}`
 export const INSERT_GUIDE = `/laser/fusionGuide/${params.m}/add`
 export const UPDATE_GUIDE = `/laser/fusionGuide/${params.m}/edit`
 export const DELETE_GUIDE = `/laser/fusionGuide/${params.m}/delete`
 
+
 // 文件上传
 export const UPLOAD_FILE = `/laser/oss/${params.m}/fuse-code/upload/fire`

+ 49 - 10
src/api/guide.ts

@@ -1,4 +1,5 @@
 import axios from './instance'
+import { params } from '@/env'
 import { 
   GUIDE_LIST,
   INSERT_GUIDE,
@@ -6,6 +7,29 @@ import {
   DELETE_GUIDE,
 } from './constant'
 
+
+ interface ServiceGuidePath {
+  id: number,
+  position: SceneLocalPos
+  target: SceneLocalPos
+  time: number
+  speed: number
+  cover: string
+}
+
+interface ServiceGuide {
+  id: number
+  cover: string
+  title: string
+  paths: {
+    id: number,
+    position: SceneLocalPos
+    target: SceneLocalPos
+    time: number
+    speed: number
+    cover: string
+  }[]
+}
 export interface GuidePath {
   id: string,
   position: SceneLocalPos
@@ -25,27 +49,42 @@ export interface Guide {
 export type Guides = Guide[]
 export type GuidePaths = GuidePath[]
 
+const serviceToLocal = (serviceGuide: ServiceGuide): Guide => ({
+  ...serviceGuide,
+  id: serviceGuide.id.toString(),
+  paths: serviceGuide.paths.map(path => ({...path, id: path.id.toString()}))
+})
+
+const localToService = (guide: Guide): ServiceGuide => ({
+  ...guide,
+  id: Number(guide.id),
+  paths: guide.paths.map(path => ({...path, id: Number(path.id)}))
+})
+
+
 export const fetchGuides = async () => {
-  axios.post<Guides>(GUIDE_LIST)
-  return []
+  const guides = await axios.post<ServiceGuide[]>(GUIDE_LIST, {})
+  return guides.map(serviceToLocal)
 }
 
 
 export const postAddGuide = async (guide: Guide) => {
-  console.log('add')
-   axios.post<Guide>(INSERT_GUIDE, guide)
-   return guide
+  const addData = {
+    ...guide,
+    fusionId: params.fushId,
+    id: undefined,
+    paths: guide.paths.map(path => ({...path, id: undefined}))
+   }
+   const serviceData = await axios.post<ServiceGuide>(INSERT_GUIDE, addData)
+   return serviceToLocal(serviceData)
 }
 
 export const postUpdateGuide = async (guide: Guide) => {
-  console.log('update')
-  // return axios.post<undefined>(UPDATE_GUIDE, guide)
-  // return
+  return axios.post<undefined>(UPDATE_GUIDE, { ...localToService(guide)})
 }
 
 export const postDeleteGuide = (id: Guide['id']) => {
-  console.log('delete')
-  return axios.post<undefined>(DELETE_GUIDE)
+  return axios.post<undefined>(DELETE_GUIDE, { ids: [id.toString()] })
 }
 
   

+ 0 - 1
src/components/tagging/sign.vue

@@ -77,7 +77,6 @@ model && watch(model, updatePosStyle, { deep: true })
 
 
 const showContent = computed(() => {
-  console.error(!~pullIndex.value , (isHover.value || custom.showTaggingPositions.has(props.scenePos)))
   return !~pullIndex.value 
     && (isHover.value || custom.showTaggingPositions.has(props.scenePos))
 })

+ 4 - 1
src/sdk/sdk.ts

@@ -54,10 +54,14 @@ export interface CameraComeToProps {
   modelId?: Model['id'], 
   distance?: 1 | 2 | 3 
 }
+
+export type CalcPathProps = [[SceneGuidePath, SceneGuidePath], Partial<Pick<SceneGuidePath, 'time' | 'speed'>>]
+
 export interface SDK {
   layout: HTMLDivElement,
   sceneBus: Emitter<{ 'cameraChange': void }>
   addModel: (props: AddModelProps) => SceneModel
+  calcPathInfo: (paths: CalcPathProps[0], info: CalcPathProps[1]) => Required<CalcPathProps[1]>
   getPositionByScreen: (screenPos: ScreenLocalPos, modelId?: Model['id']) => ScenePos | null
   getScreenByPosition: (localPos: SceneLocalPos, modelId?: Model['id']) => ScreenPos | null
   screenshot: (width: number, height: number) => Promise<string>
@@ -87,7 +91,6 @@ export const initialSDK = async (props: InialSDKProps) => {
 
   sdk = localSdk
   sdk.layout = props.layout
-  console.error('-0-0--0')
 }
 
 export default sdk

+ 8 - 5
src/store/guide.ts

@@ -1,5 +1,5 @@
 import { ref } from 'vue'
-import { createTemploraryID } from './sys'
+import { createTemploraryID, isTemploraryID } from './sys'
 import { autoSetModeCallback } from './sys'
 import { 
   fetchGuides, 
@@ -7,7 +7,6 @@ import {
   postDeleteGuide,
   postUpdateGuide,
   uploadFile,
-  
 } from '@/api'
 import { 
   deleteStoreItem, 
@@ -68,7 +67,6 @@ export const transformGuide = async (guide: Guide): Promise<SGuide> => {
   )
 
   await Promise.all([uploadGuideCover, ...uploadPathsCver])
-
   return {
     ...guide,
     paths: guide.paths.map((path, i) => ({...path, cover: pathsCover[i]})),
@@ -78,7 +76,12 @@ export const transformGuide = async (guide: Guide): Promise<SGuide> => {
 
 export const recoverGuides = recoverStoreItems(guides, getBackupGuides)
 export const addGuide = addStoreItem(guides, postAddGuide, transformGuide)
-export const updateGuide = updateStoreItem(guides, postUpdateGuide, transformGuide)
+export const updateGuide = updateStoreItem(guides, (guide) => {
+  return postUpdateGuide({
+    ...guide, 
+    paths: guide.paths.map(path => ({...path, id: isTemploraryID(path.id) ? undefined : path.id})) as any
+  })
+}, transformGuide)
 export const deleteGuide = deleteStoreItem(guides, guide => postDeleteGuide(guide.id))
 export const initialGuides = fetchStoreItems(guides, fetchGuides, backupGuides)
 export const saveGuides = saveStoreItems(
@@ -91,7 +94,7 @@ export const saveGuides = saveStoreItems(
   }
 )
 export const autoSaveGuides = autoSetModeCallback(guides, {
-  backup: getBackupGuides,
+  backup: backupGuides,
   recovery: recoverGuides,
   save: saveGuides,
 })

+ 2 - 2
src/store/model.ts

@@ -1,5 +1,5 @@
 import { computed, ref, watchPostEffect } from 'vue'
-import { autoSetModeCallback } from './sys'
+import { autoSetModeCallback, unSetModelUpdate } from './sys'
 import { showModelsMapStack, custom } from '@/env'
 import { 
   fetchModels, 
@@ -65,7 +65,7 @@ const serviceToLocal = (model: SModel): Model => ({
 export const recoverModels = recoverStoreItems(models, getBackupModels)
 export const addModel = async (file: File) => {
   const model = await postAddModel(file)
-  models.value.push(serviceToLocal(model))
+  unSetModelUpdate(() => models.value.push(serviceToLocal(model)))
 }
 export const updateModel = updateStoreItem(models, postUpdateModels)
 export const deleteModel = deleteStoreItem(models, model => postDeleteModel(model.id))

+ 8 - 2
src/store/sys.ts

@@ -1,4 +1,4 @@
-import { ref, computed, watch } from 'vue'
+import { ref, computed, watch, nextTick } from 'vue'
 import { asyncBusFactory } from '@/utils'
 import { Dialog } from 'bill/index'
 import { useViewStack } from '@/hook'
@@ -76,6 +76,12 @@ export type AutoSetModeSetting<T> = {
   recovery?: () => void
 }
 
+let isUnset = false
+export const unSetModelUpdate = (run: () => void) => {
+  isUnset = true
+  run()
+  nextTick(() => isUnset = false)
+}
 export const autoSetModeCallback = <T extends object>(current: T, setting: AutoSetModeSetting<T>) => {
   let isSave = false
 
@@ -95,7 +101,7 @@ export const autoSetModeCallback = <T extends object>(current: T, setting: AutoS
   }
 
   const handler = (newv: UnwrapRef<T>, oldv?: UnwrapRef<T>) => {
-    if (isSave) return
+    if (isSave || isUnset) return
     if (!setting.isUpdate || setting.isUpdate(newv, oldv)) {
       isEdit.value || enterEdit()
       isOld.value ||  enterOld()

+ 2 - 2
src/utils/store-help.ts

@@ -42,7 +42,8 @@ export function addStoreItem <T extends {id: any}, K extends {id: any} = T>(
       actionData = item as unknown as K
     }
     const newItem = await addAction(actionData)
-    item.id = newItem.id
+    console.log('???', newItem)
+    Object.assign(item, newItem)
     storeSecurityPush(items.value, item)
   }
 }
@@ -68,7 +69,6 @@ export function updateStoreItem <T extends {id: any}, K extends {id: any} = T>(
     } else {
       actionData = item as unknown as K
     }
-    console.error(actionData, oldItem)
     await updateAction(actionData, oldItem)
     const storeItem = items.value.find(atom => atom.id === item.id)
     if (storeItem) {

+ 111 - 22
src/views/guide/edit-paths.vue

@@ -32,21 +32,50 @@
       </div>
 
       <div class="photo-list" ref="listVm">
-        <div 
-          v-for="(path, i) in paths" 
-          class="photo" 
-          :key="path.id"
-          :class="{ active: current === path, disabled: isScenePlayIng }"
-          @click="changeCurrent(path)"
-        >
-          <ui-icon 
-            type="del" 
-            ctrl 
-            @click.stop="deletePath(path)" 
-            :class="{ disabled: isScenePlayIng }" 
-          />
-          <img :src="getFileUrl(path.cover)" />
-        </div>
+        <template v-for="(path, i) in paths" :key="path.id">
+          <div 
+            class="photo" 
+            :class="{ active: current === path, disabled: isScenePlayIng }"
+            @click="changeCurrent(path)"
+          >
+            <ui-icon 
+              type="del" 
+              ctrl 
+              @click.stop="deletePath(path)" 
+              :class="{ disabled: isScenePlayIng }" 
+            />
+            <img :src="getResource(getFileUrl(path.cover))" />
+          </div>
+          <div class="set-phone-attr" v-if="i !== paths.length - 1">
+            <ui-input 
+              type="number" 
+              width="54px" 
+              height="26px"
+              :modelValue="path.speed" 
+              @update:modelValue="(val: number) => updatePathInfo(i, { speed: val })"
+              :ctrl="false" 
+              :min="0.1" 
+              :max="10"
+            >
+              <template #icon><span>m/s</span></template>
+            </ui-input>
+            <ui-input 
+              type="number" 
+              width="54px" 
+              height="26px" 
+              v-model="path.time" 
+              :modelValue="path.time" 
+              @update:modelValue="(val: number) => updatePathInfo(i, { time: val })"
+              :ctrl="false" 
+              :min="0.1" 
+              :max="20" 
+              class="time"
+            >
+              <template #icon><span class="time">s</span></template>
+            </ui-input>
+          </div>
+        </template>
+        
       </div>
     </div>
     <p class="un-video" v-else>暂无导览</p>
@@ -56,18 +85,29 @@
 <script setup lang="ts">
 import { loadPack, togetherCallback, getFileUrl, asyncTimeout } from '@/utils'
 import { sdk, playSceneGuide, pauseSceneGuide, isScenePlayIng } from '@/sdk'
-import { createGuidePath, isTemploraryID, useAutoSetMode, guides, enterOld } from '@/store'
+import { createGuidePath, isTemploraryID, useAutoSetMode, guides } from '@/store'
 import { Dialog } from 'bill/index'
 import { useViewStack } from '@/hook'
 import { nextTick, ref, toRaw, watchEffect } from 'vue'
-import { showRightPanoStack, showLeftCtrlPanoStack, showLeftPanoStack, showRightCtrlPanoStack } from '@/env'
+import { showRightPanoStack, showLeftCtrlPanoStack, showLeftPanoStack, showRightCtrlPanoStack, getResource } from '@/env'
 
 import type { Guide, GuidePaths, GuidePath } from '@/store'
+import type { CalcPathProps } from '@/sdk'
 
 const props = defineProps< { data: Guide }>()
 const paths = ref<GuidePaths>([...props.data.paths])
 const current = ref<GuidePath>(paths.value[0])
 
+const updatePathInfo = (index: number, calcInfo: CalcPathProps[1]) => {
+  console.log(paths.value.slice(index, index + 2))
+  const info = sdk.calcPathInfo(
+    paths.value.slice(index, index + 2) as any,
+    calcInfo
+  )
+  console.log(info)
+  Object.assign(paths.value[index], info)
+}
+
 useViewStack(() => 
   togetherCallback([
     showRightPanoStack.push(ref(false)),
@@ -84,7 +124,6 @@ useAutoSetMode(paths, {
       props.data.cover = paths.value[0].cover
     }
     if (isTemploraryID(props.data.id)) {
-      console.log(JSON.stringify(props.data))
       guides.value.push(props.data)
     }
   }
@@ -141,7 +180,7 @@ const play = async () => {
     await asyncTimeout(400)
     playSceneGuide(toRaw(paths.value), (index) => {
       console.log('guide', index)
-      current.value = paths.value[index]
+      current.value = paths.value[index - 1]
     })
   }
 }
@@ -210,10 +249,42 @@ watchEffect(async () => {
     overflow-x: auto;
     display: flex;
 
+    .set-phone-attr {
+      width: 80px;
+      display: flex;
+      flex-direction: column;
+      justify-content: space-evenly;
+      align-items: center;
+      position: relative;
+
+      &::before,
+      &::after {
+        content: '';
+        color: rgba(255,255,255,.6);
+        position: absolute;
+        top: 50%;
+        transform: translateY(-50%);
+      }
+      &::before {
+        left: 0;
+        right: 7px;
+        height: 2px;
+        background-color: currentColor;
+      }
+      &::after {
+        right: -5px;
+        width: 0;
+        height: 0;
+        border: 5px solid transparent;
+        border-left: 7px solid currentColor;
+      }
+    }
+    
     .photo {
       cursor: pointer;
       flex: none;
       position: relative;
+      
 
       &.active {
         outline: 2px solid var(--colors-primary-base);
@@ -235,9 +306,6 @@ watchEffect(async () => {
         border-radius: 50%;
       }
 
-      &:not(:last-child) {
-        margin-right: 10px;
-      }
 
       img {
         width: 230px;
@@ -256,3 +324,24 @@ watchEffect(async () => {
 }
 </style>
 
+<style lang="scss">
+.set-phone-attr {
+  .ui-input .text input {
+    padding: 8px 4px;
+  }
+  .ui-input .text {
+    font-size: 12px;
+  }
+  .ui-input .text.suffix .retouch {
+    right: 4px;
+  }
+  .ui-input .text.suffix input {
+    padding-right: 28px;
+    text-align: right;
+  }
+  .ui-input.time .text.suffix input {
+    padding-right: 18px;
+    text-align: right;
+  }
+}
+</style>

+ 2 - 1
src/views/guide/sign.vue

@@ -2,7 +2,7 @@
   <ui-group-option class="sign-guide">
     <div class="info">
       <div class="guide-cover">
-        <img :src="getFileUrl(guide.cover)" />
+        <img :src="getResource(getFileUrl(guide.cover))" />
         <ui-icon 
           type="preview" 
           class="icon" 
@@ -27,6 +27,7 @@
 <script setup lang="ts">
 import { Guide } from '@/store'
 import { getFileUrl } from '@/utils'
+import { getResource } from '@/env'
 
 
 defineProps<{ guide: Guide }>()

+ 14 - 6
src/views/merge/index.vue

@@ -5,9 +5,9 @@
         <Actions class="edit-header" :items="actionItems" />
       </template>
       <ui-group-option label="等比缩放">
-        <template #icon>
+        <!-- <template #icon>
           <a href="">设置比例</a>
-        </template>
+        </template> -->
         <ui-input type="range" v-model="custom.currentModel.scale" v-bind="scaleOption" width="100%" />
       </ui-group-option>
       <ui-group-option label="离地高度">
@@ -16,11 +16,11 @@
       <ui-group-option label="模型不透明度">
         <ui-input type="range" v-model="custom.currentModel.opacity" v-bind="opacityOption" width="100%" />
       </ui-group-option>
-      <ui-group-option>
+      <!-- <ui-group-option>
         <ui-button>配准</ui-button>
-      </ui-group-option>
+      </ui-group-option> -->
       <ui-group-option>
-        <ui-button>恢复默认</ui-button>
+        <ui-button @click="Object.assign(custom.currentModel as any, defaultAttrs)">恢复默认</ui-button>
       </ui-group-option>
     </ui-group>
   </RightPano>
@@ -28,7 +28,7 @@
 
 <script lang="ts" setup>
 import { RightPano } from '@/layout'
-import { autoSaveModels } from '@/store'
+import { autoSaveModels, ModelAttrs } from '@/store'
 import { togetherCallback } from '@/utils'
 import Actions from '@/components/actions/index.vue'
 import { getSceneModel } from '@/sdk'
@@ -38,6 +38,14 @@ import { ref } from 'vue'
 
 import type { ActionsProps } from '@/components/actions/index.vue'
 
+const defaultAttrs: ModelAttrs = {
+  show: true,
+  scale: 1,
+  opacity: 1,
+  bottom: 0,
+  position: {x: 0, y: 0, z: 0},
+  rotation: {x: 0, y: 0, z: 0}
+}
 const opacityOption = { min: 0.01, max: 1, step: 0.01, }
 const bottomOption = { min: 1, max: 100, step: 1, }
 const scaleOption = { min: 0.01, max: 1, step: 0.01, }

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

@@ -4,11 +4,16 @@
       <img :src="getResource(getFileUrl(style.icon))" v-if="style">
       <div>
         <p>{{ tagging.title }}</p>
-        <a @click.stop="$emit('flyPositions')">放置:{{ positions.length }}</a>
+        <a>放置:{{ positions.length }}</a>
       </div>
     </div>
     <div class="actions" @click.stop>
-      <ui-icon type="pin" ctrl />
+      <ui-icon 
+        :class="{disabled: disabledFly}"
+        type="pin" 
+        ctrl  
+        @click.stop="$emit('flyPositions')"
+      />
       <ui-more 
         :options="menus" 
         style="margin-left: 20px" 
@@ -19,14 +24,26 @@
 </template>
 
 <script setup lang="ts">
-import { Tagging, getTaggingStyle, getTaggingPositions } from '@/store'
 import { getFileUrl } from '@/utils'
 import { computed } from 'vue';
 import { getResource } from '@/env'
+import { 
+  getTaggingStyle, 
+  getTaggingPositions, 
+  getModel,
+  getModelShowVariable
+} from '@/store'
+
+import type { Tagging } from '@/store'
 
 const props = defineProps<{ tagging: Tagging, selected?: boolean }>()
 const style = computed(() => getTaggingStyle(props.tagging.styleId))
 const positions = computed(() => getTaggingPositions(props.tagging))
+const disabledFly = computed(() => 
+  positions.value
+    .map(position => getModel(position.modelId))
+    .every(model => !model || !getModelShowVariable(model).value)
+)
 
 const emit = defineEmits<{ 
   (e: 'delete'): void