index.vue 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. <template>
  2. <Modal
  3. v-model:open="show"
  4. :width="width"
  5. :wrap-style="{ overflow: 'hidden' }"
  6. :destroyOnClose="true"
  7. :maskClosable="false"
  8. >
  9. <template #title>
  10. <div ref="modalTitleRef" style="width: 100%; cursor: move">{{ title }}</div>
  11. </template>
  12. <template #modalRender="{ originVNode }">
  13. <div :style="transformStyle">
  14. <component :is="originVNode" />
  15. </div>
  16. </template>
  17. <Editor v-model:code="code" v-bind="$attrs" :language="language"/>
  18. <template #footer>
  19. <slot name="footer">
  20. <Button @click="close">取消</Button>
  21. <Button type="primary" @click="handleOk">确定</Button>
  22. </slot>
  23. </template>
  24. </Modal>
  25. </template>
  26. <script lang="ts">
  27. import { Modal, Button } from "ant-design-vue";
  28. import { ref, defineComponent, watch, watchEffect, computed, CSSProperties } from "vue";
  29. import Editor, { Language } from "./Editor.vue";
  30. import { useDraggable } from '@vueuse/core';
  31. export default defineComponent({
  32. name: "DEditorModal",
  33. components: {
  34. Modal,
  35. Editor,
  36. Button
  37. },
  38. props: {
  39. title: {
  40. type: String,
  41. default: "编辑",
  42. },
  43. width: {
  44. type: Number,
  45. default: 800,
  46. },
  47. },
  48. emits: ["ok"],
  49. setup(_, { emit }) {
  50. const show = ref(false);
  51. const code = ref("");
  52. const language = ref<Language>();
  53. const modalTitleRef = ref<HTMLElement>();
  54. const { x, y, isDragging } = useDraggable(modalTitleRef);
  55. const startX = ref<number>(0);
  56. const startY = ref<number>(0);
  57. const startedDrag = ref(false);
  58. const transformX = ref(0);
  59. const transformY = ref(0);
  60. const preTransformX = ref(0);
  61. const preTransformY = ref(0);
  62. const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 });
  63. watch([x, y], () => {
  64. if (!startedDrag.value) {
  65. startX.value = x.value;
  66. startY.value = y.value;
  67. const bodyRect = document.body.getBoundingClientRect();
  68. const titleRect = modalTitleRef.value?.getBoundingClientRect() || { width: 0, height: 0 };
  69. dragRect.value.right = bodyRect.width - titleRect.width;
  70. dragRect.value.bottom = bodyRect.height - titleRect.height;
  71. preTransformX.value = transformX.value;
  72. preTransformY.value = transformY.value;
  73. }
  74. startedDrag.value = true;
  75. });
  76. watch(isDragging, () => {
  77. if (!isDragging) {
  78. startedDrag.value = false;
  79. }
  80. });
  81. watchEffect(() => {
  82. if (startedDrag.value) {
  83. transformX.value =
  84. preTransformX.value +
  85. Math.min(Math.max(dragRect.value.left, x.value), dragRect.value.right) -
  86. startX.value;
  87. transformY.value =
  88. preTransformY.value +
  89. Math.min(Math.max(dragRect.value.top, y.value), dragRect.value.bottom) -
  90. startY.value;
  91. }
  92. });
  93. const transformStyle = computed<CSSProperties>(() => {
  94. return {
  95. transform: `translate(${transformX.value}px, ${transformY.value}px)`,
  96. };
  97. });
  98. const handleOk = () => {
  99. // TODO: 检验code
  100. emit("ok", code.value);
  101. show.value = false;
  102. };
  103. const open = (codeStr: string, lang: Language = 'javascript') => {
  104. show.value = true;
  105. code.value = codeStr;
  106. language.value = lang;
  107. };
  108. const close = () => {
  109. show.value = false;
  110. };
  111. return {
  112. open,
  113. close,
  114. code,
  115. handleOk,
  116. show,
  117. language,
  118. modalTitleRef,
  119. transformStyle
  120. };
  121. },
  122. });
  123. </script>
  124. <style scoped></style>