sign.vue 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. <template>
  2. <ui-group-option
  3. class="sign-tagging"
  4. :class="{ active: selected, edit }"
  5. @click="edit && getTaggingIsShow(tagging) && emit('select', true)"
  6. >
  7. <div class="info">
  8. <img :src="getResource(getFileUrl(findImage))" v-if="findImage" />
  9. <div>
  10. <p>{{ tagging.title }}</p>
  11. <span>放置:{{ positions.length }}</span>
  12. </div>
  13. </div>
  14. <div class="actions" @click.stop>
  15. <ui-icon
  16. v-if="!edit"
  17. type="pin"
  18. ctrl
  19. @click.stop="$emit('select', true)"
  20. :class="{ disabled: !getTaggingIsShow(tagging) }"
  21. />
  22. <template v-else>
  23. <ui-icon type="pin1" ctrl @click.stop="$emit('fixed')" tip="放置" />
  24. <ui-more
  25. :options="menus"
  26. style="margin-left: 20px"
  27. @click="(action: keyof typeof actions) => actions[action]()"
  28. />
  29. </template>
  30. </div>
  31. </ui-group-option>
  32. </template>
  33. <script setup lang="ts">
  34. import { getFileUrl, getUrlType, MetaType } from "@/utils";
  35. import { computed, ref, watchEffect, nextTick } from "vue";
  36. import { getResource, showTaggingPositionsStack } from "@/env";
  37. import { getTaggingPosNode, sdk } from "@/sdk";
  38. import {
  39. getTaggingStyle,
  40. getTaggingPositions,
  41. getFuseModel,
  42. getFuseModelShowVariable,
  43. getTaggingIsShow,
  44. } from "@/store";
  45. import type { Tagging } from "@/store";
  46. const props = withDefaults(
  47. defineProps<{ tagging: Tagging; selected?: boolean; edit?: boolean }>(),
  48. { edit: true }
  49. );
  50. const style = computed(() => getTaggingStyle(props.tagging.styleId));
  51. const positions = computed(() => getTaggingPositions(props.tagging));
  52. const emit = defineEmits<{
  53. (e: "delete"): void;
  54. (e: "edit"): void;
  55. (e: "select", selected: boolean): void;
  56. (e: "fixed"): void;
  57. }>();
  58. const findImage = computed(() => {
  59. let img = props.tagging.images.find(
  60. (a) => getUrlType(getResource(getFileUrl(a))) === MetaType.image
  61. );
  62. if (!img) {
  63. return getTaggingStyle(props.tagging.styleId)?.icon;
  64. } else {
  65. return img;
  66. }
  67. });
  68. const menus = [
  69. { label: "编辑", value: "edit" },
  70. { label: "删除", value: "delete" },
  71. ];
  72. const actions = {
  73. edit: () => emit("edit"),
  74. delete: () => emit("delete"),
  75. };
  76. const flyTaggingPositions = (tagging: Tagging, callback?: () => void) => {
  77. const positions = getTaggingPositions(tagging);
  78. let isStop = false;
  79. const flyIndex = (i: number) => {
  80. if (isStop || i >= positions.length) {
  81. callback && nextTick(callback);
  82. return;
  83. }
  84. const position = positions[i];
  85. const model = getFuseModel(position.modelId);
  86. if (!model || !getFuseModelShowVariable(model).value) {
  87. flyIndex(i + 1);
  88. return;
  89. }
  90. const pop = showTaggingPositionsStack.push(ref(new WeakSet([position])));
  91. sdk.comeTo({
  92. position: getTaggingPosNode(position)!.getImageCenter(),
  93. modelId: position.modelId,
  94. dur: 300,
  95. // distance: 3,
  96. maxDis: 15,
  97. });
  98. setTimeout(() => {
  99. pop();
  100. flyIndex(i + 1);
  101. }, 2000);
  102. };
  103. flyIndex(0);
  104. return () => (isStop = true);
  105. };
  106. watchEffect((onCleanup) => {
  107. if (props.selected) {
  108. const success = () => emit("select", false);
  109. const stop = flyTaggingPositions(props.tagging, success);
  110. const keyupHandler = (ev: KeyboardEvent) => ev.code === "Escape" && success();
  111. document.documentElement.addEventListener("keyup", keyupHandler, false);
  112. onCleanup(() => {
  113. stop();
  114. document.documentElement.removeEventListener("keyup", keyupHandler, false);
  115. });
  116. }
  117. });
  118. </script>
  119. <style lang="scss" scoped src="./style.scss"></style>