ComponentWrapper.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. <template>
  2. <div
  3. class="component-wrapper"
  4. ref="componentWrapperRef"
  5. :style="warpperStyle"
  6. >
  7. <div class="component-content" @click="handleSelectComponent">
  8. <component :is="component" v-bind="componentData.props" />
  9. </div>
  10. <div v-if="showEditBox" class="edit-box" :style="editWapperStyle">
  11. <UseDraggable
  12. v-for="item in dragPointList"
  13. :key="item"
  14. @move="(_, e) => handleDragPoint(item, e)"
  15. @start="handleDragStart"
  16. @end="handleDragEnd"
  17. >
  18. <span class="edit-box-point" :class="item"></span>
  19. </UseDraggable>
  20. </div>
  21. </div>
  22. </template>
  23. <script setup lang="ts">
  24. import { defineProps, defineAsyncComponent, computed, ref } from "vue";
  25. import componentAll from "@/components";
  26. import type { CustomElement } from "#/project";
  27. import { useStageStore } from "@/store/modules/stage";
  28. import { useProjectStore } from "@/store/modules/project";
  29. import { useDraggable } from "@vueuse/core";
  30. import { UseDraggable } from "@vueuse/components";
  31. const { componentData } = defineProps<{ componentData: CustomElement }>();
  32. // 动态引入组件
  33. const component = defineAsyncComponent(
  34. componentAll[componentData.componentType]
  35. );
  36. const componentWrapperRef = ref<HTMLElement | null>(null);
  37. const stageStore = useStageStore();
  38. const projectStore = useProjectStore();
  39. const editWapperStyle = computed(() => {
  40. const { width = 400, height = 260 } = componentData.props || {};
  41. return {
  42. transform: `scale(${1 / stageStore.scale})`,
  43. transformOrigin: "50% 50%",
  44. width: `${width * stageStore.scale}px`,
  45. height: `${height * stageStore.scale}px`,
  46. border: "1px solid #1890ff",
  47. left: (width / 2) * (1 - stageStore.scale) + "px",
  48. top: (height / 2) * (1 - stageStore.scale) + "px",
  49. };
  50. });
  51. const warpperStyle = computed(() => {
  52. const { width = 400, height = 260 } = componentData.props || {};
  53. const { position } = componentData || {};
  54. return {
  55. width: `${width}px`,
  56. height: `${height}px`,
  57. left: position.x + "px",
  58. top: position.y + "px",
  59. };
  60. });
  61. // 是否显示编辑框
  62. const showEditBox = computed(() => {
  63. return (
  64. projectStore.mode === "edit" &&
  65. projectStore.selectedElementKeys.includes(componentData.key)
  66. );
  67. });
  68. let isPointDragFlag = false;
  69. // 拖拽移动组件
  70. useDraggable(componentWrapperRef, {
  71. onMove: (position) => {
  72. if(isPointDragFlag) return;
  73. const originPosition = componentWrapperRef.value!.getBoundingClientRect();
  74. // 计算移动的距离
  75. const x = position.x - originPosition.left;
  76. const y = position.y - originPosition.top;
  77. projectStore.updateElement(componentData.key, {
  78. position: {
  79. x: componentData.position.x + x,
  80. y: componentData.position.y + y,
  81. },
  82. });
  83. },
  84. onStart: () => {
  85. projectStore.setSelectedElementKeys([componentData.key]);
  86. }
  87. });
  88. const handleSelectComponent = () => {
  89. projectStore.setSelectedElementKeys([componentData.key]);
  90. };
  91. /* ===============================缩放组件==================================== */
  92. const dragPointList = [
  93. "top-left",
  94. "top-center",
  95. "top-right",
  96. "left-center",
  97. "right-center",
  98. "bottom-left",
  99. "bottom-center",
  100. "bottom-right",
  101. ];
  102. const startPoint = {
  103. x: 0,
  104. y: 0,
  105. };
  106. // 拖拽点移动
  107. const handleDragPoint = (type: string, e: PointerEvent) => {
  108. const moveX = (e.x - startPoint.x) / stageStore.scale;
  109. const moveY = (e.y - startPoint.y) / stageStore.scale;
  110. let width = componentData.props.width;
  111. let height = componentData.props.height;
  112. let x = componentData.position.x;
  113. let y = componentData.position.y;
  114. switch (type) {
  115. case "top-left":
  116. width -= moveX;
  117. height -= moveY;
  118. x += moveX;
  119. y += moveY;
  120. break;
  121. case "top-center":
  122. height -= moveY;
  123. y += moveY;
  124. break;
  125. case "top-right":
  126. width += moveX;
  127. height -= moveY;
  128. y += moveY;
  129. break;
  130. case "left-center":
  131. width -= moveX;
  132. x += moveX;
  133. break;
  134. case "right-center":
  135. width += moveX;
  136. break;
  137. case "bottom-left":
  138. width -= moveX;
  139. height += moveY;
  140. x += moveX;
  141. break;
  142. case "bottom-center":
  143. height += moveY;
  144. break;
  145. case "bottom-right":
  146. width += moveX;
  147. height += moveY;
  148. break;
  149. }
  150. startPoint.x = e.x;
  151. startPoint.y = e.y;
  152. if(width < 10 || height < 10) return;
  153. projectStore.updateElement(componentData.key, {
  154. position: { x, y },
  155. props: {
  156. ...componentData.props,
  157. width,
  158. height,
  159. },
  160. });
  161. };
  162. // 拖拽点开始
  163. const handleDragStart = (_, e: PointerEvent) => {
  164. startPoint.x = e.x;
  165. startPoint.y = e.y;
  166. isPointDragFlag = true;
  167. };
  168. // 拖拽点结束
  169. const handleDragEnd = () => {
  170. isPointDragFlag = false;
  171. };
  172. </script>
  173. <style lang="less" scoped>
  174. .component-wrapper {
  175. position: absolute;
  176. }
  177. .component-content {
  178. position: absolute;
  179. width: 100%;
  180. height: 100%;
  181. left: 0;
  182. top: 0;
  183. }
  184. .edit-box {
  185. position: absolute;
  186. &-point {
  187. position: absolute;
  188. width: 8px;
  189. height: 8px;
  190. background: #fff;
  191. border-radius: 50%;
  192. border: solid 1px @primary-color;
  193. }
  194. .top-left {
  195. top: -4px;
  196. left: -4px;
  197. cursor: nw-resize;
  198. }
  199. .top-center {
  200. top: -4px;
  201. left: 50%;
  202. transform: translateX(-50%);
  203. transform-origin: center;
  204. cursor: n-resize;
  205. }
  206. .top-right {
  207. top: -4px;
  208. right: -4px;
  209. cursor: ne-resize;
  210. }
  211. .left-center {
  212. top: 50%;
  213. left: -4px;
  214. transform: translateY(-50%);
  215. cursor: w-resize;
  216. }
  217. .right-center {
  218. top: 50%;
  219. right: -4px;
  220. transform: translateY(-50%);
  221. cursor: e-resize;
  222. }
  223. .bottom-left {
  224. bottom: -4px;
  225. left: -4px;
  226. cursor: sw-resize;
  227. }
  228. .bottom-center {
  229. bottom: -4px;
  230. left: 50%;
  231. transform: translateX(-50%);
  232. cursor: s-resize;
  233. }
  234. .bottom-right {
  235. bottom: -4px;
  236. right: -4px;
  237. cursor: se-resize;
  238. }
  239. }
  240. </style>