bill 4 月之前
父节点
当前提交
e02452fee2

+ 26 - 3
src/components/bill-ui/components/icon/iconfont/demo_index.html

@@ -55,6 +55,12 @@
           <ul class="icon_lists dib-box">
           
             <li class="dib">
+              <span class="icon iconfont">&#xe79c;</span>
+                <div class="name">guide_p</div>
+                <div class="code-name">&amp;#xe79c;</div>
+              </li>
+          
+            <li class="dib">
               <span class="icon iconfont">&#xe79b;</span>
                 <div class="name">view</div>
                 <div class="code-name">&amp;#xe79b;</div>
@@ -726,9 +732,9 @@
 <pre><code class="language-css"
 >@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.woff2?t=1741831605477') format('woff2'),
-       url('iconfont.woff?t=1741831605477') format('woff'),
-       url('iconfont.ttf?t=1741831605477') format('truetype');
+  src: url('iconfont.woff2?t=1743585764913') format('woff2'),
+       url('iconfont.woff?t=1743585764913') format('woff'),
+       url('iconfont.ttf?t=1743585764913') format('truetype');
 }
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -755,6 +761,15 @@
         <ul class="icon_lists dib-box">
           
           <li class="dib">
+            <span class="icon iconfont icon-guide_p"></span>
+            <div class="name">
+              guide_p
+            </div>
+            <div class="code-name">.icon-guide_p
+            </div>
+          </li>
+          
+          <li class="dib">
             <span class="icon iconfont icon-view"></span>
             <div class="name">
               view
@@ -1764,6 +1779,14 @@
           
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-guide_p"></use>
+                </svg>
+                <div class="name">guide_p</div>
+                <div class="code-name">#icon-guide_p</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#icon-view"></use>
                 </svg>
                 <div class="name">view</div>

+ 7 - 3
src/components/bill-ui/components/icon/iconfont/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 4647199 */
-  src: url('iconfont.woff2?t=1741831605477') format('woff2'),
-       url('iconfont.woff?t=1741831605477') format('woff'),
-       url('iconfont.ttf?t=1741831605477') format('truetype');
+  src: url('iconfont.woff2?t=1743585764913') format('woff2'),
+       url('iconfont.woff?t=1743585764913') format('woff'),
+       url('iconfont.ttf?t=1743585764913') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,10 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-guide_p:before {
+  content: "\e79c";
+}
+
 .icon-view:before {
   content: "\e79b";
 }

文件差异内容过多而无法显示
+ 1 - 1
src/components/bill-ui/components/icon/iconfont/iconfont.js


+ 7 - 0
src/components/bill-ui/components/icon/iconfont/iconfont.json

