123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- <template>
- <div
- v-if="show && posStyle"
- class="hot-item pc"
- :style="posStyle"
- @mouseenter="isHover = true"
- @mouseleave="isHover = false"
- >
- <div @click.stop>
- <UIBubble
- class="hot-bubble pc"
- :show="showContent"
- type="left"
- level="center"
- @click.stop
- @pointerdown.stop
- @pointerup.stop
- >
- <h2>
- {{ tagging.title }}
- <ui-audio
- v-if="tagging.audio"
- class="audio"
- :src="getResource(getFileUrl(tagging.audio))"
- ref="audio"
- />
- </h2>
- <div class="content">
- <div class="p">
- <span v-if="defStyleType.id !== taggingStyle?.typeId"> 特征描述: </span>
- <div v-html="tagging.desc"></div>
- </div>
- <template v-if="defStyleType.id !== taggingStyle?.typeId">
- <p><span>遗留部位:</span>{{ tagging.part }}</p>
- <p><span>提取方法:</span>{{ tagging.method }}</p>
- <p><span>提取人:</span>{{ tagging.principal }}</p>
- </template>
- </div>
- <Images
- class="images"
- :tagging="tagging"
- :in-full="true"
- @pull="(index) => (pullIndex = index)"
- />
- <div class="edit-hot" v-if="showDelete">
- <span @click="$emit('delete')" class="fun-ctrl">
- <ui-icon type="del" />
- 删除
- </span>
- </div>
- </UIBubble>
- <Preview
- @close="pullIndex = -1"
- :current="pullIndex"
- :items="queryItems"
- v-if="!!~pullIndex"
- />
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- import { computed, markRaw, onUnmounted, ref, watch, watchEffect } from "vue";
- import UIBubble from "bill/components/bubble/index.vue";
- import Images from "@/views/tagging/hot/images.vue";
- import Preview from "../static-preview/index.vue";
- import { getTaggingStyle } from "@/store";
- import { getFileUrl } from "@/utils";
- import { sdk, TaggingPositionNode } from "@/sdk";
- import { custom, getResource } from "@/env";
- import type { Tagging, TaggingPosition } from "@/store";
- import { useCameraChange, usePixel } from "@/hook/use-pixel";
- import { inRevise } from "bill/utils";
- import { defStyleType } from "@/api";
- export type SignProps = { tagging: Tagging; scenePos: TaggingPosition; show?: boolean };
- const props = defineProps<SignProps>();
- const emit = defineEmits<{
- (e: "delete"): void;
- (
- e: "changePosition",
- val: { position: SceneLocalPos; modelId: string; normal: SceneLocalPos }
- ): void;
- }>();
- const audio = ref();
- watchEffect(() => {
- audio.value && console.error("准备好了!,");
- if (props.show && audio.value) {
- audio.value.play();
- }
- });
- const [posStyle, pos, pause, recovery] = usePixel(() => undefined);
- const queryItems = computed(() =>
- props.tagging.images.map((image) => ({
- url: getResource(getFileUrl(image)),
- }))
- );
- const taggingStyle = computed(() => getTaggingStyle(props.tagging.styleId));
- const tag = markRaw(
- sdk.createTagging({
- ...props.scenePos,
- title: props.tagging.title,
- position: props.scenePos.localPos,
- canMove: false,
- image: getFileUrl(taggingStyle.value!.icon),
- })
- ) as TaggingPositionNode;
- const showDelete = ref(false);
- tag.showDelete = (show) => {
- showDelete.value = show;
- };
- tag.changeCanMove(false);
- const changePos = () => {
- pos.value = { localPos: tag.getImageCenter(), modelId: props.scenePos.modelId };
- };
- watch(taggingStyle, (icon) => icon && tag.changeImage(getFileUrl(icon.icon)));
- watchEffect(() => tag.changeMat(props.scenePos.mat));
- watchEffect(() => tag.changeFontSize(props.scenePos.fontSize));
- watchEffect(() => {
- tag.changeLineHeight(props.scenePos.lineHeight);
- changePos();
- });
- watchEffect(() => tag.changeTitle(props.tagging.title));
- watchEffect(() => tag.visibilityTitle(props.tagging.show3dTitle));
- watchEffect(() => {
- tag.changeType(props.scenePos.type);
- changePos();
- });
- const getPosition = () => ({
- position: props.scenePos.localPos,
- normal: props.scenePos.normal,
- modelId: props.scenePos.modelId,
- });
- let currentPosition = getPosition();
- let changeTimeout: any;
- tag.bus.on("changePosition", (data) => {
- console.error(data);
- clearTimeout(changeTimeout);
- emit(
- "changePosition",
- (currentPosition = {
- position: { ...data.pos },
- normal: { ...data.normal },
- modelId: data.modelId,
- })
- );
- changePos();
- });
- watch(getPosition, (p) => {
- changeTimeout = setTimeout(() => {
- if (inRevise(p, currentPosition)) {
- console.log("更改当前位置");
- tag.changePosition(p);
- currentPosition = p;
- }
- }, 16);
- });
- const [toCameraDistance] = useCameraChange(() => {
- return tag.getCameraDisSquared && tag.getCameraDisSquared();
- });
- const show = computed(
- () =>
- props.scenePos.globalVisibility ||
- toCameraDistance.value <= Math.pow(props.scenePos.visibilityRange, 2)
- );
- watchEffect(() => tag.visibility(show.value));
- const isHover = ref(false);
- tag.bus.on("enter", () => {
- isHover.value = true;
- });
- tag.bus.on("leave", () => {
- isHover.value = false;
- });
- tag.bus.on("click", () => iconClickHandler());
- const sHover = ref(isHover.value);
- let timeout: any;
- watchEffect(() => {
- clearTimeout(timeout);
- if (isHover.value) {
- sHover.value = true;
- } else {
- timeout = setTimeout(() => {
- sHover.value = false;
- }, 100);
- }
- });
- const pullIndex = ref(-1);
- const showContent = computed(() => {
- return (
- !~pullIndex.value && (sHover.value || custom.showTaggingPositions.has(props.scenePos))
- );
- });
- watchEffect(() => {
- if (showContent.value) {
- recovery();
- } else {
- pause();
- }
- });
- const iconClickHandler = () => {
- if (custom.showTaggingPositions.has(props.scenePos)) {
- custom.showTaggingPositions.delete(props.scenePos);
- } else {
- custom.showTaggingPositions.add(props.scenePos);
- }
- };
- onUnmounted(() => {
- tag.destory();
- });
- defineExpose(tag);
- </script>
- <style lang="scss" scoped>
- .hot-item {
- pointer-events: all;
- position: absolute;
- transform: translate(-50%, -100%);
- cursor: pointer;
- .tag-img {
- width: 32px;
- height: 32px;
- }
- .hot-bubble {
- cursor: initial;
- &.pc {
- width: 400px;
- }
- &:not(.pc) {
- width: 80vw;
- --bottom-left: 40vw;
- }
- h2 {
- font-size: 20px;
- margin-bottom: 10px;
- color: #ffffff;
- position: relative;
- display: flex;
- justify-content: space-between;
- }
- .content {
- font-size: 14px;
- font-family: MicrosoftYaHei;
- color: #999999;
- line-height: 1.35em;
- margin-bottom: 20px;
- word-break: break-all;
- .p,
- p {
- margin-bottom: 10px;
- display: flex;
- span {
- flex: 0 0 auto;
- }
- }
- }
- }
- &.active,
- &:hover {
- z-index: 3;
- }
- }
- .edit-hot {
- margin-top: 20px;
- text-align: right;
- span {
- font-size: 14px;
- color: rgba(255, 255, 255, 0.6);
- cursor: pointer;
- }
- }
- .images {
- height: 250px;
- }
- </style>
- <style>
- .tag-tip {
- z-index: 8 !important;
- }
- .tag-tip p {
- padding: 6px 10px !important;
- margin: 5px 0 !important;
- }
- </style>
|