sign-new.vue 8.0 KB

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