sign.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <template>
  2. <div
  3. v-if="posStyle"
  4. class="hot-item pc"
  5. :style="posStyle"
  6. @mouseenter="isHover = true"
  7. @mouseleave="isHover = false"
  8. :class="{ active: showContent }"
  9. >
  10. <ui-tip :tip="tagging.title" foreShow tipV="top" class="tag-tip">
  11. <img
  12. class="tag-img"
  13. :src="getResources(getFileUrl(taggingStyle.icon))"
  14. @click="iconClickHandler"
  15. v-if="taggingStyle"
  16. />
  17. </ui-tip>
  18. <div @click.stop>
  19. <UIBubble class="hot-bubble pc" :show="showContent" type="left" level="center">
  20. <h2>{{ tagging.title }}</h2>
  21. <ui-audio
  22. v-if="tagging.audio"
  23. class="audio"
  24. :src="getResources(getFileUrl(tagging.audio))"
  25. ref="audio"
  26. />
  27. <div class="content">
  28. <p><span>{{$t('tagging.tabs.typeId')}}:</span>{{ tagging.desc }}</p>
  29. <p><span>{{$t('tagging.tabs.part')}}:</span>{{ tagging.part }}</p>
  30. <p><span>{{$t('tagging.tabs.method')}}:</span>{{ tagging.method }}</p>
  31. <p><span>{{$t('tagging.tabs.principal')}}:</span>{{ tagging.principal }}</p>
  32. </div>
  33. <Images
  34. :tagging="tagging"
  35. :in-full="true"
  36. @pull="(index) => (pullIndex = index)"
  37. />
  38. <div
  39. class="edit-hot"
  40. v-if="router.currentRoute.value.name === RoutesName.tagging"
  41. >
  42. <span @click="$emit('delete')" class="fun-ctrl">
  43. <ui-icon type="del" />
  44. {{ $t('sys.del') }}
  45. </span>
  46. </div>
  47. </UIBubble>
  48. <Preview
  49. @close="pullIndex = -1"
  50. :current="pullIndex"
  51. :items="queryItems"
  52. v-if="!!~pullIndex"
  53. />
  54. </div>
  55. </div>
  56. </template>
  57. <script lang="ts" setup>
  58. import { computed, ref, watchEffect, watch, onUnmounted } from "vue";
  59. import { router, RoutesName } from "@/router";
  60. import UIBubble from "bill/components/bubble/index.vue";
  61. import Images from "@/views/tagging/images.vue";
  62. import Preview from "../static-preview/index.vue";
  63. import { getTaggingStyle, getFuseModel } from "@/store";
  64. import { getFileUrl, MetaType } from "@/utils";
  65. import { sdk } from "@/sdk";
  66. import { custom, getResource, getResources } from "@/env";
  67. import { useViewStack } from "@/hook";
  68. import type { Tagging, TaggingPosition } from "@/store";
  69. export type SignProps = { tagging: Tagging; scenePos: TaggingPosition; show?: boolean };
  70. defineEmits<{ (e: "delete"): void }>();
  71. const props = defineProps<SignProps>();
  72. const posStyle = ref<null | { left: string; top: string }>(null);
  73. const updatePosStyle = () => {
  74. const screenPos = sdk.getScreenByPosition(
  75. props.scenePos.localPos,
  76. props.scenePos.modelId
  77. );
  78. if (!screenPos?.trueSide) {
  79. posStyle.value = null;
  80. } else {
  81. posStyle.value = {
  82. left: screenPos.pos.x + "px",
  83. top: screenPos.pos.y + "px",
  84. };
  85. }
  86. };
  87. useViewStack(() => {
  88. sdk.sceneBus.on("cameraChange", updatePosStyle);
  89. return () => {
  90. sdk.sceneBus.off("cameraChange", updatePosStyle);
  91. };
  92. });
  93. watchEffect(updatePosStyle);
  94. const model = getFuseModel(props.scenePos.modelId);
  95. model && watch(model, updatePosStyle, { deep: true });
  96. const showContent = computed(() => {
  97. return (
  98. !~pullIndex.value &&
  99. (isHover.value || custom.showTaggingPositions.has(props.scenePos))
  100. );
  101. });
  102. const taggingStyle = computed(() => getTaggingStyle(props.tagging.styleId));
  103. const pullIndex = ref(-1);
  104. const isHover = ref(false);
  105. const queryItems = computed(() =>
  106. props.tagging.images.map((image) => ({
  107. type: MetaType.image,
  108. url: getResources(getFileUrl(image)),
  109. }))
  110. );
  111. const iconClickHandler = () => {
  112. if (custom.showTaggingPositions.has(props.scenePos)) {
  113. custom.showTaggingPositions.delete(props.scenePos);
  114. } else {
  115. custom.showTaggingPositions.add(props.scenePos);
  116. }
  117. };
  118. </script>
  119. <style lang="scss" scoped>
  120. .hot-item {
  121. pointer-events: all;
  122. position: absolute;
  123. transform: translate(-50%, -50%);
  124. cursor: pointer;
  125. .tag-img {
  126. width: 32px;
  127. height: 32px;
  128. }
  129. .hot-bubble {
  130. cursor: initial;
  131. &.pc {
  132. width: 400px;
  133. }
  134. &:not(.pc) {
  135. width: 80vw;
  136. --bottom-left: 40vw;
  137. }
  138. h2 {
  139. font-size: 20px;
  140. margin-bottom: 10px;
  141. color: #ffffff;
  142. position: relative;
  143. }
  144. .content {
  145. font-size: 14px;
  146. font-family: MicrosoftYaHei;
  147. color: #999999;
  148. line-height: 1.35em;
  149. margin-bottom: 20px;
  150. word-break: break-all;
  151. p {
  152. margin-bottom: 10px;
  153. }
  154. }
  155. }
  156. &.active,
  157. &:hover {
  158. z-index: 3;
  159. }
  160. }
  161. .edit-hot {
  162. margin-top: 20px;
  163. text-align: right;
  164. span {
  165. font-size: 14px;
  166. color: rgba(255, 255, 255, 0.6);
  167. cursor: pointer;
  168. }
  169. }
  170. </style>
  171. <style>
  172. .tag-tip {
  173. z-index: 8 !important;
  174. }
  175. .tag-tip p {
  176. padding: 6px 10px !important;
  177. margin: 5px 0 !important;
  178. }
  179. </style>