QmDialog.vue 5.7 KB

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