mount.vue 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <template>
  2. <Teleport :to="`#${DomOutMountId}`" v-if="stage">
  3. <transition name="mount-fade">
  4. <div class="mount-layout" v-if="show">
  5. <div :size="8" class="mount-controller">
  6. <div v-for="[key, val] in describeItems" :key="key" class="mount-item">
  7. <span class="label">{{ val.label }}</span>
  8. <component
  9. v-bind="describes[key].props"
  10. :value="
  11. 'value' in describes[key] ? describes[key].value : data && data[key]
  12. "
  13. @update:value="(val: any) => updateValue(key, val)"
  14. @change="changeHandler"
  15. :is="propertyComponents[val.type]"
  16. :key="key"
  17. />
  18. </div>
  19. </div>
  20. </div>
  21. </transition>
  22. </Teleport>
  23. </template>
  24. <script lang="ts" setup>
  25. import { computed, ref, watch } from "vue";
  26. import { useStage } from "../hook/use-global-vars.ts";
  27. import { useMode } from "../hook/use-status.ts";
  28. import { PropertyDescribes, propertyComponents } from "./index.ts";
  29. import { DC, EntityShape } from "@/deconstruction.js";
  30. import { useMouseShapeStatus } from "../hook/use-mouse-status.ts";
  31. import { DomOutMountId } from "@/constant/index.ts";
  32. import { Mode } from "@/constant/mode.ts";
  33. import { debounce } from "@/utils/shared.ts";
  34. const props = defineProps<{
  35. show?: boolean;
  36. target: DC<EntityShape> | undefined;
  37. data?: Record<string, any>;
  38. describes: PropertyDescribes;
  39. }>();
  40. const emit = defineEmits<{ (e: "change"): void }>();
  41. const describeItems = computed(() => {
  42. return Object.entries(props.describes).sort(
  43. ([_a, a], [_b, b]) => (b.sort || -1) - (a.sort || -1)
  44. );
  45. });
  46. const stage = useStage();
  47. const status = useMouseShapeStatus(computed(() => props.target));
  48. const mode = useMode();
  49. const hidden = computed(
  50. () =>
  51. !props.show &&
  52. (!stage.value?.getStage() ||
  53. !props.target?.getNode() ||
  54. !status.value.active ||
  55. mode.value.has(Mode.draw) ||
  56. mode.value.has(Mode.draging))
  57. );
  58. const show = ref(false);
  59. watch(
  60. hidden,
  61. debounce(() => {
  62. show.value = !hidden.value;
  63. }, 32)
  64. );
  65. let isUpdate = false;
  66. const updateValue = (key: string, val: any) => {
  67. if ("value" in props.describes[key]) {
  68. props.describes[key].value = val;
  69. } else {
  70. props.data![key] = val;
  71. }
  72. isUpdate = true;
  73. };
  74. watch(hidden, (nHidden, oHidden) => {
  75. if (nHidden && nHidden !== oHidden && isUpdate) {
  76. isUpdate = false;
  77. emit("change");
  78. }
  79. });
  80. const changeHandler = () => {
  81. isUpdate = false;
  82. emit("change");
  83. };
  84. </script>
  85. <style lang="scss" scoped>
  86. .mount-layout {
  87. pointer-events: all;
  88. right: 0;
  89. top: 0;
  90. bottom: 0;
  91. position: absolute;
  92. border-left: 1px solid #e4e7ed;
  93. background-color: rgba(255, 255, 255, 0.7);
  94. overflow-y: auto;
  95. padding: 6px 12px;
  96. color: #303133;
  97. width: 240px;
  98. font-size: 12px;
  99. box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
  100. overflow: hidden;
  101. .mount-controller {
  102. // display: grid;
  103. // grid-template-columns: auto 1fr;
  104. // gap: 10px 0;
  105. // align-items: center;
  106. .mount-item {
  107. margin-bottom: 15px;
  108. display: flex;
  109. flex-wrap: wrap;
  110. align-items: center;
  111. }
  112. .label {
  113. display: block;
  114. margin-right: 10px;
  115. color: #303133;
  116. &::after {
  117. content: ":";
  118. }
  119. }
  120. }
  121. }
  122. .mount-fade-enter-active,
  123. .mount-fade-leave-active {
  124. transition: transform 0.3s ease, opacity 0.3s ease;
  125. }
  126. .mount-fade-enter-from,
  127. .mount-fade-leave-to {
  128. transform: translateX(100%);
  129. opacity: 0;
  130. }
  131. </style>