bill преди 8 месеца
родител
ревизия
b439d7d2df

+ 3 - 3
src/app.vue

@@ -9,9 +9,9 @@
     >
       <div :ref="(el: any) => appEl = (el as HTMLDivElement)" v-if="loaded">
         <router-view v-slot="{ Component }">
-          <keep-alive>
-            <component :is="Component" />
-          </keep-alive>
+          <!-- <keep-alive> -->
+          <component :is="Component" />
+          <!-- </keep-alive> -->
         </router-view>
       </div>
     </ui-editor-layout>

+ 1 - 1
src/components/bill-ui/assets/scss/editor/_head.scss

@@ -8,5 +8,5 @@
     left: 0;
     top: 0;
     width: 100%;
-    z-index: 1000;
+    z-index: 100;
 }

+ 1 - 1
src/components/bill-ui/assets/scss/editor/_menu.scss

@@ -9,7 +9,7 @@
     left: var(--editor-menu-left);
     top: var(--editor-head-height);
     bottom: 0;
-    z-index: 2000;
+    z-index: 200;
     overflow: hidden;
 
     backdrop-filter: blur(4px);

+ 10 - 30
src/components/static-preview/sign.vue

@@ -6,43 +6,23 @@
   </div>
 </template>
 
-<script lang="ts">
-import { ref, watchEffect, defineComponent, PropType } from "vue";
+<script lang="ts" setup>
+import { ref, watchEffect } from "vue";
 import { getResource } from "@/env";
-import { MediaType } from "./index.vue";
 import ResourceView from "./resource.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);
+const props = defineProps<{ media: MediaItem }>();
+defineEmits<{ (e: "close", v: boolean): void }>();
 
-      staticURL.value = url;
-      return () => URL.revokeObjectURL(url);
-    });
+const staticURL = ref("");
+watchEffect(() => {
+  const data = props.media.url;
+  const url = typeof data === "string" ? getResource(data) : URL.createObjectURL(data);
 
-    return {
-      staticURL,
-      MediaType,
-    };
-  },
+  staticURL.value = url;
+  return () => URL.revokeObjectURL(url);
 });
-
-export default Preview;
 </script>
 
 <style scoped lang="scss" src="./style.scss"></style>

+ 3 - 1
src/components/tagging/sign-new.vue

@@ -54,6 +54,7 @@ import { custom, getResource } from "@/env";
 
 import type { Tagging, TaggingPosition } from "@/store";
 import { useCameraChange, usePixel } from "@/hook/use-pixel";
+import { useViewStack } from "@/hook";
 
 export type SignProps = { tagging: Tagging; scenePos: TaggingPosition; show?: boolean };
 
@@ -74,7 +75,6 @@ const queryItems = computed(() =>
   }))
 );
 const taggingStyle = computed(() => getTaggingStyle(props.tagging.styleId));
