bill 5 ماه پیش
والد
کامیت
fd8236c144

+ 4 - 0
src/api/guide-path.ts

@@ -19,6 +19,8 @@ interface ServiceGuidePath {
   speed: number
   panoInfo?: string
   cover: string
+
+  playAnimation?: boolean
 }
 
 export interface GuidePath {
@@ -36,6 +38,8 @@ export interface GuidePath {
   sort: number
   speed: number
   cover: string
+
+  playAnimation?: boolean
 }
 
 export type GuidePaths = GuidePath[]

+ 13 - 0
src/api/guide.ts

@@ -11,6 +11,10 @@ interface ServiceGuide {
   fusionGuideId: number
   cover: string
   title: string
+  showTaggings?: boolean
+  showMeasure?: boolean
+  showVideo?: boolean
+  showPath?: boolean
 }
 
 export interface Guide {
@@ -19,11 +23,20 @@ export interface Guide {
   title: string
   recoveryContent?: string
   changeAnimationStatus?: boolean
+
+  showTagging: boolean
+  showMeasure: boolean
+  showVideo: boolean
+  showPath: boolean
 }
 
 export type Guides = Guide[]
 
 const serviceToLocal = (serviceGuide: ServiceGuide): Guide => ({
+  showTagging: true,
+  showMeasure: true,
+  showVideo: true,
+  showPath: true,
   ...serviceGuide,
   id: serviceGuide.fusionGuideId.toString(),
 })

+ 14 - 4
src/components/drawing-time/current.vue

@@ -13,6 +13,7 @@
     }"
   />
   <v-line
+    v-if="!hideLine"
     :config="{
         points: [currentX, size!.height, currentX, 10],
         stroke: currentColor,
@@ -37,11 +38,20 @@ import { Transform } from "konva/lib/Util";
 import { Arrow } from "konva/lib/shapes/Arrow";
 import { DC } from "../drawing/dec";
 
-const props = withDefaults(defineProps<{ currentTime: number; follow?: boolean }>(), {
-  follow: false,
-});
+const props = withDefaults(
+  defineProps<{
+    currentTime: number;
+    follow?: boolean;
+    hideLine?: boolean;
+    currentColor?: string;
+  }>(),
+  {
+    follow: false,
+    currentColor: "#fff",
+  }
+);
 
-const currentColor = "#fff";
+const currentColor = props.currentColor;
 const { misPixel } = useGlobalVar();
 const emit = defineEmits<{ (e: "update:currentTime", num: number): void }>();
 

+ 0 - 1
src/components/drawing/hook.ts

@@ -168,7 +168,6 @@ export const useGlobalResize = installGlobalVar(() => {
 
     const dom = stage.value!.getNode().container().parentElement!;
     await asyncTimeout(16)
-    
     size.value = {
       width: dom.offsetWidth,
       height: dom.offsetHeight,

+ 1 - 1
src/hook/ids.ts

@@ -11,7 +11,6 @@ export const useSelects = <T extends { id: any }>(items: Ref<T[]>) => {
     } else {
       ~ndx && selects.value.splice(ndx, 1);
     }
-    console.log(selects.value)
   };
   const updateSelectId = (id: any, select: boolean) => {
     const item = items.value.find((s) => s.id === id);
@@ -43,6 +42,7 @@ export const useSelects = <T extends { id: any }>(items: Ref<T[]>) => {
         items.value.forEach(item => updateSelect(item, select))
       } 
     }),
+    include: (id: string) => selects.value.some(item => item.id === id),
     updateSelect,
     updateSelectId,
   };

+ 38 - 24
src/sdk/association/guide.ts

@@ -6,6 +6,7 @@ import { fuseModels, isEdit, sysBus, fuseModelsLoaded } from "@/store";
 import type { FuseModel, FuseModels, Guide, GuidePath } from "@/store";
 import { analysisPoseInfo } from ".";
 import { fullView, isScenePlayRun, pauseScene, playScene } from "@/utils/full";
+import { animationGroup, currentTime } from "./animation";
 
 // -----------------导览关联--------------------
 
@@ -80,34 +81,47 @@ export const recovery = async (guide: Guide) => {
     );
 };
 
