sign-new.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. <template>
  2. <div
  3. v-if="show && posStyle"
  4. class="hot-item pc"
  5. :style="posStyle"
  6. @mouseenter="isHover = true"
  7. @mouseleave="isHover = false"
  8. >
  9. <div @click.stop>
  10. <UIBubble
  11. class="hot-bubble pc"
  12. :show="showContent"
  13. type="left"
  14. level="center"
  15. @click.stop
  16. @pointerdown.stop
  17. @pointerup.stop
  18. >
  19. <h2>
  20. {{ tagging.title }}
  21. <ui-audio
  22. v-if="tagging.audio"
  23. class="audio"
  24. :src="getResource(getFileUrl(tagging.audio))"
  25. ref="audio"
  26. />
  27. </h2>
  28. <div class="content">
  29. <div class="p">
  30. <span v-if="defStyleType.id !== taggingStyle?.typeId"> 特征描述: </span>
  31. <div v-html="tagging.desc"></div>
  32. </div>
  33. <template v-if="defStyleType.id !== taggingStyle?.typeId">
  34. <p><span>遗留部位:</span>{{ tagging.part }}</p>
  35. <p><span>提取方法:</span>{{ tagging.method }}</p>
  36. <p><span>提取人:</span>{{ tagging.principal }}</p>
  37. </template>
  38. </div>
  39. <Images
  40. class="images"
  41. :tagging="tagging"
  42. :in-full="true"
  43. @pull="(index) => (pullIndex = index)"
  44. />
  45. <div class="edit-hot" v-if="showDelete">
  46. <span @click="$emit('delete')" class="fun-ctrl">
  47. <ui-icon type="del" />
  48. 删除
  49. </span>
  50. </div>
  51. </UIBubble>
  52. <Preview
  53. @close="pullIndex = -1"
  54. :current="pullIndex"
  55. :items="queryItems"
  56. v-if="!!~pullIndex"
  57. />
  58. </div>
  59. </div>
  60. </template>
  61. <script lang="ts" setup>
  62. import { computed, markRaw, onUnmounted, ref, watch, watchEffect } from "vue";
  63. import UIBubble from "bill/components/bubble/index.vue";
  64. import Images from "@/views/tagging/hot/images.vue";
  65. import Preview from "../static-preview/index.vue";
  66. import { getTaggingStyle } from "@/store";
  67. import { getFileUrl } from "@/utils";
  68. import { sdk, TaggingPositionNode } from "@/sdk";
  69. import { custom, getResource } from "@/env";
  70. import type { Tagging, TaggingPosition } from "@/store";
  71. import { useCameraChange, usePixel } from "@/hook/use-pixel";
  72. import { inRevise } from "bill/utils";
  73. import { defStyleType } from "@/api";
  74. export type SignProps = { tagging: Tagging; scenePos: TaggingPosition; show?: boolean };
  75. const props = defineProps<SignProps>();
  76. const emit = defineEmits<{
  77. (e: "delete"): void;
  78. (
  79. e: "changePosition",
  80. val: { position: SceneLocalPos; modelId: string; normal: SceneLocalPos }
  81. ): void;
  82. }>();
  83. const audio = ref();
  84. watchEffect(() => {
  85. audio.value && console.error("准备好了!,");
  86. if (props.show && audio.value) {
  87. audio.value.play();
  88. }
  89. });
  90. const [posStyle, pos, pause, recovery] = usePixel(() => undefined);
  91. const queryItems = computed(() =>
  92. props.tagging.images.map((image) => ({
  93. url: getResource(getFileUrl(image)),
  94. }))
  95. );
  96. const taggingStyle = computed(() => getTaggingStyle(props.tagging.styleId));
  97. const tag = markRaw(
  98. sdk.createTagging({
  99. ...props.scenePos,
  100. title: props.tagging.title,
  101. position: props.scenePos.localPos,
  102. canMove: false,
  103. image: getFileUrl(taggingStyle.value!.icon),
  104. })
  105. ) as TaggingPositionNode;
  106. const showDelete = ref(false);
  107. tag.showDelete = (show) => {
  108. showDelete.value = show;
  109. };
  110. tag.changeCanMove(false);
  111. const changePos = () => {
  112. pos.value = { localPos: tag.getImageCenter(), modelId: props.scenePos.modelId };
  113. };
  114. watch(taggingStyle, (icon) => icon && tag.changeImage(getFileUrl(icon.icon)));
  115. watchEffect(() => tag.changeMat(props.scenePos.mat));
  116. watchEffect(() => tag.changeFontSize(props.scenePos.fontSize));
  117. watchEffect(() => {
  118. tag.changeLineHeight(props.scenePos.lineHeight);
  119. changePos();
  120. });
  121. watchEffect(() => tag.changeTitle(props.tagging.title));
  122. watchEffect(() => tag.visibilityTitle(props.tagging.show3dTitle));
  123. watchEffect(() => {
  124. tag.changeType(props.scenePos.type);
  125. changePos();
  126. });
  127. const getPosition = () => ({
  128. position: props.scenePos.localPos,
  129. normal: props.scenePos.normal,
  130. modelId: props.scenePos.modelId,
  131. });
  132. let currentPosition = getPosition();
  133. let changeTimeout: any;
  134. tag.bus.on("changePosition", (data) => {
  135. console.error(data);
  136. clearTimeout(changeTimeout);
  137. emit(
  138. "changePosition",
  139. (currentPosition = {
  140. position: { ...data.pos },
  141. normal: { ...data.normal },
  142. modelId: data.modelId,
  143. })
  144. );
  145. changePos();
  146. });
  147. watch(getPosition, (p) => {
  148. changeTimeout = setTimeout(() => {
  149. if (inRevise(p, currentPosition)) {
  150. console.log("更改当前位置");
  151. tag.changePosition(p);
  152. currentPosition = p;
  153. }
  154. }, 16);
  155. });
  156. const [toCameraDistance] = useCameraChange(() => {
  157. return tag.getCameraDisSquared && tag.getCameraDisSquared();
  158. });
  159. const show = computed(
  160. () =>
  161. props.scenePos.globalVisibility ||
  162. toCameraDistance.value <= Math.pow(props.scenePos.visibilityRange, 2)
  163. );
  164. watchEffect(() => tag.visibility(show.value));
  165. const isHover = ref(false);
  166. tag.bus.on("enter", () => {
  167. isHover.value = true;
  168. });
  169. tag.bus.on("leave", () => {
  170. isHover.value = false;
  171. });
  172. tag.bus.on("click", () => iconClickHandler());
  173. const sHover = ref(isHover.value);
  174. let timeout: any;
  175. watchEffect(() => {
  176. clearTimeout(timeout);
  177. if (isHover.value) {
  178. sHover.value = true;
  179. } else {
  180. timeout = setTimeout(() => {
  181. sHover.value = false;
  182. }, 100);
  183. }
  184. });
  185. const pullIndex = ref(-1);
  186. const showContent = computed(() => {
  187. return (
  188. !~pullIndex.value && (sHover.value || custom.showTaggingPositions.has(props.scenePos))
  189. );
  190. });
  191. watchEffect(() => {
  192. if (showContent.value) {
  193. recovery();
  194. } else {
  195. pause();
  196. }
  197. });
  198. const iconClickHandler = () => {
  199. if (custom.showTaggingPositions.has(props.scenePos)) {
  200. custom.showTaggingPositions.delete(props.scenePos);
  201. } else {
  202. custom.showTaggingPositions.add(props.scenePos);
  203. }
  204. };
  205. onUnmounted(() => {
  206. tag.destory();
  207. });
  208. defineExpose(tag);
  209. </script>
  210. <style lang="scss" scoped>
  211. .hot-item {
  212. pointer-events: all;
  213. position: absolute;
  214. transform: translate(-50%, -100%);
  215. cursor: pointer;
  216. .tag-img {
  217. width: 32px;
  218. height: 32px;
  219. }
  220. .hot-bubble {
  221. cursor: initial;
  222. &.pc {
  223. width: 400px;
  224. }
  225. &:not(.pc) {
  226. width: 80vw;
  227. --bottom-left: 40vw;
  228. }
  229. h2 {
  230. font-size: 20px;
  231. margin-bottom: 10px;
  232. color: #ffffff;
  233. position: relative;
  234. display: flex;
  235. justify-content: space-between;
  236. }
  237. .content {
  238. font-size: 14px;
  239. font-family: MicrosoftYaHei;
  240. color: #999999;
  241. line-height: 1.35em;
  242. margin-bottom: 20px;
  243. word-break: break-all;
  244. .p,
  245. p {
  246. margin-bottom: 10px;
  247. display: flex;
  248. span {
  249. flex: 0 0 auto;
  250. }
  251. }
  252. }
  253. }
  254. &.active,
  255. &:hover {
  256. z-index: 3;
  257. }
  258. }
  259. .edit-hot {
  260. margin-top: 20px;
  261. text-align: right;
  262. span {
  263. font-size: 14px;
  264. color: rgba(255, 255, 255, 0.6);
  265. cursor: pointer;
  266. }
  267. }
  268. .images {
  269. height: 250px;
  270. }
  271. </style>
  272. <style>
  273. .tag-tip {
  274. z-index: 8 !important;
  275. }
  276. .tag-tip p {
  277. padding: 6px 10px !important;
  278. margin: 5px 0 !important;
  279. }
  280. </style>