sign.vue 4.4 KB

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