-
 const tag = sdk.createTagging({
   ...props.scenePos,
   title: props.tagging.title,
@@ -105,9 +105,11 @@ watchEffect(() => {
 });
 
 const [toCameraDistance] = useCameraChange(() => {
+  console.log(tag.getCameraDisSquared(), Math.pow(30, 2), props.scenePos.id);
   return tag.getCameraDisSquared && tag.getCameraDisSquared();
 });
 const maxDisSqua = Math.pow(30, 2);
+
 const show = computed(
   () => props.scenePos.globalVisibility || toCameraDistance.value <= maxDisSqua
 );

+ 3 - 3
src/layout/edit/fuse-edit.vue

@@ -2,9 +2,9 @@
   <template v-if="loaded" style="height: 100%">
     <Header></Header>
     <router-view v-slot="{ Component }" v-if="fuseModels.length">
-      <keep-alive>
-        <component :is="Component" />
-      </keep-alive>
+      <!-- <keep-alive> -->
+      <component :is="Component" />
+      <!-- </keep-alive> -->
     </router-view>
 
     <SelectModel v-else>

+ 3 - 3
src/layout/edit/fuse-switch.vue

@@ -4,9 +4,9 @@
   <ModelList />
 
   <router-view v-slot="{ Component }">
-    <keep-alive>
-      <component :is="Component" />
-    </keep-alive>
+    <!-- <keep-alive> -->
+    <component :is="Component" />
+    <!-- </keep-alive> -->
   </router-view>
 </template>
 

+ 3 - 3
src/layout/edit/scene-edit.vue

@@ -6,9 +6,9 @@
     </LeftPano>
 
     <router-view v-slot="{ Component }">
-      <keep-alive>
-        <component :is="Component" />
-      </keep-alive>
+      <!-- <keep-alive> -->
+      <component :is="Component" />
+      <!-- </keep-alive> -->
     </router-view>
   </template>
 </template>

+ 12 - 11
src/layout/left-pano.vue

@@ -1,10 +1,11 @@
 <template>
   <div id="left-pano">
-    <span 
+    <span
       class="ctrl-pano-c fun-ctrl strengthen-right strengthen-top strengthen-bottom"
-      v-if="custom.viewMode !== 'full' && custom.showLeftCtrlPano" 
+      v-if="custom.viewMode !== 'full' && custom.showLeftCtrlPano"
       @click="custom.showLeftPano = !custom.showLeftPano"
-      :class="{ active: custom.showLeftPano }">
+      :class="{ active: custom.showLeftPano }"
+    >
       <ui-icon type="extend" class="icon"></ui-icon>
     </span>
     <div class="left-pano strengthen">
@@ -14,21 +15,21 @@
 </template>
 
 <script lang="ts" setup>
-import { custom } from '@/env'
+import { custom } from "@/env";
 </script>
 
 <style lang="scss" scoped>
 .left-pano {
   position: absolute;
   width: var(--left-pano-width);
-  background: rgba(27,27,28,0.8);
+  background: rgba(27, 27, 28, 0.8);
   top: calc(var(--editor-head-height) + var(--header-top));
   left: var(--left-pano-left);
   bottom: var(--editor-menu-bottom);
-  z-index: 1000;
+  z-index: 100;
   backdrop-filter: blur(4px);
   overflow-y: auto;
-  transition: all .3s ease;
+  transition: all 0.3s ease;
 }
 
 .ctrl-pano-c {
@@ -47,7 +48,7 @@ import { custom } from '@/env'
   color: rgba(255, 255, 255, 0.6);
   font-size: 14px;
   cursor: pointer;
-  transition: inset .3s ease, color .3s ease;
+  transition: inset 0.3s ease, color 0.3s ease;
 
   &:hover {
     color: rgba(255, 255, 255, 1);
@@ -59,8 +60,8 @@ import { custom } from '@/env'
 
   .icon {
     display: inline-block;
-    transition: transform .3s ease;
-    transform: rotate(0)
+    transition: transform 0.3s ease;
+    transform: rotate(0);
   }
   &.active {
     .icon {
@@ -68,4 +69,4 @@ import { custom } from '@/env'
     }
   }
 }
-</style>
+</style>

+ 84 - 0
src/utils/event.ts

@@ -0,0 +1,84 @@
+
+
+export const getOffset = (ev: MouseEvent | TouchEvent, dom = ev.target! as HTMLElement, ndx = 0) => {
+	const event = ev instanceof TouchEvent ? ev.changedTouches[ndx] : ev
+	const rect = dom.getBoundingClientRect();
+	const offsetX = event.clientX - rect.left;
+	const offsetY = event.clientY - rect.top;
+	return {
+		x: offsetX,
+		y: offsetY,
+	};
+};
+
+
+export type Pos = { x: number; y: number };
+type DragProps = {
+	move?: (info: Record<'start' | 'prev' | 'end', Pos> & {ev: PointerEvent}) => void,
+	down?: (pos: Pos, ev: PointerEvent) => void,
+	up?: (pos: Pos, ev: PointerEvent) => void,
+}
+export const dragListener = (dom: HTMLElement, props: DragProps | DragProps['move'] = {}) => {
+	if (typeof props === 'function') {
+		props = { move: props }
+	}
+	const { move, up, down } = props
+	const mount = document.documentElement
+
+	if (!move && !up && !down) return () => {}
+
+	let moveHandler: any, endHandler: any
+	const downHandler = (ev: PointerEvent) => {
+		const start = getOffset(ev, dom)
+		let prev = start
+		down && down(start, ev)
+		// ev.preventDefault();
+
+		moveHandler = (ev: PointerEvent) => {
+			const end = getOffset(ev, dom)
+			move!({start, end, prev, ev})
+			prev = end
+
+			// ev.preventDefault();
+		}
+		endHandler = (ev: PointerEvent) => {
+			up && up(getOffset(ev, dom), ev)
+			mount.removeEventListener('pointermove', moveHandler);
+			mount.removeEventListener('pointerup', endHandler);
+			// ev.preventDefault();
+		}
+	
+		
+		move && mount.addEventListener('pointermove', moveHandler, { passive: false })
+		mount.addEventListener('pointerup', endHandler, { passive: false })
+	}
+
+	dom.addEventListener('pointerdown', downHandler, { passive: false });
+	return () => {
+		dom.removeEventListener('pointerdown', downHandler);
+		moveHandler && mount.removeEventListener('pointermove', moveHandler);
+		endHandler && mount.removeEventListener('pointerup', endHandler);
+	}
+}
+
+
+
+export const clickListener = (dom: HTMLDivElement, callback: (position: Pos, ev: PointerEvent) => void) => {
+	let downTime = 0;
+	let move = false
+	return dragListener(dom, {
+		down(_, ev) {
+			if (ev.button !== 0) return;
+			downTime = Date.now();
+		},
+		up(position, ev) {
+			const prevMove = move
+			move = false
+			if (prevMove || !downTime) return;
+			if (Date.now() - downTime <= 300) {
+				callback(position, ev)
+			}
+			downTime = 0
+		},
+	});
+};

+ 44 - 21
src/views/tagging-position/index.vue

@@ -5,12 +5,25 @@
       <PositionSign
         v-for="(position, i) in positions"
         :key="position.id"
+        :class="{ disabled: unKeepAdding && position.id !== showId }"
         :position="position"
         :title="`位置${i + 1}`"
         @applyGlobal="(keys) => applyGlobal(position, keys)"
         @delete="deletePosition(position)"
       />
     </Collapse>
+    <Teleport to="#layout-app">
+      <span
+        @click="unKeepAdding ? unKeepAdding() : keepAdding()"
+        class="pin-position strengthen fun-ctrl"
+      >
+        <ui-icon
+          :style="{ color: unKeepAdding ? 'var(--color-main-normal)' : 'currentColor' }"
+          type="pin1"
+          size="22px"
+        />
+      </span>
+    </Teleport>
   </RightFillPano>
 </template>
 
@@ -21,7 +34,7 @@ import { Dialog, Message } from "bill/index";
 import { RightFillPano } from "@/layout";
 import { asyncTimeout } from "@/utils";
 import { useViewStack } from "@/hook";
-import { computed, nextTick, onUnmounted, ref, watch, watchEffect } from "vue";
+import { computed, nextTick, onUnmounted, ref, shallowRef, watch } from "vue";
 import { sdk } from "@/sdk";
 import { showTaggingPositionsStack } from "@/env";
 import {
@@ -34,10 +47,10 @@ import {
   getTagging,
   enterEdit,
 } from "@/store";
-import { Collapse, CollapsePanel } from "ant-design-vue";
+import { Collapse } from "ant-design-vue";
 
 import type { TaggingPosition } from "@/store";
-import { distance } from "@/utils/math";
+import { clickListener } from "@/utils/event";
 
 const showId = ref<TaggingPosition["id"]>();
 const tagging = computed(() => getTagging(router.currentRoute.value.params.id as string));
@@ -63,7 +76,6 @@ const flyTaggingPosition = (position: TaggingPosition) => {
     dur: 300,
     distance: 3,
   });
-  // setTimeout(pop, 2000);
 };
 onUnmounted(() => pop && pop());
 
@@ -91,19 +103,16 @@ const applyGlobal = async (position: TaggingPosition, keys: string | string[]) =
   }
 };
 