-
 export const playSceneGuide = (
   paths: GuidePath[],
   changeIndexCallback?: (index: number) => void,
   forceFull = false
 ) => {
-  let sceneGuide: SceneGuide
-  return playScene({
-    create: () => {
-      sceneGuide = sdk.enterSceneGuide(
-        paths.map((path) => ({ ...path, ...analysisPoseInfo(path) }))
-      );
-      changeIndexCallback && sceneGuide.bus.on("changePoint", changeIndexCallback);
-    },
-    play: () => {
-      sceneGuide.play();
-      return new Promise((resolve) => sceneGuide.bus.on("playComplete", resolve))
-    },
-    pause: () => {
-      console.error('pause??')
-      sceneGuide.pause();
+  let sceneGuide: SceneGuide;
+  currentTime.value = 0
+  return playScene(
+    {
+      create: () => {
+        sceneGuide = sdk.enterSceneGuide(
+          paths.map((path) => ({ ...path, ...analysisPoseInfo(path) }))
+        );
+        sceneGuide.bus.on("changePoint", (index) => {
+          console.log(index)
+          if (paths[index - 1].playAnimation) {
+            animationGroup && animationGroup.play()
+          }
+          changeIndexCallback && changeIndexCallback(index);
+        });
+      },
+      play: () => {
+        sceneGuide.play();
+        return new Promise((resolve) =>
+          sceneGuide.bus.on("playComplete", resolve)
+        );
+      },
+      pause: () => {
+        console.error("pause??");
+        sceneGuide.pause();
+        animationGroup && animationGroup.pause()
+      },
+      clear: () => {
+        currentTime.value = 0
+        sceneGuide.clear();
+        sceneGuide.bus.off("changePoint");
+      },
     },
-    clear: () => {
-      sceneGuide.clear();
-      sceneGuide.bus.off("changePoint");
-    }
-  }, forceFull)
-}
+    forceFull
+  );
+};
 
-export const pauseSceneGuide = pauseScene
-export const isScenePlayIng = isScenePlayRun
+export const pauseSceneGuide = pauseScene;
+export const isScenePlayIng = isScenePlayRun;

+ 4 - 0
src/store/guide.ts

@@ -36,6 +36,10 @@ export const createGuide = (guide: Partial<Guide> = {}): Guide => ({
   id: createTemploraryID(),
   title: `路径${guides.value.length + 1}`,
   cover: '',
+  showMeasure: true,
+  showTagging: true,
+  showPath: true,
+  showVideo: true,
   ...guide
 })
 

+ 116 - 0
src/views/guide/guide/attach-animation.vue

@@ -0,0 +1,116 @@
+<template>
+  <div class="animation-layout">
+    <div class="info">
+      <div>
+        <ui-icon
+          type="a-animation_s"
+          ctrl
+          @click="attachAnimation"
+          :class="{ disabled: disableAttach, active: isPlayIng }"
+        />
+      </div>
+    </div>
+    <div class="renderer">
+      <Renderer v-model:scale="scale">
+        <v-group>
+          <Time @update-current-time="(val) => (currentTime = val)">
+            <!-- <v-group>
+              <template v-for="(_, ndx) in paths">
+                <TimeCurrent
+                  v-if="ndx >= playNdx"
+                  hideLine
+                  current-color="#00c8af"
+                  :currentTime="getNdxTime(ndx)"
+                />
+              </template>
+            </v-group> -->
+            <TimeCurrent
+              hideLine
+              :currentTime="currentTime"
+              :follow="follow"
+              @update:current-time="(val) => (currentTime = val)"
+            />
+          </Time>
+        </v-group>
+      </Renderer>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import Renderer from "@/components/drawing/renderer.vue";
+import Time from "@/components/drawing-time/time.vue";
+import TimeCurrent from "@/components/drawing-time/current.vue";
+import { currentTime } from "@/sdk/association/animation";
+import { computed, ref, watchEffect } from "vue";
+import { GuidePath } from "@/store";
+
+const props = defineProps<{ current: GuidePath; paths: GuidePath[] }>();
+const scale = ref(1);
+const follow = ref(true);
+
+const playNdx = computed(() => props.paths.findIndex((path) => path.playAnimation));
+const curNdx = computed(() => props.paths.indexOf(props.current));
+const isCurrentPlay = computed(
+  () => playNdx.value !== -1 && curNdx.value === playNdx.value
+);
+const disableAttach = computed(() => playNdx.value !== -1 && !isCurrentPlay.value);
+const isPlayIng = computed(() => playNdx.value !== -1 && curNdx.value >= playNdx.value);
+
+const getNdxTime = (curNdx: number) => {
+  if (playNdx.value === -1 || curNdx < playNdx.value) return 0;
+  let mis = 0;
+  for (let i = playNdx.value; i < curNdx; i++) {
+    mis += props.paths[i].time;
+  }
+  return mis;
+};
+
+watchEffect(() => {
+  currentTime.value = getNdxTime(curNdx.value);
+});
+
+const attachAnimation = () => {
+  props.current.playAnimation = !props.current.playAnimation;
+};
+</script>
+
+<style lang="scss" scoped>
+.animation-layout {
+  height: 30px;
+  display: flex;
+  position: relative;
+  // &::before {
+  //   content: "";
+  //   position: absolute;
+  //   left: 0;
+  //   bottom: 0;
+  //   height: 50%;
+  //   background: rgba(0, 0, 0, 0.5);
+  //   width: 100%;
+  // }
+
+  .info {
+    flex: 0 0 auto;
+    width: 33px;
+    color: #fff;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    flex-direction: column;
+    .icon {
+      display: block;
+      padding: 5px;
+      font-size: 16px;
+
+      &.active {
+        color: var(--colors-primary-base) !important;
+      }
+    }
+  }
+  .renderer {
+    flex: 1;
+    pointer-events: none;
+  }
+}
+</style>

