소스 검색

修改bug

bill 2 년 전
부모
커밋
299d6f376e

+ 11 - 9
src/api/fuse-model.ts

@@ -7,7 +7,7 @@ import {
 } from './constant'
 import { params } from '@/env'
 
-import { Scene, SceneType } from './scene'
+import { Scene, SceneStatus, SceneType } from './scene'
 
 
 export interface FuseModelAttrs {
@@ -29,6 +29,7 @@ export interface FuseModel extends FuseModelAttrs {
   modelType: string,
   type: SceneType
   size: number,
+  status: SceneStatus,
   time: string
 }
 
@@ -47,22 +48,23 @@ interface ServiceFuseModel {
 }
 
 const serviceToLocal = (serviceModel: ServiceFuseModel): FuseModel => ({
-  show: !serviceModel.hide,
+  show: true,
   scale: serviceModel.transform.scale[0],
   opacity: serviceModel.opacity || 1,
   bottom: serviceModel.bottom || 0,
   fusionNumId: serviceModel.fusionNumId,
-  modelType: serviceModel.sceneData.modelDateType,
+  modelType: serviceModel.sceneData?.modelDateType,
   position: serviceModel.transform.position,
   rotation: serviceModel.transform.rotation,
   id: serviceModel.fusionNumId.toString(),
-  url: serviceModel.sceneData.type === SceneType.SWSS ? serviceModel.sceneData.num : serviceModel.sceneData.modelGlbUrl,
-  title: serviceModel.sceneData.name || serviceModel.sceneData.sceneName || serviceModel.sceneData.modelTitle,
-  modelId: serviceModel.sceneData.modelId,
+  url: serviceModel.sceneData ? (serviceModel.sceneData.type === SceneType.SWSS ? serviceModel.sceneData.num : serviceModel.sceneData.modelGlbUrl) : '',
+  title: serviceModel.sceneData ? (serviceModel.sceneData?.name || serviceModel.sceneData.sceneName || serviceModel.sceneData.modelTitle) : '-',
+  modelId: serviceModel.sceneData?.modelId,
   fusionId: serviceModel.fusionId,
-  type: serviceModel.sceneData.type,
-  size: serviceModel.sceneData.modelSize,
-  time: serviceModel.sceneData.createTime
+  type: serviceModel.sceneData?.type,
+  size: serviceModel.sceneData?.modelSize,
+  time: serviceModel.sceneData?.createTime,
+  status: serviceModel.sceneData ? serviceModel.sceneData.status : SceneStatus.ERR
 })
 
 const localToService = (model: FuseModel): Omit<ServiceFuseModel, 'sceneData'> => ({

+ 6 - 1
src/api/instance.ts

@@ -49,6 +49,12 @@ addHook({
     if (config.url !== URL.RECORD_STATUS) {
       showLoad()
     }
+    const isShare = Number(params.share)
+    if (config.headers) {
+      config.headers.share = isShare
+    } else {
+      config.headers = { share: isShare }
+    }
   }, 
   after: (config) => {
     if (config.url !== URL.RECORD_STATUS) {
@@ -74,5 +80,4 @@ addUnsetTokenURLS(
 setDefaultURI(baseURL)
 params.token && setToken(params.token)
 
-
 export default axios

+ 11 - 1
src/api/scene.ts

@@ -9,6 +9,15 @@ export enum SceneType {
   SWMX = 3,
 }
 
+export enum SceneStatus {
+  DEL = -1,
+  RUN = 0,
+  ERR = 1,
+  SUCCESS = 2,
+  ARCHIVE = 3,
+  RERUN = 4,
+}
+
 export const SceneTypeDesc: Record<SceneType, string>  = {
   [SceneType.SWKK]: '四维看看',
   [SceneType.SWKJ]: '四维看见',
@@ -25,6 +34,7 @@ export interface Scene {
   modelId: number
   modelObjUrl: string
   modelSize: number
+  status: SceneStatus
   modelTitle: string
   name: string
   num: string
@@ -44,7 +54,7 @@ const toLocalScene = (scene: Scene) => ({
 })
 
 export const fetchScenes = async () => {
-  const scenes = await axios.post<Scenes>(MODEL_LIST, { caseId: params.caseId })
+  const scenes = await axios.get<Scenes>(MODEL_LIST, { params: { caseId: params.caseId } })
   return scenes.map(toLocalScene)
 }
 

+ 1 - 0
src/components/bill-ui/assets/scss/components/_slide.scss

@@ -49,6 +49,7 @@
       display: inline-flex;
       align-items: center;
       font-size: 10px;
+      color: rgba(255,255,255,.6);
 
       span {
         color: var(--colors-primary-base);

+ 2 - 2
src/components/bill-ui/components/slide/index.vue

@@ -6,8 +6,8 @@
             </GateContent>
         </Gate>
         <template v-if="showCtrl">
-            <span class="left" @click="prevHandler"><UIIcon type="left1" /></span>
-            <span class="right" @click="nextHandler"><UIIcon type="right" /></span>
+            <span class="left fun-ctrl" @click="prevHandler"><UIIcon type="left1" /></span>
+            <span class="right fun-ctrl" @click="nextHandler"><UIIcon type="right" /></span>
         </template>
         <slot name="attach" :active="items[index]" />
 

+ 20 - 34
src/components/static-preview/index.vue

@@ -4,24 +4,19 @@
       <ui-icon type="close" ctrl />
     </span>
     <div class="pull-preview pc">
-      <div class="preview-layer">
-        <div class="pull-meta">
-            <video v-if="type === MediaType.video" controls autoplay playsinline webkit-playsinline>
-              <source :src="staticURL" />
-            </video>
-            <iframe v-else-if="type === MediaType.web" :src="staticURL"></iframe>
-            <div v-if="type === MediaType.img" class="full-img pc">
-              <img :src="staticURL" />
-            </div>
-        </div>
-      </div>
+      <ui-slide v-if="items.length > 1" :currentIndex="current" showCtrl :items="items" showInfos>
+        <template v-slot="{ raw }">
+          <Sign :media="raw" />
+        </template>
+      </ui-slide>
+      <Sign :media="items[0]" />
     </div>
   </teleport>
 </template>
 
 <script lang="ts">
-import { ref, watchEffect, defineComponent, PropType } from 'vue'
-import { getResource } from '@/env'
+import { defineComponent, PropType, ref } from 'vue'
+import Sign from './sign.vue'
 
 export enum MediaType {
   video,
@@ -29,37 +24,28 @@ export enum MediaType {
   web
 }
 
+export type MediaItem = {
+  url: Blob | string,
+  type: MediaType
+}
+
 export const Preview =  defineComponent({
   name: 'static-preview',
   props: {
-    url: {
-      type: String as PropType<Blob | string>,
+    items: {
+      type: Array as PropType<MediaItem[]>,
       required: true
     },
-    type: {
-      type: Number as PropType<MediaType>,
-      required: true
+    current: {
+      type: Number,
+      default: 1
     }
   },
   emits: {
     close: () => true
   },
-  setup(props) {
-    const staticURL = ref('')
-    watchEffect(() => {
-      const data = props.url
-      const url = typeof data === 'string'
-        ? getResource(data)
-        : URL.createObjectURL(data)
-
-      staticURL.value = url
-      return () => URL.revokeObjectURL(url)
-    })
-    
-    return {
-      staticURL,
-      MediaType
-    }
+  components: {
+    Sign
   }
 })
 

+ 54 - 0
src/components/static-preview/sign.vue

@@ -0,0 +1,54 @@
+<template>
+  <div class="preview-layer">
+    <div class="pull-meta">
+      <video v-if="media.type === MediaType.video" controls autoplay playsinline webkit-playsinline>
+        <source :src="staticURL" />
+      </video>
+      <iframe v-else-if="media.type === MediaType.web" :src="staticURL"></iframe>
+      <img :src="staticURL" v-if="media.type === MediaType.img"/>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { ref, watchEffect, defineComponent, PropType } from 'vue'
+import { getResource } from '@/env'
+import { MediaType } from './index.vue'
+import type { MediaItem } from './index.vue'
+
+
+export const Preview =  defineComponent({
+  name: 'static-preview',
+  props: {
+    media: {
+      type: Object as PropType<MediaItem>,
+        required: true
+    }
+  },
+  emits: {
+    close: () => true
+  },
+  setup(props) {
+    const staticURL = ref('')
+    watchEffect(() => {
+      const data = props.media.url
+      const url = typeof data === 'string'
+        ? getResource(data)
+        : URL.createObjectURL(data)
+
+      staticURL.value = url
+      return () => URL.revokeObjectURL(url)
+    })
+    
+    return {
+      staticURL,
+      MediaType
+    }
+  }
+})
+
+
+export default Preview
+</script>
+
+<style scoped lang="scss" src="./style.scss"></style>

+ 11 - 3
src/components/static-preview/style.scss

@@ -61,6 +61,9 @@
       width: 100%;
       overflow-y: auto;
       flex: 1;
+      display: flex;
+      align-items: center;
+      justify-content: center;
 
       .content {
         margin-bottom: 10px;
@@ -73,14 +76,19 @@
   
       }
 
-      iframe,
-      video,
-      img {
+      iframe {
         width: 100%;
         height: 100%;
         display: block;
       }
 
+      video,
+      img {
+        max-width: 100%;
+        max-height: 100%;
+        display: block;
+      }
+
       video, img {
         object-fit: contain;
       }

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

@@ -44,8 +44,8 @@
 
       <Preview 
         @close="pullIndex = -1"
-        :type="MediaType.img" 
-        :url="getResource(getFileUrl(tagging.images[pullIndex]))"
+        :current="pullIndex"
+        :items="queryItems"
         v-if="!!~pullIndex" 
       />
     </div>
@@ -97,6 +97,12 @@ const taggingStyle = getTaggingStyle(props.tagging.styleId)
 
 const pullIndex = ref(-1)
 const isHover = ref(false)
+const queryItems = computed(() => 
+  props.tagging.images.map(image => ({
+    type: MediaType.img, 
+    url: getResource(getFileUrl(image))
+  }))
+)
 
 const iconClickHandler = () => {
   if (custom.showTaggingPositions.has(props.scenePos)) {

+ 3 - 1
src/env/index.ts

@@ -10,7 +10,7 @@ export const showRightPanoStack = stackFactory(ref<boolean>(true))
 export const showLeftPanoStack = stackFactory(ref<boolean>(false))
 export const showLeftCtrlPanoStack = stackFactory(ref<boolean>(true))
 export const showRightCtrlPanoStack = stackFactory(ref<boolean>(true))
-export const showBottomBarStack = stackFactory(ref<boolean>(false))
+export const showBottomBarStack = stackFactory(ref<boolean>(false), true)
 export const bottomBarHeightStack = stackFactory(ref<string>('60px'))
 export const showTaggingsStack = stackFactory(ref<boolean>(true))
 export const showMeasuresStack = stackFactory(ref<boolean>(false))
@@ -42,10 +42,12 @@ export const custom = flatStacksValue({
 
 export const params = strToParams(location.search) as unknown as Params
 params.caseId = Number(params.caseId)
+params.share = Boolean(Number(params.share))
 export type Params = { 
   caseId: number,
   baseURL?: string,
   modelId?: string,
+  share: boolean,
   token?: string
 }
 

+ 2 - 14
src/layout/edit/header/index.vue

@@ -22,9 +22,8 @@
 </template>
 
 <script setup lang="ts">
-import { computed, watchEffect, ref } from 'vue'
+import { computed, ref } from 'vue'
 import { isEdit, title, isOld, leave, save } from '@/store'
-import { currentMeta } from '@/router'
 import { getCaseInfo } from '@/api'
 
 const prefix = ref('')
@@ -32,18 +31,7 @@ getCaseInfo()
   .then(ecase => prefix.value = `${ecase.caseTitle} | `)
 
 const props = defineProps<{ title?: string }>()
-const sysTitle = computed(() => {
-  if (props.title) {
-    return props.title
-  } else if (currentMeta.value && 'sysTitle' in currentMeta.value) {
-    return prefix.value + currentMeta.value.sysTitle
-  } else {
-    return prefix.value + title.value
-  }
-})
-
-
-watchEffect(() => (document.title = sysTitle.value))
+const sysTitle = computed(() => props.title || title.value)
 </script>
 
 <style lang="sass" scoped>

+ 20 - 4
src/layout/edit/scene-select.vue

@@ -11,20 +11,30 @@
   >
     <div>
       <div className='model-header'>
+        <p class="header-desc">已选择数据<span>( {{ selectIds.length }} )</span></p>
         <Search
           className='content-header-search'
           placeholder="输入名称搜索" 
+          v-model:value="keyword"
+          allow-clear
           style="width: 264px"
-          @search="val => keyword = val"
         />
       </div>
       <Table 
+        v-if="filterScenes.length"
         :row-key="record => record.modelId"
         :columns="cloumns"
         :rowSelection="rowSelection"
         :data-source="filterScenes"
         :pagination="false"
       />
+      <div style="padding: 1px;" v-else>
+        <Empty 
+          :description="keyword.length ? '暂无搜索结果': '暂无结果'" 
+          :image="Empty.PRESENTED_IMAGE_SIMPLE"
+          className="ant-empty ant-empty-normal" 
+        />
+      </div>
     </div>
   </Modal>
 
@@ -34,14 +44,13 @@
 </template>
 
 <script lang="ts" setup>
-import { Modal, Input, Table } from 'ant-design-vue'
+import { Modal, Input, Table, Empty } from 'ant-design-vue'
 import { computed, nextTick, ref, watch, watchEffect } from 'vue';
 import { scenes, save, SceneTypeDesc } from '@/store'
 import { createLoadPack } from '@/utils'
 import { fuseModels, createFuseModels, addFuseModel, fuseModelsLoaded, initialScenes } from '@/store'
 
 import type { Scene } from '@/api'
-import { useViewStack } from '@/hook';
 
 type Key = Scene['modelId']
 
@@ -122,8 +131,9 @@ watch(visible, (visible, oldvisible) => {
 <style lang="less" scoped>
 .model-header {
   display: flex;
-  justify-content: flex-end;
+  justify-content: space-between;
   padding-bottom: 24px;
+  align-items: center;
 }
 </style>
 
@@ -136,5 +146,11 @@ watch(visible, (visible, oldvisible) => {
   // .ant-table-tbody > tr.ant-table-row-selected > td {
   //   background: none;
   // }
+  .model-header .header-desc {
+    margin-bottom: 0;
+  }
+  .model-header .header-desc span {
+    color: @primary-color;
+  }
 }
 </style>

+ 1 - 7
src/layout/model-list/index.vue

@@ -48,13 +48,7 @@ const modelList = computed(() =>
 
 const modelChangeSelect = (model: FuseModel) => {
   if (getFuseModelShowVariable(model).value) {
-    if (custom.currentModel !== model) {
-      getSceneModel(model)?.changeSelect(true)
-      custom.currentModel = model
-    } else {
-      getSceneModel(custom.currentModel)?.changeSelect(false)
-      custom.currentModel = null
-    }
+    custom.currentModel = custom.currentModel !== model ? model : null
   }
 }
 

+ 22 - 15
src/layout/model-list/sign.vue

@@ -1,20 +1,27 @@
 <template>
-  <div class="model-header" @click="!model.error && $emit('click')">
-    <p>{{ model.title }}</p>
-    <div class="model-action">
-      <ui-input type="checkbox" v-model="show" :class="{disabled: model.error}"/>
-      <ui-icon 
-        v-if="custom.modelsChangeStore" 
-        type="del" 
-        ctrl 
-        @click="$emit('delete')" 
-      />
+  <div @click="!model.error && $emit('click')" class="sign-layout">
+    <div class="model-header">
+      <p>{{ model.title }}</p>
+      <div class="model-action">
+        <ui-input 
+          type="checkbox" 
+          v-model="show" 
+          @click.stop 
+          :class="{disabled: model.error}"
+        />
+        <ui-icon 
+          v-if="custom.modelsChangeStore" 
+          type="del" 
+          ctrl 
+          @click="$emit('delete')" 
+        />
+      </div>
+    </div>
+    <div class="model-desc"  v-if="active">
+      <p><span>数据来源:</span>{{ SceneTypeDesc[model.type] }}</p>
+      <p><span>数据大小:</span>{{ model.size }}</p>
+      <p><span>拍摄时间:</span>{{ model.time }}</p>
     </div>
-  </div>
-  <div class="model-desc" @click="$emit('click')" v-if="active">
-    <p><span>数据来源:</span>{{ SceneTypeDesc[model.type] }}</p>
-    <p><span>数据大小:</span>{{ model.size }}</p>
-    <p><span>拍摄时间:</span>{{ model.time }}</p>
   </div>
 </template>
 

+ 5 - 0
src/layout/model-list/style.scss

@@ -23,3 +23,8 @@
     margin-left: 20px;
   }
 }
+
+.sign-layout {
+  margin: -20px 0;
+  padding: 20px 0;
+}

+ 1 - 0
src/layout/right-fill-pano.vue

@@ -13,6 +13,7 @@
 
 <script setup lang="ts">
 import { custom } from '@/env'
+
 </script>
 
 <style lang="scss" scoped>

+ 9 - 2
src/layout/scene-list/index.vue

@@ -31,7 +31,7 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, ref } from 'vue'
+import { computed, nextTick, ref, watch } from 'vue'
 import { scenes, SceneType, SceneTypeDesc, fuseModels } from '@/store'
 import List from '@/components/list/index.vue'
 import ModelList from '../model-list/index.vue'
@@ -42,7 +42,7 @@ import type { Scene } from '@/store'
 
 const emit = defineEmits<{ (e: 'update:current', data: ModelType): void }>()
 const props = defineProps<{ current: ModelType }>()
-const showModelList = ref(false)
+const showModelList = ref(true)
 
 const list = computed(() => {
   const sceneList = scenes.value.map(scene => ({
@@ -65,6 +65,13 @@ const updateCurrent = (scene: FuseModelType | Scene) => {
     emit('update:current', { type: scene.type, num: scene.num })
   }
 }
+
+const stopWatch = watch(list, () => {
+  if (!list.value.some(model => model.raw === fuseModel)) {
+    updateCurrent(list.value[0].raw as any)
+    nextTick(() => stopWatch())
+  }
+}, { immediate: true })
 </script>
 
 <style lang="scss">

+ 7 - 0
src/model/app.vue

@@ -103,4 +103,11 @@ export default Model
   height: calc(100% - (var(--editor-head-height) + var(--header-top)));
   width: 100%;
 }
+
+#direction {
+  right: calc(var(--editor-menu-right) + var(--editor-toolbox-width)) !important;
+  top: calc(var(--header-top) + var(--editor-head-height)) !important;
+  margin: 10px;
+  transition: top .3s ease, right .3s ease;
+}
 </style>

+ 26 - 5
src/sdk/association.ts

@@ -41,6 +41,7 @@ import type {
   ModelAttrRange, 
   Measure as SceneMeasure 
 } from '.'
+import { SceneStatus } from '@/api'
 
 let isUnSet = false
 const unSet = ((fn: () => void) => {
@@ -72,6 +73,12 @@ const associationModels = (sdk: SDK) => {
         continue;
       }
 
+      if (item.status !== SceneStatus.SUCCESS) {
+        item.error = true
+        item.loaded = true
+        continue;
+      }
+
       const itemRaw = toRaw(item)
       let sceneModel: SceneModel
       console.error('loaded', itemRaw)
@@ -131,11 +138,13 @@ const associationModels = (sdk: SDK) => {
       })
 
       sceneModel.bus.on('changeSelect', select => {
-        if (custom.currentModel === item && !select) {
-          custom.currentModel = null
-        } else if (custom.currentModel !== item && select) {
-          custom.currentModel = item
-        }
+        unSet(() => {
+          if (custom.currentModel === item && !select) {
+            custom.currentModel = null
+          } else if (custom.currentModel !== item && select) {
+            custom.currentModel = item
+          }
+        })
       })
       showLoad()
       sceneModel.bus.on('loadDone', () => {
@@ -208,12 +217,24 @@ const associationModels = (sdk: SDK) => {
             { immediate: true }
           )
 
+          watch(
+            () => custom.currentModel === item,
+            (selected) => {
+              console.log(item.title, selected, getSceneModel(item))
+              isUnSet || getSceneModel(item)?.changeSelect(selected)
+            }
+          )
+
           stopLoadedWatch()
         }
       },
       // { immediate: true }
     )
   })
+
+  watch(() => custom.currentModel, () => {
+
+  })
 }
 
 

+ 5 - 5
src/store/fuse-model.ts

@@ -6,15 +6,14 @@ import {
   postAddFuseModel, 
   postDeleteFuseModel,
   postUpdateFuseModels,
-  SceneType
+  SceneType,
+  SceneStatus
 } from '@/api'
 import { 
   deleteStoreItem, 
-  addStoreItem, 
   updateStoreItem, 
   fetchStoreItems,
   saveStoreItems,
-  recoverStoreItems,
   deepIsRevise
 } from '@/utils'
 import { initialTaggings } from './tagging'
@@ -44,6 +43,7 @@ export const createFuseModels = (model: Partial<FuseModel> = {}): FuseModel => s
   title: '',
   modelType: 'glb',
   type: SceneType.SWMX,
+  status: SceneStatus.SUCCESS,
   size: 0,
   time: new Date().toString(),
   ...defaultFuseModelAttrs,
@@ -53,11 +53,11 @@ export const createFuseModels = (model: Partial<FuseModel> = {}): FuseModel => s
 export const getFuseModel = (modelId: FuseModel['id']) => fuseModels.value.find(model => model.id === modelId)
 export const getFuseModelShowVariable = (model: FuseModel) => 
   computed({
-    get: () => custom.modelsChangeStore 
+    get: () => false && custom.modelsChangeStore 
       ? model.show 
       : custom.showModelsMap.get(model) || false,
     set: (show: boolean) => {
-      if (custom.modelsChangeStore) {
+      if (false && custom.modelsChangeStore) {
         model.show = show
       } else {
         custom.showModelsMap.set(model, show)

+ 1 - 1
src/store/record.ts

@@ -40,7 +40,7 @@ export const records = ref<Records>([])
 
 export const createRecord = (record: Partial<Record> = {}): Record => ({
   id: createTemploraryID(),
-  title: '讲解视频',
+  title: '讲解视频' + (records.value.length + 1),
   cover: '',
   url: '',
   status: RecordStatus.UN,

+ 16 - 2
src/store/sys.ts

@@ -1,9 +1,11 @@
-import { ref, computed, watch, nextTick } from 'vue'
+import { ref, computed, watch, nextTick, watchEffect } from 'vue'
 import { asyncBusFactory } from '@/utils'
 import { Dialog } from 'bill/index'
 import { useViewStack } from '@/hook'
 
 import type { UnwrapRef } from 'vue'
+import { getCaseInfo } from '@/api'
+import { currentMeta } from '@/router'
 
 const Flags = {
   EDIT: 0b10,
@@ -18,9 +20,21 @@ export const isEdit = computed(() => !!(mode.value & Flags.EDIT))
 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 = ref('多元融合')
 export const appEl = ref<HTMLDivElement | null>(null)
 
+
+const prefix = ref('')
+getCaseInfo().then(ecase => prefix.value = `${ecase.caseTitle} | `)
+
+export const title = computed(() => {
+  if (currentMeta.value && 'sysTitle' in currentMeta.value) {
+    return prefix.value + currentMeta.value.sysTitle
+  } else {
+    return prefix.value + '多元融合'
+  }
+})
+watchEffect(() => (document.title = title.value))
+
 let currentTempIndex = 0
 export const isTemploraryID = (id: string) => id.includes('__currentTempIndex__')
 export const createTemploraryID = () => `__currentTempIndex__${currentTempIndex++}`

+ 1 - 1
src/store/view.ts

@@ -30,7 +30,7 @@ export const views = ref<Views>([])
 export const createView = (view: Partial<View> = {}): View => {
   const base = {
     id: createTemploraryID(),
-    title: '视图',
+    title: '视图' + (views.value.length + 1),
     cover: 'https://4dkk.4dage.com/scene_view_data/KK-t-F8e5M46wcQ/images/floor_0.png?t=1659422513133?v=0&rnd=0.9219648338739086&x-oss-process=image/resize,m_fill,w_80,h_60/quality,q_70&rnd=0.25420557086595965',
     flyData: '',
     num: null,

+ 10 - 0
src/utils/stack.ts

@@ -19,13 +19,23 @@ export const stackFactory = <T>(initVal?: T, debug?: boolean): Stack<T> => {
   return {
     push(raw: T) {
       stack.push(raw);
+      if (debug) {
+        console.warn('push', raw)
+      }
       return () => {
         const index = stack.indexOf(raw);
         ~index && stack.splice(index, 1);
+        if (debug) {
+          console.warn('pop', raw)
+          console.warn('current', stack)
+        }
       };
     },
     pop() {
       let ret = stack[stack.length-- - 1];
+      if (debug) {
+        console.warn('pop current', stack)
+      }
       return ret;
     },
     current: computed(() => {

+ 11 - 5
src/views/merge/index.vue

@@ -7,7 +7,7 @@
       <ui-group-option label="等比缩放">
         <template #icon>
           <a class="set-prop" 
-            :class="{disabled: isOld}"
+            :class="{disabled: isOld || currentItem}"
             @click="router.push({ 
               name: RoutesName.proportion, 
               params: { id: custom.currentModel!.id, save: '1' },
@@ -70,8 +70,8 @@ import { autoSaveFuseModels, defaultFuseModelAttrs, isOld } from '@/store'
 import { togetherCallback } from '@/utils'
 import { getSceneModel, modelRange } from '@/sdk'
 import { useViewStack, useActive } from '@/hook'
-import { showLeftCtrlPanoStack, showLeftPanoStack, custom, modelsChangeStoreStack } from '@/env'
-import { ref, nextTick, watchEffect } from 'vue'
+import { showLeftPanoStack, custom, modelsChangeStoreStack, showRightPanoStack } from '@/env'
+import { ref, nextTick, watchEffect, computed } from 'vue'
 import { Dialog } from 'bill/expose-common'
 
 import Actions from '@/components/actions/index.vue'
@@ -93,7 +93,9 @@ const actionItems: ActionsProps['items'] = [
     text: '旋转',
     action: () => {
       getSceneModel(custom.currentModel)?.enterRotateMode()
-      return () => getSceneModel(custom.currentModel)?.leaveTransform()
+      return () => {
+        getSceneModel(custom.currentModel)?.leaveTransform()
+      }
     }
   },
 ]
@@ -112,13 +114,17 @@ const reset = async () => {
   }
 }
 
+
 useViewStack(() => togetherCallback([
   showLeftPanoStack.push(ref(true)),
+  showRightPanoStack.push(computed(() => !!custom.currentModel)),
   modelsChangeStoreStack.push(ref(true)),
   () => currentItem.value = null
 ]))
 useViewStack(autoSaveFuseModels)
-
+watchEffect(() => {
+  console.error(custom.showRightPano)
+}, { flush: 'sync' })
 </script>
 
 <style lang="scss">

+ 2 - 1
src/views/proportion/index.vue

@@ -59,11 +59,12 @@ watchEffect((onCleanup) => {
   if (smodel) {
     scaleSet = smodel.enterScaleSet()
     scaleSet.startMeasure()
-    currentModelStack.push(model as any)
+    const pop = currentModelStack.push(model as any)
 
     onCleanup(() => {
       smodel.leaveScaleSet()
       scaleSet = null
+      pop()
     })
   } else if (isCurrent.value) {
     leave()

+ 3 - 2
src/views/record/index.vue

@@ -35,7 +35,7 @@ import { showMeasuresStack, showTaggingsStack } from '@/env'
 import { useViewStack } from '@/hook'
 import { diffArrayChange, togetherCallback } from '@/utils'
 import { RecordProcess } from './help'
-import { records, createRecord, Record, RecordStatus, autoSaveRecords, initialRecords } from '@/store'
+import { records, createRecord, Record, RecordStatus, autoSaveRecords, initialRecords, getRecordFragmentBlobs, isTemploraryID } from '@/store'
 import { RightFillPano } from '@/layout'
 import Draggable from 'vuedraggable'
 import Sign from './sign.vue'
@@ -45,7 +45,8 @@ initialRecords()
 
 const start = () => records.value.push(createRecord())
 const deleteRecord = async (record: Record) => {
-  if (await Dialog.confirm('确定要删除视频吗?')) {
+  const isTemp = getRecordFragmentBlobs(record).length === 0 && isTemploraryID(record.id)
+  if (isTemp || await Dialog.confirm('确定要删除视频吗?')) {
     const index = records.value.indexOf(record)
     if (~index) {
       records.value.splice(index, 1)

+ 30 - 18
src/views/record/shot.vue

@@ -8,10 +8,10 @@
     <ui-editor-toolbar :toolbar="custom.showBottomBar" class="shot-ctrl">
       <ui-button type="submit" class="btn" @click="close">取消</ui-button>
       <ui-button type="primary" class="btn" @click="complete" :class="{disabled: blobs.length === 0}">合并视频</ui-button>
-      <div class="other">
+      <div class="other" :style="{bottom: barHeight}">
         <ui-icon class="icon" type="video1" ctrl @click="start" tip="继续录制" tipV="top" />
       </div>
-      <div class="video-list">
+      <div class="video-list" v-if="videoList.length">
         <div class="layout" :style="{width: `${videoList.length * 130}px`}">
           <div v-for="video in videoList" :key="video.cover" class="cover">
             <img :src="video.cover">
@@ -28,15 +28,14 @@
 
     <Preview 
       v-if="palyUrl" 
-      :type="MediaType.video" 
-      :url="palyUrl" 
+      :items="[{ type: MediaType.video, url: palyUrl }]"
       @close="palyUrl = null" 
     />
   </teleport>
 </template>
 
 <script lang="ts">
-import { ref, defineComponent, onUnmounted, watch, shallowReactive, PropType } from 'vue'
+import { ref, defineComponent, onUnmounted, watch, shallowReactive, PropType, computed, watchEffect } from 'vue'
 import { VideoRecorder } from '@simaq/core';
 import { sdk } from '@/sdk'
 import { getVideoCover, togetherCallback } from '@/utils'
@@ -54,6 +53,7 @@ import {
 } from '@/env'
 import { appEl } from '@/store';
 import { useViewStack } from '@/hook';
+import { currentModel } from '@/model';
 
 
 export default defineComponent({
@@ -79,6 +79,8 @@ export default defineComponent({
   
     console.error('new VideoRecorder')
     const videoRecorder = new VideoRecorder(config);
+    const showLeftPano = ref(false)
+    const showBottomBar = ref(false)
 
     type VideoItem = { origin: Blob | string, cover: string }
 
@@ -86,7 +88,7 @@ export default defineComponent({
     let interval: NodeJS.Timer
     let recordIng = false
     const start = () => {
-      custom.showBottomBar = false
+      showBottomBar.value = false
       countdown.value = 1
       const timeiffe = () => {
         if (--countdown.value === 0) {
@@ -113,7 +115,7 @@ export default defineComponent({
         recordIng = false
       }
       countdown.value = 0
-      custom.showBottomBar = true
+      showBottomBar.value = true
       clearInterval(interval)
     }
 
@@ -172,21 +174,30 @@ export default defineComponent({
     }
 
     start()
-    const pop = togetherCallback([
-      showHeadBarStack.push(ref(false)),
-      showRightCtrlPanoStack.push(ref(false)),
-      showRightPanoStack.push(ref(false)),
-      showBottomBarStack.push(ref(false)),
-      bottomBarHeightStack.push(ref('180px'))
-    ])
+    const barHeight = computed(() => videoList.length ? '180px' : '60px')
+    
     onUnmounted(() => {
-      close()
-      pop()
-      custom.showBottomBar = false
       document.body.removeEventListener('keyup', upHandler, { capture: true })
     })
 
-    useViewStack(() => showLeftPanoStack.push(ref(false)))
+
+    watch(currentModel, () => {
+      if (currentModel.value) {
+        showLeftPano.value = false
+      }
+    })
+    useViewStack(() => togetherCallback([
+      showHeadBarStack.push(ref(false)),
+      showRightCtrlPanoStack.push(ref(false)),
+      showRightPanoStack.push(ref(false)),
+      showBottomBarStack.push(showBottomBar),
+      bottomBarHeightStack.push(barHeight),
+      showLeftPanoStack.push(showLeftPano),
+      close,
+      () => {
+        console.log('pop', showBottomBarStack.current)
+      }
+    ]))
 
     return {
       MediaType,
@@ -194,6 +205,7 @@ export default defineComponent({
       pause,
       close,
       start,
+      barHeight,
       el: sdk.layout,
       blobs,
       countdown,

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

@@ -43,8 +43,7 @@
       :record="record" />
     <Preview 
       v-if="isPlayVideo" 
-      :type="MediaType.video" 
-      :url="record.url" 
+      :items="[{ type: MediaType.video, url: record.url }]"
       @close="isPlayVideo = false" 
     />
   </ui-group-option>

+ 2 - 3
src/views/registration/index.vue

@@ -68,8 +68,6 @@ const model = computed(() => {
   }
 })
 
-console.log(model)
-
 const sceneModel = computed(() => model.value && getSceneModel(model.value))
 const options = [
   { desc: '移动', icon: 'move', key: 'move' },
@@ -108,11 +106,12 @@ watchEffect((onCleanup) => {
   const smodel = sceneModel.value
   if (smodel) {
     smodel.enterAlignment()
-    currentModelStack.push(model as any)
+    const pop = currentModelStack.push(model as any)
 
     onCleanup(() => {
       smodel.leaveTransform()
       smodel.leaveAlignment()
+      pop()
     })
   } else if (isCurrent.value) {
     leave()

+ 10 - 9
src/views/summary/index.vue

@@ -21,9 +21,8 @@
 </template>
 
 <script setup lang="ts">
-import { computed, ref } from 'vue'
+import { ref, watchEffect } from 'vue'
 import { useViewStack } from '@/hook'
-import { togetherCallback } from '@/utils'
 import { showRightCtrlPanoStack, showRightPanoStack } from '@/env'
 import { currentModel, fuseModel, loadModel } from '@/model'
 import { LeftPano, RightFillPano } from '@/layout'
@@ -39,14 +38,16 @@ const tabs = [
   { key: TabKey.guide, text: '路径' },
 ]
 const current = ref(tabs[0].key)
+const showRightCtrl = ref(true)
 
-const showRightPano = computed(() => currentModel.value === fuseModel)
-useViewStack(
-  () => togetherCallback([
-    showRightCtrlPanoStack.push(showRightPano), 
-    showRightPanoStack.push(showRightPano)
-  ])
-)
+watchEffect((onclean) => {
+  const isFuse = currentModel.value === fuseModel
+  if (!isFuse) {
+    onclean(showRightPanoStack.push(ref(false)))
+  }
+  showRightCtrl.value = isFuse
+})
+useViewStack(() => showRightCtrlPanoStack.push(showRightCtrl))
 </script>
 
 <style lang="scss" scoped>

+ 7 - 2
src/views/tagging/index.vue

@@ -61,7 +61,7 @@ import { router, RoutesName } from '@/router'
 import { custom } from '@/env'
 import { 
   taggings, 
-  isTemploraryID, 
+  getTaggingStyle, 
   Tagging, 
   autoSaveTaggings, 
   createTagging,
@@ -69,7 +69,7 @@ import {
   taggingPositions,
   isOld,
   save,
-getTagging
+  getTagging
 } from '@/store'
 
 const showSearch = ref(false)
@@ -81,9 +81,14 @@ const saveHandler = (tagging: Tagging) => {
   if (!editTagging.value) return;
   if (!getTagging(editTagging.value.id)) {
     taggings.value.push(tagging)
+    const style = getTaggingStyle(tagging.styleId)
+    if (style) {
+      style.lastUse = 1
+    }
   } else {
     Object.assign(editTagging.value, tagging)
   }
+  
   editTagging.value = null
 }
 

+ 4 - 1
src/views/tagging/sign.vue

@@ -5,7 +5,10 @@
     @click="edit && getTaggingIsShow(tagging) && emit('select', true)"
   >
     <div class="info">
-      <img :src="getResource(getFileUrl(tagging.images.length ? tagging.images[0] : style.icon))" v-if="style">
+      <img 
+        :src="getResource(getFileUrl(tagging.images[0]))" 
+        v-if="tagging.images.length"
+      >
       <div>
         <p>{{ tagging.title }}</p>
         <span>放置:{{ positions.length }}</span>

+ 8 - 5
src/views/tagging/styles.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="hot-styles">
-    <div class="add item" v-if="!props.all">
+    <div class="add item" v-if="!props.all && styles.length < maxLength">
       <span class="fun-ctrl">
         <ui-input 
           class="input" 
@@ -32,7 +32,7 @@
       </span>
     </div>
     <div 
-      v-if="!props.all && props.styles.length > 6"
+      v-if="!props.all && props.styles.length > maxShowLen"
       class="add item style-more" 
       @click="showAll = !showAll"
     >
@@ -76,6 +76,9 @@ const props = defineProps<{
   all?: boolean
 }>()
 
+const maxLength = 40
+const maxShowLen = computed(() => props.styles.length < maxLength ? 5 : 6)
+
 const emit = defineEmits<{
   (e: 'change', style: TaggingStyle): void
   (e: 'delete', style: TaggingStyle): void
@@ -88,9 +91,9 @@ const styleAll = computed(() => {
   if (props.all) {
     return props.styles
   } else {
-    const styles = props.styles.slice(0, props.styles.length > 6 ? 5 : 6)
-    if (!styles.includes(props.active)) {
-      styles[3] = props.active
+    const styles = props.styles.slice(0, props.styles.length > maxShowLen.value ? maxShowLen.value : maxShowLen.value + 1)
+    if (!styles.includes(props.active) && props.active) {
+      styles[styles.length - 1] = props.active
     }
     return styles
   }

+ 1 - 1
src/views/view/sign.vue

@@ -9,7 +9,7 @@
         type="text" 
         :modelValue="view.title" 
         :maxlength="15"
-        @update:modelValue="(title: string) => $emit('updateTitle', title)"
+        @update:modelValue="(title: string) => $emit('updateTitle', title.trim())"
         v-show="isEditTitle" 
         ref="inputRef" 
         height="28px"