-// const cameraPos = ref<SceneLocalPos>();
-// sdk.sceneBus.on("cameraChange", (pos) => (cameraPos.value = pos));
-
-let unKeepAdding: () => void;
+let unKeepAdding = shallowRef<() => void>();
 const keepAdding = () => {
+  unKeepAdding.value && unKeepAdding.value();
   const hide = Message.show({ msg: "请在模型上单击选择标签位置", type: "warning" });
-  const clickHandler = async (ev: MouseEvent) => {
+  showId.value = void 0;
+
+  const removeListener = clickListener(sdk.layout, async (pos) => {
     await nextTick();
     await asyncTimeout();
-    const position = sdk.getPositionByScreen({
-      x: ev.clientX,
-      y: ev.clientY,
-    });
+    const position = sdk.getPositionByScreen(pos);
 
     if (!position) {
       Message.error("当前位置无法添加");
@@ -113,22 +122,22 @@ const keepAdding = () => {
         taggingId: tagging.value!.id,
       });
       taggingPositions.value.push(storePosition);
-
       showId.value = storePosition.id;
-      // if (cameraPos.value && distance(cameraPos.value, position.worldPos) > 8) {
-      //   flyTaggingPosition(storePosition);
-      // }
     }
-  };
-  sdk.layout.addEventListener("click", clickHandler, false);
+  });
 
-  unKeepAdding = () => {
+  unKeepAdding.value = () => {
     hide();
-    sdk.layout.removeEventListener("click", clickHandler, false);
+    removeListener();
+    unKeepAdding.value = void 0;
   };
 };
+onUnmounted(() => unKeepAdding.value && unKeepAdding.value());
 
 useViewStack(autoSaveTaggings);
+useViewStack(() => {
+  enterEdit(() => router.back());
+});
 </script>
 
 <style lang="scss" scoped>
