Browse Source

Merge branch 'master' of http://192.168.0.115:3000/bill/fuse-code

xzw 3 năm trước cách đây
mục cha
commit
f7785750ee

+ 1 - 1
src/api/model.ts

@@ -6,7 +6,7 @@ import {
 export enum ModelType {
   SWKK,
   SWKJ,
-  SWMX,
+  SWMX = 'glb',
   SWSS = 'laser',
 }
 

+ 9 - 4
src/env/index.ts

@@ -1,12 +1,17 @@
 import { stackFactory, flatStacksValue } from '@/utils'
+import { ref } from 'vue'
 
-export const viewModeStack = stackFactory<'full' | 'auto'>('auto')
-export const showToolbarStack = stackFactory<boolean>(false)
-export const showToolboxStack = stackFactory<boolean>(true)
+export const viewModeStack = stackFactory(ref<'full' | 'auto'>('auto'))
+export const showToolbarStack = stackFactory(ref<boolean>(false))
+export const showRightPanoStack = stackFactory(ref<boolean>(true))
+export const showLeftPanoStack = stackFactory(ref<boolean>(false))
+export const showLeftCtrlPanoStack = stackFactory(ref<boolean>(true))
 
 export const custom = flatStacksValue({
   viewMode: viewModeStack,
   showToolbar: showToolbarStack,
-  showToolbox: showToolboxStack
+  showRightPano: showRightPanoStack,
+  showLeftPano: showLeftPanoStack,
+  showLeftCtrlPano: showLeftCtrlPanoStack
 })
 

+ 3 - 1
src/layout/index.ts

@@ -1,7 +1,9 @@
 import LeftPano from './left-pano.vue'
 import RightPano from './right-pano.vue'
+import RightFillPano from './right-fill-pano.vue'
 
 export {
   LeftPano,
-  RightPano
+  RightPano,
+  RightFillPano
 }

+ 57 - 3
src/layout/left-pano.vue

@@ -1,20 +1,74 @@
 <template>
+  <span 
+    class="ctrl-pano-c fun-ctrl strengthen-right strengthen-top strengthen-bottom"
+    v-if="custom.viewMode !== 'full' && custom.showLeftCtrlPano" 
+    @click="hidePano"
+    :class="{ active: custom.showLeftPano }">
+    <ui-icon type="extend" class="icon"></ui-icon>
+  </span>
   <div class="left-pano strengthen">
     <slot></slot>
   </div>
 </template>
 
+<script lang="ts" setup>
+import { custom } from '@/env'
+
+const hidePano = () => {
+  custom.showLeftPano = !custom.showLeftPano
+  custom.showRightPano = !custom.showLeftPano
+}
+</script>
+
 <style lang="scss" scoped>
 .left-pano {
   position: absolute;
-  width: 340px;
+  width: var(--left-pano-width);
   background: rgba(27,27,28,0.8);
   top: calc(var(--editor-head-height) + var(--header-top));
-  left: calc(var(--editor-menu-left) + var(--editor-menu-width));
+  left: var(--left-pano-left);
   bottom: 0;
-  z-index: 2000;
+  z-index: 1000;
   backdrop-filter: blur(4px);
   overflow-y: auto;
   transition: all .3s ease;
 }
+
+.ctrl-pano-c {
+  position: absolute;
+  left: calc(var(--left-pano-left) + var(--left-pano-width));
+  width: 20px;
+  height: 80px;
+  background: rgba(26, 26, 26, 0.8);
+  border-radius: 0 6px 6px 0;
+  top: 50%;
+  transform: translateY(-50%);
+  z-index: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: rgba(255, 255, 255, 0.6);
+  font-size: 14px;
+  cursor: pointer;
+  transition: inset .3s ease, color .3s ease;
+
+  &:hover {
+    color: rgba(255, 255, 255, 1);
+  }
+
+  &:active {
+    color: var(--colors-primary-base);
+  }
+
+  .icon {
+    display: inline-block;
+    transition: transform .3s ease;
+    transform: rotate(0)
+  }
+  &.active {
+    .icon {
+      transform: rotate(180deg);
+    }
+  }
+}
 </style>

+ 13 - 2
src/layout/main.vue

@@ -7,7 +7,8 @@
     <template v-if="loaded" style="height: 100%">
       <SlideMenu />
       <Header></Header>
-
+      <ModelList />
+      
       <router-view v-slot="{ Component }">
         <keep-alive>
           <component :is="Component" />
@@ -24,13 +25,15 @@ import { isEdit, loaded } from '@/store'
 import { initialSDK } from '@/sdk'
 import SlideMenu from './slide-menu.vue'
 import Header from './header/index.vue'
+import ModelList from './model-list/index.vue'
 
 const layoutClassNames = computed(() => {
   return {
     [`sys-view-${custom.viewMode}`]: true,
     'edit-mode': isEdit.value || custom.showToolbar,
     'setting-mode': custom.showToolbar,
-    'hide-box-mode': !custom.showToolbox
+    'hide-right-box-mode': !custom.showRightPano,
+    'hide-left-box-mode': !custom.showLeftPano,
   }
 })
 
@@ -55,6 +58,14 @@ const stopSdkInstalWatch = watchEffect(() => {
   --search-left: 52px;
 }
 
+.hide-right-box-mode {
+  --editor-menu-right: calc(-1 * var(--editor-toolbox-width)) !important;
+}
+
+.hide-left-box-mode {
+  --left-pano-left: calc(var(--editor-menu-width) - var(--left-pano-width)) !important;
+}
+
 .sys-view-auto {
   --header-top: var(--show-header-top);
   --search-left: 0px;

+ 55 - 0
src/layout/model-list/index.vue

@@ -0,0 +1,55 @@
+<template>
+  <LeftPano>
+    <List 
+      title="数据列表" 
+      key="id" 
+      :data="modelList" 
+      @change-select="item => modelChangeSelect(item.raw)"
+    >
+      <template #action>
+        <ui-icon type="add" ctrl/>
+      </template>
+      <template #atom="{ item }">
+        <ModelSign :model="item.raw" @delete="modelDelete(item.raw)" />
+      </template>
+    </List>
+  </LeftPano>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref, toRaw } from 'vue'
+import { LeftPano } from '@/layout'
+import { models, currentModel } from '@/store'
+import { getSceneModel } from '@/sdk'
+import List from '@/components/list/index.vue'
+import ModelSign from './sign.vue'
+
+import type { Model } from '@/store'
+
+const modelList = computed(() => 
+  models.value.map(model => ({
+    raw: model,
+    select: currentModel.value === model
+  }))
+)
+
+const modelChangeSelect = (model: Model) => {
+  if (currentModel.value) {
+    getSceneModel(currentModel.value)?.changeSelect(false)
+  }
+  if (toRaw(currentModel.value) !== toRaw(model)) {
+    getSceneModel(model)?.changeSelect(true)
+    currentModel.value = model
+  } else {
+    currentModel.value = null
+  }
+}
+
+const modelDelete = (model: Model) => {
+  const index = models.value.indexOf(model)
+  if (~index) {
+    models.value.splice(index, 1)
+  }
+}
+
+</script>

src/views/merge/sign.vue → src/layout/model-list/sign.vue


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

@@ -0,0 +1,25 @@
+.model-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 10px;
+  
+  p {
+    font-size: 14px;
+    color: #fff;
+  }
+}
+
+.model-desc {
+  color: rgba(255,255,255,0.6);
+  line-height: 18px;
+  font-size: 12px;
+}
+
+.model-action {
+  display: flex;
+  align-items: center;
+  > * {
+    margin-left: 20px;
+  }
+}

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

@@ -0,0 +1,64 @@
+<template>
+  <span 
+    class="ctrl-pano-c fun-ctrl strengthen-left strengthen-top strengthen-bottom"
+    v-if="custom.viewMode !== 'full'" 
+    @click="hidePano"
+    :class="{ active: custom.showRightPano }">
+    <ui-icon type="extend" class="icon"></ui-icon>
+  </span>
+  <ui-editor-toolbox :toolbox="true" disabledAnimation>
+    <slot></slot>
+  </ui-editor-toolbox>
+</template>
+
+<script setup lang="ts">
+import { custom } from '@/env'
+
+const hidePano = () => {
+  custom.showRightPano = !custom.showRightPano
+  custom.showLeftPano = !custom.showRightPano
+  console.log(custom.showRightPano)
+}
+
+</script>
+
+<style lang="scss" scoped>
+.ctrl-pano-c {
+  position: absolute;
+  right: calc(var(--editor-menu-right) + var(--editor-toolbox-width));
+  width: 20px;
+  height: 80px;
+  background: rgba(26, 26, 26, 0.8);
+  border-radius: 6px 0px 0px 6px;
+  top: 50%;
+  transform: translateY(-50%);
+  z-index: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: rgba(255, 255, 255, 0.6);
+  font-size: 14px;
+  cursor: pointer;
+  transition: inset .3s ease, color .3s ease;
+
+  &:hover {
+    color: rgba(255, 255, 255, 1);
+  }
+
+  &:active {
+    color: var(--colors-primary-base);
+  }
+
+  .icon {
+    display: inline-block;
+    transition: transform .3s ease;
+    transform: rotate(180deg);
+  }
+  &.active {
+    .icon {
+      transform: rotate(0)
+    }
+  }
+}
+
+</style>

+ 4 - 4
src/layout/slide-menu.vue

@@ -13,12 +13,12 @@ import type { Items } from '@/components/menu/index.vue'
 
 const items: Items = [
   {
-    name: RoutesName.scene,
-    ...metas[RoutesName.scene]
-  },
-  {
     name: RoutesName.merge,
     ...metas[RoutesName.merge]
+  },
+  {
+    name: RoutesName.tagging,
+    ...metas[RoutesName.tagging]
   }
 ]
 

+ 6 - 6
src/router/config.ts

@@ -8,16 +8,16 @@ export const routes: RouteRecordRaw[] = [
     component: () => import('@/layout/main.vue'),
     children: [
       {
-        path: paths.scene,
-        name: RoutesName.scene,
-        meta: metas.scene,
-        component: () => import('@/views/query/index.vue')
-      },
-      {
         path: paths.merge,
         name: RoutesName.merge,
         meta: metas.merge,
         component: () => import('@/views/merge/index.vue')
+      },
+      {
+        path: paths.tagging,
+        name: RoutesName.tagging,
+        meta: metas.tagging,
+        component: () => import('@/views/tagging/index.vue')
       }
     ],
   }

+ 9 - 9
src/router/constant.ts

@@ -1,26 +1,26 @@
 export enum RoutesName {
-  scene = 'scene',
-  merge = 'merge'
+  merge = 'merge',
+  tagging = 'tagging'
 }
 
 type RouteSeting<T> = {[key in RoutesName]: T}
 
 export const paths: RouteSeting<string> = {
-  [RoutesName.scene]: '/scene',
-  [RoutesName.merge]: '/merge'
+  [RoutesName.merge]: '/merge',
+  [RoutesName.tagging]: '/tagging'
 }
 
 export type Meta = { title: string, icon: string }
 
 export const metas: RouteSeting<Meta> = {
-  [RoutesName.scene]: {
-    icon: 'nav-browse',
-    title: '场景'
-  },
   [RoutesName.merge]: {
     icon: 'nav-browse',
     title: '拼接'
+  },
+  [RoutesName.tagging]: {
+    icon: 'nav-browse',
+    title: '标注'
   }
 }
 
-export const ViewHome = RoutesName.scene
+export const ViewHome = RoutesName.merge

+ 5 - 5
src/sdk/association.ts

@@ -11,7 +11,7 @@ import type { SDK, SceneModel } from '.'
 import type { Model } from '@/api'
 
 const sceneModelMap = new WeakMap<Model, SceneModel>()
-export const getSceneModel = (model: Model) => sceneModelMap.get(toRaw(model))
+export const getSceneModel = (model: Model | null) => model && sceneModelMap.get(toRaw(model))
 
 const associationModels = (sdk: SDK) => {
   const getModels = () => models.value
@@ -23,11 +23,11 @@ const associationModels = (sdk: SDK) => {
       }
 
       const itemRaw = toRaw(item)
-      // const sceneModel = sdk.addModel(itemRaw)
-      // sceneModelMap.set(itemRaw, sceneModel)
+      const sceneModel = sdk.addModel(itemRaw)
+      sceneModelMap.set(itemRaw, sceneModel)
 
-      // sceneModel.on('position', pos => item.position = pos)
-      // sceneModel.on('rotation', rot => item.rotation = rot)
+      sceneModel.on('position', pos => item.position = pos)
+      sceneModel.on('rotation', rot => item.rotation = rot)
     }
     for (const item of deleted) {
       getSceneModel(item)?.destroy()

+ 18 - 4
src/store/model.ts

@@ -1,9 +1,9 @@
 import { ref } from 'vue'
 import { getModels, ModelType } from '@/api'
-import sdk from '@/sdk'
 
-import type { Models } from '@/api'
+import type { Models, Model } from '@/api'
 
+export const currentModel = ref<Model | null>(null)
 export const models = ref<Models>([])
 
 
@@ -12,8 +12,22 @@ export const initialModels = async () => {
   models.value = [
     {
       id: '123',
-      url: '',
-      type: ModelType.SWKJ,
+      url: 'SS-t-7DUfWAUZ3V',
+      type: ModelType.SWSS,
+      title: 'SS-t-7DUfWAUZ3V',
+      size: 1000,
+      time: '2012-02-05',
+      scale: 1,
+      rotation: { x: 1, y: 1, z: 1},
+      position: { x: 1, y: 1, z: 1},
+      opacity: 0.1,
+      bottom: 1,
+      show: true
+    },
+    {
+      id: '124',
+      url: '/lib/resources/models/glb/coffeemat.glb',
+      type: ModelType.SWMX,
       title: '某安外',
       size: 1000,
       time: '2012-02-05',

+ 3 - 0
src/style.scss

@@ -24,6 +24,9 @@ body {
   --taggle-btn-width: 30px;
 
   --body-right-margin: 20px;
+
+  --left-pano-left: calc(var(--editor-menu-left) + var(--editor-menu-width));
+  --left-pano-width: 340px;
 }
 
 

+ 7 - 3
src/utils/stack.ts

@@ -3,7 +3,7 @@ import { computed, shallowReactive, isRef } from 'vue'
 import type { ComputedRef, UnwrapRef } from 'vue'
 
 export type Stack<T> = {
-  push: (raw: T) => void
+  push: (raw: T) => () => void
   pop: () => T
   length: ComputedRef<number>
   current: ComputedRef<T>
@@ -20,6 +20,10 @@ export const stackFactory = <T>(initVal?: T, debug?: boolean): Stack<T> => {
   return {
       push(raw: T) {
           stack.push(raw)
+          return () => {
+            const index = stack.indexOf(raw)
+            ~index && stack.splice(index, 1)
+          }
       },
       pop() {
           let ret = stack[stack.length-- - 1]
@@ -47,10 +51,10 @@ export const flatStacksValue = <T extends { [key in any]: Stack<any> }>(stacks:
         set(_, key: keyof T, val) {
             if (isRef(stacks[key].current.value)) {
                 ;(stacks[key].current.value as any).value = val
-                return true
             } else {
-                return false
+                (stacks[key].current.value as any) = val
             }
+            return true
         },
     })
     return proxy

+ 0 - 62
src/views/merge/edit.vue

@@ -1,62 +0,0 @@
-<template>
-  <ui-group>
-    <template #header>
-        <Actions class="edit-header" :items="actionItems" />
-    </template>
-    <ui-group-option label="等比缩放">
-      <template #icon>
-        <a href="">设置比例</a>
-      </template>
-      <ui-input type="range" v-model="model.scale" v-bind="scaleOption" width="100%" />
-    </ui-group-option>
-    <ui-group-option label="离地高度">
-      <ui-input type="range" v-model="model.bottom" v-bind="bottomOption" width="100%"/>
-    </ui-group-option>
-    <ui-group-option label="模型不透明度">
-      <ui-input type="range" v-model="model.opacity" v-bind="opacityOption" width="100%" />
-    </ui-group-option>
-    <ui-group-option>
-      <ui-button>配准</ui-button>
-    </ui-group-option>
-    <ui-group-option>
-      <ui-button>恢复默认</ui-button>
-    </ui-group-option>
-  </ui-group>
-</template>
-
-<script lang="ts" setup>
-import { getSceneModel } from '@/sdk'
-import Actions from '@/components/actions/index.vue'
-
-import type { Model } from '@/store'
-import type { ActionsProps } from '@/components/actions/index.vue'
-
-const props = defineProps<{model: Model}>()
-const sceneModel = getSceneModel(props.model)
-
-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, }
-const actionItems: ActionsProps['items'] = [
-  { 
-    icon: 'move', 
-    text: '移动', 
-    action: () => {
-      sceneModel?.enterMoveMode()
-      return () => sceneModel?.leaveMoveMode()
-    } 
-  },
-  { 
-    icon: 'flip', 
-    text: '旋转', 
-    action: () => {
-      sceneModel?.enterRotateMode()
-      return () => sceneModel?.leaveRotateMode()
-    } 
-  },
-]
-
-
-</script>
-
-<style lang="scss" scoped src="./style.scss" />

+ 59 - 47
src/views/merge/index.vue

@@ -1,60 +1,72 @@
 <template>
-  <LeftPano>
-    <List 
-      title="数据列表" 
-      key="id" 
-      :data="modelList" 
-      @change-select="item => modelChangeSelect(item.raw)"
-    >
-      <template #action>
-        <ui-icon type="add" ctrl/>
+  <RightPano v-if="currentModel">
+    <ui-group>
+      <template #header>
+          <Actions class="edit-header" :items="actionItems" />
       </template>
-      <template #atom="{ item }">
-        <ModelSign :model="item.raw" @delete="modelDelete(item.raw)" />
-      </template>
-    </List>
-  </LeftPano>
-  <RightPano v-if="selectModel">
-    <ModelEdit :model="selectModel" />
+      <ui-group-option label="等比缩放">
+        <template #icon>
+          <a href="">设置比例</a>
+        </template>
+        <ui-input type="range" v-model="currentModel.scale" v-bind="scaleOption" width="100%" />
+      </ui-group-option>
+      <ui-group-option label="离地高度">
+        <ui-input type="range" v-model="currentModel.bottom" v-bind="bottomOption" width="100%"/>
+      </ui-group-option>
+      <ui-group-option label="模型不透明度">
+        <ui-input type="range" v-model="currentModel.opacity" v-bind="opacityOption" width="100%" />
+      </ui-group-option>
+      <ui-group-option>
+        <ui-button>配准</ui-button>
+      </ui-group-option>
+      <ui-group-option>
+        <ui-button>恢复默认</ui-button>
+      </ui-group-option>
+    </ui-group>
   </RightPano>
 </template>
 
 <script lang="ts" setup>
-import { computed, ref, toRaw } from 'vue'
-import { LeftPano, RightPano } from '@/layout'
-import { models } from '@/store'
+import { RightPano } from '@/layout'
+import { currentModel } from '@/store'
+import Actions from '@/components/actions/index.vue'
 import { getSceneModel } from '@/sdk'
-import List from '@/components/list/index.vue'
-import ModelSign from './sign.vue'
-import ModelEdit from './edit.vue'
-
-import type { Model } from '@/store'
+import { useViewStack } from '@/hook'
+import { showLeftCtrlPanoStack, showLeftPanoStack } from '@/env'
+import { ref } from 'vue'
 
-const selectModel = ref<Model | null>(null)
-const modelList = computed(() => 
-  models.value.map(model => ({
-    raw: model,
-    select: selectModel.value === model
-  }))
-)
+import type { ActionsProps } from '@/components/actions/index.vue'
 
-const modelChangeSelect = (model: Model) => {
-  if (selectModel.value) {
-    getSceneModel(selectModel.value)?.changeSelect(false)
-  }
-  if (toRaw(selectModel.value) !== toRaw(model)) {
-    getSceneModel(model)?.changeSelect(true)
-    selectModel.value = model
-  } else {
-    selectModel.value = null
-  }
-}
+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, }
+const actionItems: ActionsProps['items'] = [
+  { 
+    icon: 'move', 
+    text: '移动', 
+    action: () => {
+      getSceneModel(currentModel.value)?.enterMoveMode()
+      return () => getSceneModel(currentModel.value)?.leaveMoveMode()
+    } 
+  },
+  { 
+    icon: 'flip', 
+    text: '旋转', 
+    action: () => {
+      getSceneModel(currentModel.value)?.enterRotateMode()
+      return () => getSceneModel(currentModel.value)?.leaveRotateMode()
+    } 
+  },
+]
 
-const modelDelete = (model: Model) => {
-  const index = models.value.indexOf(model)
-  if (~index) {
-    models.value.splice(index, 1)
+useViewStack(() => {
+  const pops = [
+    showLeftCtrlPanoStack.push(ref(false)),
+    showLeftPanoStack.push(ref(true))
+  ]
+  return () => {
+    pops.forEach(pop => pop())
   }
-}
+})
 
 </script>

+ 10 - 0
src/views/tagging/index.vue

@@ -0,0 +1,10 @@
+<template>
+  <RightFillPano>
+    123123
+  </RightFillPano>
+</template>
+
+<script lang="ts" setup>
+import { RightFillPano } from '@/layout'
+
+</script>