import { installGlobalVar, useCursor, useInstanceProps, useLayers, useMountMenusAttachs, useStage, useTempStatus, } from "./use-global-vars.ts"; import { useMode, useOperMode } from "./use-status"; import { Stage } from "konva/lib/Stage"; import { useInteractiveProps } from "./use-interactive.ts"; import { useStore } from "../store/index.ts"; import { useGetViewBoxPositionPixel, useSetViewport, useViewer } from "./use-viewer.ts"; import { useGlobalResize, useListener } from "./use-event.ts"; import { useInteractiveDrawShapeAPI } from "./use-draw.ts"; import { useHistory } from "./use-history.ts"; import { watchEffect } from "vue"; import { usePaste } from "./use-paste.ts"; import { useMouseShapesStatus } from "./use-mouse-status.ts"; import { Mode } from "@/constant/mode.ts"; import { ElMessageBox } from "element-plus"; import { mergeFuns } from "@/utils/shared.ts"; import { themeColor } from "@/constant"; import { getImage, isSvgString } from "@/utils/resource.ts"; import { useResourceHandler } from "./use-fetch.ts"; import { useConfig } from "./use-config.ts"; import { useSelectionRevise } from "./use-selection.ts"; import { useFormalLayer, useGetFormalChildren } from "./use-layer.ts"; import { components } from "../components/index.ts"; import { useProportion } from "./use-proportion.ts"; import { ShapeType } from "@/index.ts"; import { useGetDXF } from "./use-dxf.ts"; // 自动粘贴服务 export const useAutoPaste = () => { const paste = usePaste(); const drawAPI = useInteractiveDrawShapeAPI(); const resourceHandler = useResourceHandler(); paste.push({ ["text/plain"]: { async handler(pos, val) { console.log(val); if (isSvgString(val)) { const url = await resourceHandler(val, "svg"); drawAPI.addShape("icon", { url, stroke: themeColor }, pos, true); } else { drawAPI.addShape("text", { content: val }, pos, true); } }, type: "string", }, ["image"]: { async handler(pos, val, type) { const url = await resourceHandler(val, type); if (type.includes("svg")) { drawAPI.addShape("icon", { url, stroke: themeColor }, pos, true); } else { const image = await getImage(url); drawAPI.addShape( "image", { url, width: image.width, height: image.height }, pos, true ); } }, type: "file", }, }); }; // 快捷键服务 export const useShortcutKey = () => { // 自动退出添加模式 const { quitDrawShape, enterDrawShape } = useInteractiveDrawShapeAPI(); const interactiveProps = useInteractiveProps(); const store = useStore(); useListener( "contextmenu", (ev) => { const iProps = interactiveProps.value; if (!iProps?.type || ev.button !== 2) return; const addCount = quitDrawShape(); // 钢笔工具需要右键两次才退出,右键一次相当于完成 const isDots = ['dots', 'single-dots'].includes(components[iProps.type].addMode); if (isDots && addCount > 0) { setTimeout(() => { enterDrawShape(iProps.type, iProps.preset, iProps.operate?.single); }, 10); } }, document.documentElement ); const history = useHistory(); const status = useMouseShapesStatus(); const getChildren = useGetFormalChildren(); const operMode = useOperMode(); useListener( "keydown", (ev) => { if (ev.target !== document.body) return; if (ev.key === "z" && ev.ctrlKey) { history.hasUndo.value && history.undo(); } else if (ev.key === "y" && ev.ctrlKey) { history.hasRedo.value && history.redo(); } else if (ev.key === "s" && ev.ctrlKey) { // 保存 // history.saveLocal(); } else if (ev.key === "Delete" || ev.key === "Backspace") { // 删除 const isSelect = status.selects.length; const shapes = isSelect ? status.selects : status.actives; const delItems = shapes.map((shape) => { const id = shape.id(); if (!id) return; const item = store.getItemById(id) const type = store.getType(id); if (!item?.disableDelete && type) { return [type, item] as const } }).filter(item => !!item); history.onceTrack(() => { delItems.forEach(([type, item]) => { if (components[type as ShapeType].delItem) { components[type as ShapeType].delItem!(store, item as any) } else { store.delItem(type as ShapeType, item!.id); } }) }); if (delItems.length) { if (isSelect) { status.selects = []; } else { status.actives = []; } } } else if (operMode.value.mulSelection && ev.key === "A") { if (status.selects.length) { status.selects = []; } else { status.selects = getChildren(); } } }, window ); }; export const useAutoService = () => { useAutoPaste(); useShortcutKey(); // 鼠标自动变化服务 const status = useMouseShapesStatus(); const operMode = useOperMode(); const mode = useMode(); const cursor = useCursor(); const { set: setCursor } = cursor.push("initial"); watchEffect(() => { let style: string | null = null; if (operMode.value.freeView) { style = "pointer"; } else if (mode.include(Mode.update)) { style = "./icons/m_move.png"; } else if (status.hovers.length) { style = "pointer"; } else { style = "initial"; } setCursor(style); }); // 自动保存历史及恢复服务 const history = useHistory(); const instanceProps = useInstanceProps(); const init = (id: any) => { const quitHooks: (() => void)[] = []; if (!id) return quitHooks; const unloadHandler = () => { if (history.hasRedo.value || history.hasUndo.value) { // history.saveLocal(); } }; history.setLocalId(id); window.addEventListener("beforeunload", unloadHandler); quitHooks.push(() => window.removeEventListener("beforeunload", unloadHandler) ); if (!history.hasLocal()) return quitHooks; if (!import.meta.env.DEV) { let isOpen = true; ElMessageBox.confirm("检测到有历史数据,是否要恢复?", { type: "info", confirmButtonText: "恢复", cancelButtonText: "取消", }) .then(() => { history.loadLocalStorage(); }) .catch(() => { history.clearLocal(); }) .finally(() => { isOpen = false; }); quitHooks.push(() => isOpen && ElMessageBox.close()); } return quitHooks; }; watchEffect((onCleanup) => { onCleanup(mergeFuns(init(instanceProps.get().id))); }); useSelectionRevise(); }; export type DrawExpose = ReturnType; type PickParams = Stage[K] extends ( ...args: any ) => any ? Omit>[0], O> : never; export const useExpose = installGlobalVar(() => { const mode = useMode(); const interactiveProps = useInteractiveProps(); const stage = useStage(); const layers = useLayers(); const store = useStore(); const history = useHistory(); const viewer = useViewer().viewer; const { updateSize } = useGlobalResize(); const exposeBlob = (config?: PickParams<"toBlob", "callback">) => { const $stage = stage.value!.getStage(); return new Promise((resolve) => { $stage.toBlob({ ...config, resolve } as any); }); }; const toggleHit = () => { if (!layers.value) return; layers.value.forEach((layer) => { layer.toggleHitCanvas(); }); }; return { ...useInteractiveDrawShapeAPI(), get stage() { const $store = stage.value?.getStage(); return $store; }, ...useTempStatus(), exposeBlob, toggleHit, formalLayer: useFormalLayer(), updateSize, history, store, ...useSetViewport(), mode, getData() { return store.data; }, getViewBoxPositionPixel: useGetViewBoxPositionPixel(), viewer, presetAdd: interactiveProps, config: useConfig(), mountMenus: useMountMenusAttachs(), proportion: useProportion(), getDXF: useGetDXF() }; });