@@ -139,6 +148,20 @@ h3 {
   color: #999999;
   margin-bottom: 4px;
 }
+.pin-position {
+  position: absolute;
+  left: 50%;
+  transform: translate(-50%);
+  width: 64px;
+  height: 64px;
+  background: rgba(27, 27, 28, 0.8);
+  border-radius: 50%;
+  bottom: 20px;
+  z-index: 9;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
 </style>
 <style lang="scss">
 .position-group .group-title {

+ 3 - 4
src/views/tagging-position/sign.vue

@@ -63,12 +63,11 @@
             :modelValue="!!position.globalVisibility"
             @update:modelValue="(v: boolean) => position.globalVisibility = v"
           />
-          {{ position.globalVisibility }}
         </template>
       </SignItem>
-      <Button block type="primary" danger ghost size="large" @click="$emit('delete')"
-        >删除</Button
-      >
+      <Button block type="primary" danger ghost size="large" @click="$emit('delete')">
+        删除
+      </Button>
     </div>
   </CollapsePanel>
 </template>

+ 1 - 1
src/views/tagging/edit.vue

@@ -316,7 +316,7 @@ const imageSelect = async () => {
   inset: 0;
   background: rgba(0, 0, 0, 0.3);
   backdrop-filter: blur(4px);
-  z-index: 2;
+  z-index: 201;
   padding: 20px;
   overflow-y: auto;
 }

+ 28 - 15
src/views/tagging/style-type-select.vue

@@ -1,8 +1,9 @@
 <template>
   <!-- <Menu style="width: 256px" mode="vertical" :items="getItems()" @click="handleClick" /> -->
   <Dropdown placement="bottom">
-    <span
-      >{{ current.title }}<span class="count">({{ current.count }})</span>
+    <span>
+      {{ current.title }}
+      <span class="count" v-if="count">({{ current.count }})</span>
       <DownOutlined
     /></span>
     <template #overlay>
@@ -18,8 +19,8 @@
 </template>
 
 <script lang="ts" setup>
-import { getStyleTypeName, styleTypes } from "@/api";
-import { computed, reactive, ref, shallowReactive, watchEffect } from "vue";
+import { styleTypes } from "@/api";
+import { computed } from "vue";
 import { Menu, Dropdown } from "ant-design-vue";
 import { DownOutlined } from "@ant-design/icons-vue";
 import { taggings, getTaggingStyle } from "@/store";
@@ -27,20 +28,33 @@ import { taggings, getTaggingStyle } from "@/store";
 const props = defineProps<{ value: number; all?: boolean; count?: boolean }>();
 const emit = defineEmits<{ (e: "update:value", v: number): void }>();
 const allType = { name: "全部", id: -1 };
+const getTypeCount = (item: any) => {
+  if (item.id === allType.id) {
+    return taggings.value.length;
+  } else if (item.children) {
+    return item.children.reduceRight((c: number, item: any) => {
+      return (
+        c +
+        taggings.value.filter((tag) => {
+          return getTaggingStyle(tag.styleId)?.typeId === item.id;
+        }).length
+      );
+    }, 0);
+  } else {
+    return taggings.value.filter((tag) => {
+      return getTaggingStyle(tag.styleId)?.typeId === item.id;
+    }).length;
+  }
+};
+
 const getItems = (types = styleTypes): any => {
   return types.map((item) => {
     let count = 0;
     if (props.count) {
-      if (item.id === allType.id) {
-        count = taggings.value.length;
-      } else {
-        count = taggings.value.filter((tag) => {
-          return getTaggingStyle(tag.styleId)?.typeId === item.id;
-        }).length;
-      }
+      count = getTypeCount(item);
     }
     return {
-      label: item.name + `(${count})`,
+      label: item.name + (props.count ? ` (${count}) ` : ""),
       title: item.name,
       count,
       key: item.id,
@@ -48,12 +62,12 @@ const getItems = (types = styleTypes): any => {
     };
   });
 };
-const getCurrentItem = (type: number, all = items.value) => {
+const getCurrentItem = (type: number, all = items.value): any => {
   for (const item of all) {
     if (type === item.key) {
       return item;
     } else if ("children" in item && item.children) {
-      const citem = getStyleTypeName(type, item.children);
+      const citem = getCurrentItem(type, item.children);
       if (citem) {
         return citem;
       }
@@ -68,7 +82,6 @@ const types = computed(() => {
   }
 });
 const items = computed(() => getItems(types.value));
-const text = computed(() => getStyleTypeName(props.value, types.value));
 const current = computed(() => getCurrentItem(props.value));
 
 const handleClick = (info: any) => {