@@ -6,6 +6,13 @@
   "description": "",
   "glyphs": [
     {
+      "icon_id": "43886962",
+      "name": "guide_p",
+      "font_class": "guide_p",
+      "unicode": "e79c",
+      "unicode_decimal": 59292
+    },
+    {
       "icon_id": "43623992",
       "name": "view",
       "font_class": "view",

二进制
src/components/bill-ui/components/icon/iconfont/iconfont.ttf


二进制
src/components/bill-ui/components/icon/iconfont/iconfont.woff


二进制
src/components/bill-ui/components/icon/iconfont/iconfont.woff2


+ 2 - 1
src/components/drawing-time/time.vue

@@ -10,7 +10,6 @@
         opacity: 0,
         ...invConfig,
       }"
-      @click="clickHandler"
       ref="line"
     />
     <slot />
@@ -20,6 +19,7 @@
 <script setup lang="ts">
 import { computed, ref } from "vue";
 import {
+  useClickHandler,
   useGlobalResize,
   useGlobalVar,
   useHoverPointer,
@@ -116,4 +116,5 @@ const clickHandler = () => {
   const x = invMat.value.point(pos).x;
   emit("updateCurrentTime", x / misPixel);
 };
+useClickHandler(line, clickHandler);
 </script>

+ 39 - 3
src/components/drawing/hook.ts

@@ -72,7 +72,7 @@ export const useGlobalVar = installGlobalVar(() => {
 
 export const onlyId = () => uuid();
 
-export const stackVar = <T>(init?: T) => {
+export const stackVar = <T>(init?: T, test = false) => {
   const factory = (init: T) => ({ var: init, id: onlyId() });
   const stack = reactive([]) as { var: T; id: string }[];
   if (init) {
@@ -86,6 +86,7 @@ export const stackVar = <T>(init?: T) => {
       stack[stack.length - 1].var = val;
     },
     push(data: T) {
+      test && console.error('push', data)
       stack.push(factory(data));
       const item = stack[stack.length - 1];
       const pop = (() => {
@@ -95,11 +96,13 @@ export const stackVar = <T>(init?: T) => {
         }
       }) as (() => void) & { set: (data: T) => void };
       pop.set = (data) => {
+        test && console.error('pop', data)
         item.var = data;
       };
       return pop;
     },
     pop() {
+      test && console.error('pop')
       if (stack.length - 1 > 0) {
         stack.pop();
       } else {
@@ -117,7 +120,7 @@ export const stackVar = <T>(init?: T) => {
 };
 
 export const useCursor = installGlobalVar(
-  () => stackVar("default"),
+  () => stackVar("default", false),
   Symbol("cursor")
 );
 
@@ -491,6 +494,7 @@ export const usePause = <T extends object>(api?: T): PausePack<T> => {
 
 const hoverPointer = (shape: EntityShape, cursor: ReturnType<typeof useCursor>) => {
   shape.on("pointerenter.hover", () => {
+    if (downing) return;
     const pop = cursor.push("pointer");
     shape.on("pointerleave.hover", () => {
       pop();
@@ -502,6 +506,34 @@ const hoverPointer = (shape: EntityShape, cursor: ReturnType<typeof useCursor>)
   }
 }
 
+export const useClickHandler = (shape: Ref<DC<EntityShape> |  undefined>, callback: () => void) => {
+  watchEffect((onCleanup) => {
+    if (!shape.value) return;
+    
+    const $shape = shape.value.getNode()
+    let downPos:Pos | null
+    const downHandler = (ev: KonvaEventObject<any>) => {
+      downPos = getOffset(ev.evt)
+      $shape.on('pointerup.clickHandler', upHandler)
+
+    }
+    const upHandler = (ev: KonvaEventObject<any>) => {
+      const upPos = getOffset(ev.evt)
+      if (lineLen(downPos!, upPos) < 0.01) {
+        callback()
+      }
+      $shape.off('pointerup.clickHandler', upHandler)
+    }
+    $shape.on('pointerdown.clickHandler', downHandler)
+
+    onCleanup(() => {
+      $shape.off('pointerdown.clickHandler', downHandler)
+      $shape.off('pointerup.clickHandler', upHandler)
+    })
+  })
+}
+
+let downing = false
 export const useHoverPointer = (shape: Ref<DC<EntityShape> |  undefined>) => {
   const cursor = useCursor()
   watchEffect((onCleanup) => {
@@ -523,6 +555,7 @@ export const useDrag = (
 
   const init = (shape: EntityShape, dom: HTMLDivElement, ndx: number) => {
     shape.on("pointerenter.drag", () => {
+      if (downing) return;
       const pop = cursor.push("pointer");
       shape.on("pointerleave.drag", () => {
         pop();
@@ -533,11 +566,12 @@ export const useDrag = (
     let pop: (() => void) | null = null;
     let start = { x: 0, y: 0 }
     shape.on("pointerdown.drag", (ev) => {
+      downing = true
       pop = cursor.push("move")
       start = invMat.value.point(getOffset(ev.evt, stage.value!.getNode().container()));
+      shape.draggable(true);
     });
 
-    shape.draggable(true);
     shape.dragBoundFunc(function (this: any, _: any, ev: MouseEvent) {
       if (ev.buttons <= 0) return upHandler()
       const current = invMat.value.point(getOffset(ev, stage.value!.getNode().container()));
@@ -550,9 +584,11 @@ export const useDrag = (
       return this.absolutePosition();
     });
     const upHandler = () => {
+      downing = false
       pop && pop();
       pop = null;
       drag.value = undefined;
+      shape.draggable(false);
     }
 
     return mergeFuns(

+ 4 - 0
src/components/tagging/sign-new.vue

@@ -241,8 +241,12 @@ const iconClickHandler = () => {
   }
 };
 
+console.log("标签 创建", props.tagging.id);
 onUnmounted(() => {
   tag.destroy();
+  clearTimeout(timeout);
+  clearTimeout(changeTimeout)
+  console.error("标签 销毁", props.tagging.id);
 });
 
 defineExpose(tag);

+ 0 - 1
src/layout/bottom-pano.vue

@@ -1,5 +1,4 @@
 <template>
-  {{ custom.showBottomBar }}
   <ui-editor-toolbar toolbar class="animation-toolbar" v-if="custom.showBottomBar">
     <slot />
   </ui-editor-toolbar>

+ 2 - 2
src/router/constant.ts

@@ -88,8 +88,8 @@ export const metas = {
     sysTitle: "多元融合",
   },
   [RoutesName.guide]: {
-    icon: "path",
-    title: "路径",
+    icon: "guide_p",
+    title: "导览",
     sysTitle: "多元融合",
   },
   [RoutesName.animation]: {

+ 1 - 1
src/sdk/sdk.ts

@@ -387,7 +387,7 @@ export type AnimationGroup = {
 };
 
 export type AnimationModel3D = {
-  getSupportActions: () => string[]
+  getSupportActions: () => {name: string, duration: number}[]
 
   // 销毁动画模型
   destroy: () => void;

+ 5 - 2
src/views/animation/index.vue

@@ -28,7 +28,7 @@
       @change-frame-action="(action) => (frameAction = action.action)"
     />
 
-    <BottomPano>
+    <BottomPano class="strengthen-top">
       <Bottom
         :am="focusAM"
         :follow="play || follow"
@@ -102,7 +102,10 @@ watchEffect(() => {
 
 const amM = computed(() => focusAM.value && amMap[getAMKey(focusAM.value)]);
 
-onUnmounted(() => (currentTime.value = 0));
+onUnmounted(() => {
+  currentTime.value = 0;
+  play.value = false;
+});
 
 const asyncOper = (item: AnimationModel, oper: (obj: AnimationModel3D) => void) => {
   let onCleanup = () => {};

+ 17 - 3
src/views/animation/right/action.vue

@@ -22,6 +22,9 @@
         </ui-group-option>
         <ui-group-option>
           <SignItem label="速度" not-apply>
+            <template v-slot:append>
+              <span v-if="dur">时长: {{ round(dur, 2) }}S</span>
+            </template>
             <Slider v-model:value="data.speed" :min="0.1" :max="10" :step="0.1" />
           </SignItem>
         </ui-group-option>
@@ -38,7 +41,7 @@
               :min="0.1"
               @change="(ev: any) => $emit('updateDuration', Math.max(0.1, Number(ev.target.value)))"
             />
-            S
+            &nbsp;S
           </span>
         </ui-group-option>
       </ui-group>
@@ -48,11 +51,22 @@
 
 <script lang="ts" setup>
 import { Slider, TabPane, Tabs } from "ant-design-vue";
-import { AnimationModelAction } from "@/api";
+import { AnimationModel, AnimationModelAction } from "@/api";
 import SignItem from "@/views/tagging-position/sign-item.vue";
+import { amMap } from "@/sdk/association/animation";
+import { computed, ref } from "vue";
+import { round } from "@/utils";
 
-defineProps<{ data: AnimationModelAction }>();
+const props = defineProps<{ data: AnimationModelAction; am: AnimationModel }>();
 defineEmits<{ (e: "updateDuration", dur: number): void }>();
+
+const dur = computed(() => {
+  const actions = amMap[props.am.id].am?.getSupportActions() || [];
+  const action = actions.find((item) => item.name === props.data.key);
+  if (action?.duration) {
+    return action?.duration / props.data.speed;
+  }
+});
 </script>
 
 <style scoped lang="scss">

+ 11 - 4
src/views/animation/right/am.vue

@@ -77,7 +77,7 @@
         <ui-group-option class="item">
           <span class="label">动作库</span>
         </ui-group-option>
-        <ui-group-option class="item" v-for="action in amActions">
+        <ui-group-option class="item action-item" v-for="action in amActions">
           <span class="label">{{ action.title }}</span>
           <span class="oper">
             <ui-icon
@@ -171,13 +171,12 @@ const actionsMap: Record<string, string> = {
 const keys = Object.keys(actionsMap);
 const amActions = computed(() => {
   const actions = amMap[props.am.id].am?.getSupportActions() || [];
-  return actions.map((action) => {
+  return actions.map(({ name: action, duration }) => {
     let key = action.toLowerCase().replaceAll(/( |-)/gi, "_");
     !keys.find((k) => key.includes(k)) && console.log(key);
     key = keys.find((k) => key.includes(k)) || key;
 
-    console.log(action, key);
-    return { action, title: actionsMap[key] || action };
+    return { action, title: actionsMap[key] || action, duration };
   });
 });
 
@@ -203,6 +202,14 @@ const selectPathHandler = () => {
   display: flex;
   align-items: center;
   justify-content: space-between;
+
+  &.action-item {
+    height: 38px;
+    background: rgba(255, 255, 255, 0.1);
+    border-radius: 4px 4px 4px 4px;
+    margin-bottom: 10px;
+    padding: 0 10px;
+  }
 }
 .pin-position {
   position: absolute;

+ 1 - 0
src/views/animation/right/index.vue

@@ -25,6 +25,7 @@
       <component
         @updateDuration="setDuration"
         :is="(comps[activeAttrib.key] as any)"
+        :am="am"
         :data="am[activeAttrib.key][activeAttrib.ndx]"
       />
     </template>

+ 1 - 1
src/views/animation/right/path.vue

@@ -42,7 +42,7 @@
               :min="0.1"
               @change="(ev: any) => $emit('updateDuration', Math.max(0.1, Number(ev.target.value)))"
             />
-            S
+            &nbsp;S
           </span>
         </ui-group-option>
       </ui-group>

+ 1 - 1
src/views/animation/right/subtitle.vue

@@ -42,7 +42,7 @@
               :min="0.1"
               @change="(ev: any) => $emit('updateDuration', Math.max(0.1, Number(ev.target.value)))"
             />
-            S
+            &nbsp;S
           </span>
         </ui-group-option>
 

+ 3 - 1
src/views/merge/index.vue

@@ -5,7 +5,7 @@
   >
     <div class="actions-group">
       <Actions :items="actionItems" v-model:current="currentItem" />
-      <Actions class="merge-action" :items="othActions" />
+      <Actions class="merge-action" :items="othActions" single />
     </div>
     <ui-group>
       <ui-group-option label="等比缩放">
@@ -109,6 +109,7 @@ const othActions = reactive([
     icon: "rectification",
     text: "配准",
     disabled: isOld,
+    single: true,
     action: () => {
       router.push({
         name: RoutesName.registration,
@@ -119,6 +120,7 @@ const othActions = reactive([
   {
     icon: "reset",
     text: "恢复默认",
+    single: true,
     action: () => {
       reset();
     },

+ 13 - 14
src/views/registration/index.vue

@@ -84,6 +84,7 @@ import {
 } from "@/env";
 
 import type { ControlExpose } from "@/components/control-panl";
+import { mergeFuns } from "@/components/drawing/hook";
 
 const isCurrent = computed(
   () => router.currentRoute.value.name === RoutesName.registration
@@ -141,21 +142,19 @@ watchEffect((onCleanup) => {
   const smodel = sceneModel.value;
   if (smodel) {
     smodel.enterAlignment();
-    const pop = currentModelStack.push(model as any);
-    showPathStack.push(ref(undefined));
-    showPathsStack.push(ref(false));
-    showTaggingsStack.push(ref(false));
-
     selectOptions.value = [options[0]];
-
-    onCleanup(() => {
-      smodel.leaveTransform();
-      smodel.leaveAlignment();
-      showPathsStack.pop();
-      showPathStack.pop();
-      showTaggingsStack.pop();
-      pop();
-    });
+    onCleanup(
+      mergeFuns(
+        () => {
+          smodel.leaveTransform();
+          smodel.leaveAlignment();
+        },
+        currentModelStack.push(model as any),
+        showPathStack.push(ref(undefined)),
+        showPathsStack.push(ref(false)),
+        showTaggingsStack.push(ref(false))
+      )
+    );
   } else if (isCurrent.value) {
     leave();
   }

+ 2 - 0
src/views/setting/back-item.vue

@@ -4,6 +4,8 @@
     <i class="iconfont" :class="url" v-else-if="type === 'icon'" />
     <span :style="{ background: url }" v-else></span>
     <p class="back-item-desc">{{ label }}</p>
+
+    <div class="border"></div>
   </div>
 </template>
 <script lang="ts" setup>

+ 1 - 1
src/views/setting/select-back.vue

@@ -133,7 +133,7 @@ const activeParent = computed(() => {
     cursor: pointer;
   }
   &.active::after {
-    inset: -2px;
+    inset: 0px;
   }
 }
 </style>

+ 1 - 0
src/views/tagging-position/sign-item.vue

@@ -8,6 +8,7 @@
         </template>
       </span>
       <span v-if="!notApply" @click="$emit('applyGlobal')" class="apply">应用到全部</span>
+      <slot name="append" v-else></slot>
     </div>
     <div class="item-content" v-if="$slots.default"><slot /></div>
   </div>