use-component.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. import { DC, EntityShape } from "@/deconstruction";
  2. import {
  3. computed,
  4. EmitFn,
  5. isRef,
  6. reactive,
  7. Ref,
  8. ref,
  9. shallowReactive,
  10. watchEffect,
  11. } from "vue";
  12. import { useAutomaticData } from "./use-automatic-data";
  13. import { useCurrentZIndex, useZIndex } from "./use-layer";
  14. import { useAnimationMouseStyle } from "./use-mouse-status";
  15. import { components, DrawItem, ShapeType } from "../components";
  16. import { useMatCompTransformer, useLineTransformer } from "./use-transformer";
  17. import { useGetShapeCopyTransform } from "./use-copy";
  18. import {
  19. Bottom,
  20. Delete,
  21. DocumentCopy,
  22. Location,
  23. Lock,
  24. Top,
  25. Unlock,
  26. } from "@element-plus/icons-vue";
  27. import { mergeFuns, onlyId } from "@/utils/shared";
  28. import { Shape } from "konva/lib/Shape";
  29. import { Transform } from "konva/lib/Util";
  30. import { mergeDescribes, PropertyKeys } from "../propertys";
  31. import { useStore } from "../store";
  32. import { globalWatch } from "./use-global-vars";
  33. import { useAlignmentShape } from "./use-alignment";
  34. type Emit<T> = EmitFn<{
  35. updateShape: (value: T) => void;
  36. addShape: (value: T) => void;
  37. delShape: () => void;
  38. }>;
  39. export const useComponentMenus = <T extends DrawItem>(
  40. shape: Ref<DC<EntityShape> | undefined>,
  41. data: Ref<T>,
  42. emit: Emit<T>,
  43. alignment?: (data: T, mat: Transform) => void,
  44. copyHandler?: (transform: Transform, data: T) => T
  45. ) => {
  46. const operateMenus: Array<{
  47. icon?: any;
  48. label?: string;
  49. handler: () => void;
  50. }> = shallowReactive([]);
  51. // 锁定 解锁
  52. operateMenus.push(
  53. reactive({
  54. label: computed(() => (data.value.lock ? "解锁" : "锁定")) as any,
  55. icon: computed(() => (data.value.lock ? Unlock : Lock)),
  56. handler() {
  57. data.value.lock = !data.value.lock;
  58. emit("updateShape", { ...data.value });
  59. },
  60. })
  61. );
  62. // 置顶 置底
  63. const currentZIndex = useCurrentZIndex();
  64. operateMenus.push(
  65. {
  66. label: `置顶`,
  67. icon: Top,
  68. handler() {
  69. data.value.zIndex = currentZIndex.max + 1;
  70. emit("updateShape", { ...data.value });
  71. },
  72. },
  73. {
  74. label: `置底`,
  75. icon: Bottom,
  76. handler() {
  77. data.value.zIndex = currentZIndex.min - 1;
  78. emit("updateShape", { ...data.value });
  79. },
  80. }
  81. );
  82. if (alignment) {
  83. const [alignmentShape] = useAlignmentShape(shape);
  84. operateMenus.push({
  85. label: "对齐",
  86. async handler() {
  87. const mat = await alignmentShape();
  88. alignment(data.value, mat);
  89. emit("updateShape", { ...data.value });
  90. },
  91. icon: Location,
  92. });
  93. }
  94. if (copyHandler) {
  95. const getCopyTransform = useGetShapeCopyTransform(shape);
  96. operateMenus.push({
  97. label: `复制`,
  98. icon: DocumentCopy,
  99. handler() {
  100. const transform = getCopyTransform();
  101. const copyData = copyHandler(
  102. transform,
  103. JSON.parse(JSON.stringify(data.value)) as T
  104. );
  105. copyData.id = onlyId();
  106. emit("addShape", copyData);
  107. },
  108. });
  109. }
  110. operateMenus.push({
  111. label: `删除`,
  112. icon: Delete,
  113. handler() {
  114. emit("delShape");
  115. },
  116. });
  117. return operateMenus;
  118. };
  119. export type UseComponentStatusProps<
  120. T extends DrawItem,
  121. S extends EntityShape
  122. > = {
  123. emit: Emit<T>;
  124. type?: ShapeType;
  125. props: { data: T };
  126. alignment?: (data: T, mat: Transform) => void;
  127. getMouseStyle: any;
  128. defaultStyle: any;
  129. propertys: PropertyKeys;
  130. transformType?: "line" | "mat" | "custom";
  131. customTransform?: (
  132. callback: () => void,
  133. shape: Ref<DC<S> | undefined>,
  134. data: Ref<T>
  135. ) => void;
  136. getRepShape?: () => Shape;
  137. copyHandler: (transform: Transform, data: T) => T;
  138. };
  139. export const useComponentStatus = <S extends EntityShape, T extends DrawItem>(
  140. args: UseComponentStatusProps<T, S>
  141. ) => {
  142. const shape = ref<DC<S>>();
  143. const data = useAutomaticData(() => args.props.data);
  144. const [style] = useAnimationMouseStyle({
  145. data: data,
  146. shape,
  147. getMouseStyle: args.getMouseStyle,
  148. }) as any;
  149. if (args.transformType === "line") {
  150. useLineTransformer(
  151. shape as any,
  152. data as any,
  153. (newData) => args.emit("updateShape", newData as T),
  154. args.getRepShape as any
  155. );
  156. } else if (args.transformType === "mat") {
  157. useMatCompTransformer(shape, data as any, (nData) =>
  158. args.emit("updateShape", nData as any)
  159. );
  160. } else if (args.transformType === "custom" && args.customTransform) {
  161. args.customTransform(
  162. () => args.emit("updateShape", data.value as any),
  163. shape,
  164. data
  165. );
  166. }
  167. useZIndex(shape, data);
  168. return {
  169. data,
  170. style,
  171. tData: computed(() => {
  172. const tData = { ...args.defaultStyle, ...data.value };
  173. if (style) {
  174. Object.assign(tData, style.value);
  175. }
  176. return tData;
  177. }),
  178. shape,
  179. operateMenus: useComponentMenus(
  180. shape,
  181. data,
  182. args.emit,
  183. args.alignment,
  184. args.copyHandler
  185. ),
  186. describes: mergeDescribes(
  187. data,
  188. args.defaultStyle,
  189. args.propertys || []
  190. )
  191. };
  192. };
  193. export const useGetComponentData = <D extends DrawItem>() => {
  194. const store = useStore();
  195. return (shape: Ref<EntityShape | undefined> | EntityShape | undefined) =>
  196. computed(() => {
  197. shape = isRef(shape) ? shape.value : shape;
  198. if (!shape?.id()) return;
  199. return store.getItemById(shape.id()) as D;
  200. });
  201. };
  202. export const useComponentsAttach = <T>(
  203. getter: <K extends ShapeType>(type: K, data: DrawItem<K>) => T,
  204. types = Object.keys(components) as ShapeType[]
  205. ) => {
  206. const store = useStore();
  207. const attachs = reactive([]) as T[];
  208. const cleanups = [] as Array<() => void>;
  209. for (const type of types) {
  210. cleanups.push(
  211. globalWatch(
  212. () => store.data[type]?.map((item) => item),
  213. (items, _, onCleanup) => {
  214. if (!items) return;
  215. for (const item of items) {
  216. const attachWatchStop = watchEffect((onCleanup) => {
  217. const attach = getter(type, item);
  218. attachs.push(attach);
  219. onCleanup(() => {
  220. const ndx = attachs.indexOf(attach);
  221. ~ndx && attachs.splice(ndx, 1);
  222. });
  223. });
  224. const existsWatchStop = watchEffect(() => {
  225. if (!items.includes(item)) {
  226. attachWatchStop();
  227. existsWatchStop();
  228. }
  229. });
  230. onCleanup(() => {
  231. attachWatchStop();
  232. existsWatchStop();
  233. });
  234. }
  235. },
  236. { immediate: true }
  237. )
  238. );
  239. }
  240. return {
  241. attachs,
  242. cleanup: mergeFuns(cleanups),
  243. };
  244. };