QmDialog.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <template>
  2. <teleport to="body">
  3. <div
  4. class="dialog-container"
  5. :style="positionStyle"
  6. @click="increaseZIndex"
  7. >
  8. <header
  9. ref="mouseHandler"
  10. class="tw-flex tw-place-content-between"
  11. @mousedown="handleDragMouseDown"
  12. >
  13. <div class="tw-text-2xl tw-cursor-move tw-flex-grow">{{ title }}</div>
  14. <a-button shape="circle" @click="$emit('close')">
  15. <template #icon><CloseOutlined /></template>
  16. </a-button>
  17. </header>
  18. <div class="tw-m-1 tw-overflow-scroll" style="height: calc(100% - 60px)">
  19. <slot></slot>
  20. </div>
  21. <div
  22. ref="resizeHandler"
  23. class="resize-handler"
  24. @mousedown="handleResizeMouseDown"
  25. ></div>
  26. </div>
  27. </teleport>
  28. </template>
  29. <script lang="ts">
  30. import { defineComponent, onUnmounted, reactive, ref } from "vue";
  31. import { CloseOutlined } from "@ant-design/icons-vue";
  32. import { store } from "@/features/mark/store";
  33. export default defineComponent({
  34. name: "QmDialog",
  35. components: { CloseOutlined },
  36. props: {
  37. title: { type: String, default: "" },
  38. top: { type: String, default: "10%" },
  39. width: { type: String, default: "30%" },
  40. height: { type: String, default: "30%" },
  41. zIndex: { type: Number, default: 1000 },
  42. },
  43. emits: ["close"],
  44. setup({ top, width, height, title, zIndex }) {
  45. const positionStyle = reactive({
  46. top,
  47. left: "10%",
  48. width,
  49. height,
  50. zIndex,
  51. });
  52. const savedStyle = JSON.parse(
  53. sessionStorage.getItem("dialog-" + title) ?? "{}"
  54. );
  55. if (savedStyle?.top) positionStyle.top = savedStyle?.top;
  56. if (savedStyle?.left) positionStyle.left = savedStyle?.left;
  57. if (savedStyle?.width) positionStyle.width = savedStyle?.width;
  58. if (savedStyle?.height) positionStyle.height = savedStyle?.height;
  59. const mouseHandler = ref((null as unknown) as HTMLHeadElement);
  60. const resizeHandler = ref((null as unknown) as HTMLDivElement);
  61. const mousePosition = {
  62. offsetX: 0,
  63. offsetY: 0,
  64. };
  65. const handleDragMouseDown = (e: MouseEvent) => {
  66. mouseHandler.value.addEventListener("mousemove", handleDragMouseMove);
  67. mouseHandler.value.addEventListener("mouseup", handleDragMouseUp);
  68. // console.log(e);
  69. const { offsetX, offsetY } = e;
  70. mousePosition.offsetX = offsetX;
  71. mousePosition.offsetY = offsetY;
  72. };
  73. const handleDragMouseMove = (e: MouseEvent) => {
  74. const { clientX, clientY } = e;
  75. const windowWidth = window.innerWidth;
  76. const windowHeight = window.innerHeight;
  77. const newXRatio = (clientX - mousePosition.offsetX) / windowWidth;
  78. const newYRatio = (clientY - mousePosition.offsetY) / windowHeight;
  79. // console.log({
  80. // offsetX: mousePosition.offsetX,
  81. // offsetY: mousePosition.offsetY,
  82. // newYRatio,
  83. // newXRatio,
  84. // });
  85. positionStyle.top = newYRatio * 100 + "%";
  86. positionStyle.left = newXRatio * 100 + "%";
  87. };
  88. const handleDragMouseUp = (e: MouseEvent) => {
  89. mousePosition.offsetX = 0;
  90. mousePosition.offsetY = 0;
  91. mouseHandler.value.removeEventListener("mousemove", handleDragMouseMove);
  92. mouseHandler.value.removeEventListener("mouseup", handleDragMouseUp);
  93. };
  94. const handleResizeMouseDown = (e: MouseEvent) => {
  95. resizeHandler.value.addEventListener("mousemove", handleResizeMouseMove);
  96. resizeHandler.value.addEventListener("mouseup", handleResizeMouseUp);
  97. resizeHandler.value.addEventListener("mouseout", handleResizeMouseOut);
  98. };
  99. const handleResizeMouseMove = (e: MouseEvent) => {
  100. // console.log(e);
  101. // console.log("mouse move");
  102. // @ts-ignore
  103. const dialog = e.target.parentElement as HTMLDivElement;
  104. // console.log(dialog);
  105. const newXRatio =
  106. parseFloat(getComputedStyle(dialog).width) + e.movementX;
  107. const newYRatio =
  108. parseFloat(getComputedStyle(dialog).height) + e.movementY;
  109. positionStyle.width = newXRatio + "px";
  110. positionStyle.height = newYRatio + "px";
  111. };
  112. const handleResizeMouseUp = (e: MouseEvent) => {
  113. resizeHandler.value.removeEventListener(
  114. "mousemove",
  115. handleResizeMouseMove
  116. );
  117. resizeHandler.value.removeEventListener("mouseup", handleResizeMouseUp);
  118. resizeHandler.value.removeEventListener("mouseout", handleResizeMouseOut);
  119. };
  120. const handleResizeMouseOut = (e: MouseEvent) => {
  121. resizeHandler.value.removeEventListener(
  122. "mousemove",
  123. handleResizeMouseMove
  124. );
  125. resizeHandler.value.removeEventListener("mouseup", handleResizeMouseUp);
  126. resizeHandler.value.removeEventListener("mouseout", handleResizeMouseOut);
  127. };
  128. onUnmounted(() => {
  129. if (title) {
  130. sessionStorage.setItem(
  131. "dialog-" + title,
  132. JSON.stringify(positionStyle)
  133. );
  134. }
  135. });
  136. const increaseZIndex = () => {
  137. positionStyle.zIndex = store.maxModalZIndex++;
  138. if (store.maxModalZIndex === 5000) {
  139. store.maxModalZIndex = 1020;
  140. }
  141. };
  142. return {
  143. positionStyle,
  144. mouseHandler,
  145. resizeHandler,
  146. handleDragMouseDown,
  147. handleResizeMouseDown,
  148. increaseZIndex,
  149. };
  150. },
  151. });
  152. </script>
  153. <style scoped>
  154. .dialog-container {
  155. z-index: 1000;
  156. position: absolute;
  157. min-width: 100px;
  158. background-color: white;
  159. border: 1px solid grey;
  160. border-radius: 5px;
  161. box-shadow: 4px 6px 2px grey;
  162. }
  163. header {
  164. background-color: #eff3f6;
  165. }
  166. .resize-handler {
  167. position: absolute;
  168. bottom: 0;
  169. right: 0;
  170. width: 100px;
  171. height: 100px;
  172. cursor: nwse-resize;
  173. }
  174. </style>