+ 67 - 5
src/views/guide/guide/edit-paths.vue

@@ -16,6 +16,24 @@
         添加视角
       </ui-button>
     </div>
+
+    <Dropdown placement="top">
+      <div class="show-setting strengthen"><span>显示设置</span> <DownOutlined /></div>
+      <template #overlay>
+        <Menu>
+          <menu-item v-for="option in showOptions">
+            <ui-input
+              @click.stop
+              type="checkbox"
+              :modelValue="show.include(option.key)"
+              @update:modelValue="(s: boolean) => show.updateSelectId(option.key, s)"
+              :label="option.text"
+            />
+          </menu-item>
+        </Menu>
+      </template>
+    </Dropdown>
+
     <div class="info" v-if="paths.length">
       <div class="meta">
         <div class="length">
@@ -30,6 +48,7 @@
           <span>清空画面</span>
         </div>
       </div>
+      <attachAnimation :current="current" :paths="paths" />
 
       <div class="photo-list" ref="listVm">
         <template v-for="(path, i) in paths" :key="path.id">
@@ -81,6 +100,8 @@
 </template>
 
 <script setup lang="ts">
+import attachAnimation from "./attach-animation.vue";
+import { Dropdown, Menu, MenuItem } from "ant-design-vue";
 import { loadPack, togetherCallback, getFileUrl, asyncTimeout } from "@/utils";
 import {
   sdk,
@@ -100,7 +121,7 @@ import {
 } from "@/store";
 import { Dialog } from "bill/index";
 import { useViewStack } from "@/hook";
-import { nextTick, onUnmounted, ref, watch, watchEffect } from "vue";
+import { computed, nextTick, onUnmounted, ref, watch, watchEffect } from "vue";
 import {
   showRightPanoStack,
   showLeftCtrlPanoStack,
@@ -108,28 +129,46 @@ import {
   showRightCtrlPanoStack,
   getResource,
   custom,
+  showTaggingsStack,
+  showPathsStack,
+  showMeasuresStack,
 } from "@/env";
 
 import type { Guide, GuidePaths, GuidePath } from "@/store";
 import type { CalcPathProps } from "@/sdk";
+import { DownOutlined } from "@ant-design/icons-vue";
+import { useSelects } from "@/hook/ids";
+import { mergeFuns } from "@/components/drawing/hook";
 
 const props = defineProps<{ data: Guide }>();
 const paths = ref<GuidePaths>(getGuidePaths(props.data));
 const current = ref<GuidePath>(paths.value[0]);
 
+const showOptions = [
+  { text: "标签", key: "tagging" },
+  { text: "监控", key: "video" },
+  { text: "路径", key: "path" },
+  { text: "测量", key: "measure" },
+];
+const show = useSelects(ref(showOptions.map((item) => ({ id: item.key }))));
+show.all.value = true;
+
 const updatePathInfo = (index: number, calcInfo: CalcPathProps[1]) => {
   const info = sdk.calcPathInfo(paths.value.slice(index, index + 2) as any, calcInfo);
   Object.assign(paths.value[index], info);
 };
 
-useViewStack(() =>
-  togetherCallback([
+useViewStack(() => {
+  return togetherCallback([
     showRightPanoStack.push(ref(false)),
     showLeftCtrlPanoStack.push(ref(false)),
     showLeftPanoStack.push(ref(false)),
     showRightCtrlPanoStack.push(ref(false)),
-  ])
-);
+    showTaggingsStack.push(computed(() => show.include("tagging"))),
+    showPathsStack.push(computed(() => show.include("path"))),
+    showMeasuresStack.push(computed(() => show.include("measure"))),
+  ]);
+});
 
 useAutoSetMode(
   paths,
@@ -265,6 +304,21 @@ onUnmounted(() => {
     }
   }
 
+  .show-setting {
+    position: absolute;
+    right: 20px;
+    bottom: 100%;
+    margin-bottom: 20px;
+    width: 160px;
+    background: rgba(27, 27, 28, 0.9);
+    border-radius: 4px;
+    height: 34px;
+    display: flex;
+    padding: 8px;
+    align-items: center;
+    justify-content: space-between;
+  }
+
   .meta {
     font-size: 12px;
     border-bottom: 1px solid rgba(255, 255, 255, 0.16);
@@ -279,6 +333,7 @@ onUnmounted(() => {
     .clear {
       display: flex;
       align-items: center;
+
       .icon {
         font-size: 1.4em;
         margin-right: 5px;
@@ -307,12 +362,14 @@ onUnmounted(() => {
         top: 50%;
         transform: translateY(-50%);
       }
+
       &::before {
         left: 0;
         right: 7px;
         height: 2px;
         background-color: currentColor;
       }
+
       &::after {
         right: -5px;
         width: 0;
@@ -355,6 +412,7 @@ onUnmounted(() => {
     }
   }
 }
+
 .un-video {
   height: 100px;
   line-height: 100px;
@@ -369,16 +427,20 @@ onUnmounted(() => {
   .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;

+ 9 - 8
src/views/guide/guide/edit.vue

@@ -18,14 +18,14 @@
     />
   </ui-group>
   <Teleport to="#layout-app">
-    <ui-editor-toolbar :toolbar="!!currentGuide" class="video-toolbar">
+    <ui-editor-toolbar :toolbar="!!currentGuide" class="video-toolbar" disabledAnimation>
       <EditPaths :data="currentGuide" v-if="currentGuide" />
     </ui-editor-toolbar>
   </Teleport>
 </template>
 
 <script lang="ts" setup>
-import { ref } from "vue";
+import { ref, watchEffect } from "vue";
 import GuideSign from "./sign.vue";
 import EditPaths from "./edit-paths.vue";
 import { useViewStack } from "@/hook";
@@ -35,12 +35,14 @@ import {
   enterEdit,
   sysBus,
   autoSaveGuides,
-  enterOld,
 } from "@/store";
 
 import type { Guide } from "@/store";
 
-const currentGuide = ref<Guide | null>();
+const currentGuide = ref<Guide | null>(null);
+const emit = defineEmits<{(e: 'update:current', v: Guide | null): void}>()
+watchEffect(() => emit('update:current', currentGuide.value))
+
 const leaveEdit = () => (currentGuide.value = null);
 const edit = (guide: Guide) => {
   currentGuide.value = guide;
@@ -57,10 +59,9 @@ useViewStack(autoSaveGuides);
 
 defineExpose({
   add: () => {
-    edit(createGuide())
-  }
-})
-
+    edit(createGuide());
+  },
+});
 </script>
 
 <style lang="scss" scoped>

+ 1 - 1
src/views/guide/index.vue

@@ -16,7 +16,7 @@
     <PathEdit v-if="current === 'path'" ref="quiskObj" />
   </RightFillPano>
   <Teleport to=".laser-layer">
-    <div class="quisks">
+    <div class="quisks" v-if="!isEdit">
       <div class="quisk-item fun-ctrl" @click="quiskAdd('guide')">
         <ui-icon type="a-guide_s" />
         <span>导览</span>

+ 5 - 2
src/views/guide/path/edit.vue

@@ -30,7 +30,7 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, ref } from "vue";
+import { computed, ref, watchEffect } from "vue";
 import PathSign from "./sign.vue";
 import EditPath from "./edit-path.vue";
 import { getPathNode, pathsGroup } from "@/sdk/association/path";
@@ -54,7 +54,10 @@ import { asyncTimeout } from "@/utils";
 const { all, selects, updateSelect } = selectPaths;
 
 
-const currentPath = ref<Path | null>();
+const currentPath = ref<Path | null>(null);
+const emit = defineEmits<{(e: 'update:current', v: Path | null): void}>()
+watchEffect(() => emit('update:current', currentPath.value))
+
 const leaveEdit = () => {
   currentPath.value = null;
   pathsGroup.visibility(true);