|
@@ -1,63 +1,182 @@
|
|
|
-import { useLayers, useMode, useStage } from "./use-global-vars.ts";
|
|
|
+import {
|
|
|
+ useCursor,
|
|
|
+ useDownKeys,
|
|
|
+ useInstanceProps,
|
|
|
+ useLayers,
|
|
|
+ useMode,
|
|
|
+ useStage,
|
|
|
+} from "./use-global-vars.ts";
|
|
|
import { Stage } from "konva/lib/Stage";
|
|
|
import { useInteractiveProps } from "./use-interactive.ts";
|
|
|
import { useStore } from "../store/index.ts";
|
|
|
import { useViewer } from "./use-viewer.ts";
|
|
|
-import { useGlobalResize } from "./use-event.ts";
|
|
|
+import { useGlobalResize, useListener } from "./use-event.ts";
|
|
|
import { useInteractiveDrawShapeAPI } from "./use-draw.ts";
|
|
|
import { useHistory } from "./use-history.ts";
|
|
|
-import { reactive } from "vue";
|
|
|
+import { reactive, 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/help-style.ts";
|
|
|
+import { isSvgString } from "@/utils/resource.ts";
|
|
|
+import { useResourceHandler } from "./use-fetch.ts";
|
|
|
|
|
|
-type PickParams<K extends keyof Stage, O extends string> = Stage[K] extends (...args: any) => any ? Omit<Required<Parameters<Stage[K]>>[0], O> : never
|
|
|
+export const useAutoService = () => {
|
|
|
+ // 自动粘贴服务
|
|
|
+ const paste = usePaste();
|
|
|
+ const drawAPI = useInteractiveDrawShapeAPI()
|
|
|
+ const resourceHandler = useResourceHandler()
|
|
|
+ paste.push({
|
|
|
+ ["text/plain"]: {
|
|
|
+ async handler(pos, 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 {
|
|
|
+ drawAPI.addShape('image', { url }, pos, true)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ type: "file",
|
|
|
+ },
|
|
|
+ });
|
|
|
|
|
|
-export type DrawExpose = ReturnType<typeof useExpose>
|
|
|
+ // 自动退出添加模式
|
|
|
+ const { quitDrawShape } = useInteractiveDrawShapeAPI();
|
|
|
+ useListener(
|
|
|
+ "contextmenu",
|
|
|
+ (ev) => {
|
|
|
+ if (ev.button === 2) {
|
|
|
+ quitDrawShape();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ document.documentElement
|
|
|
+ );
|
|
|
|
|
|
-export const useExpose = () => {
|
|
|
- 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 config = reactive({
|
|
|
- showGrid: true,
|
|
|
- showLabelLine: true
|
|
|
- })
|
|
|
-
|
|
|
- const exposeBlob = (config?: PickParams<'toBlob', 'callback'>) => {
|
|
|
- const $stage = stage.value!.getStage()
|
|
|
- return new Promise<Blob>(resolve => {
|
|
|
- $stage.toBlob({ ...config, resolve } as any)
|
|
|
- })
|
|
|
- }
|
|
|
+ // 鼠标自动变化服务
|
|
|
+ const status = useMouseShapesStatus();
|
|
|
+ const downKeys = useDownKeys();
|
|
|
+ const mode = useMode();
|
|
|
+ const cursor = useCursor();
|
|
|
+
|
|
|
+ watchEffect((onCleanup) => {
|
|
|
+ let style: string | null = null;
|
|
|
+ if (downKeys.has(" ")) {
|
|
|
+ style = "pointer";
|
|
|
+ } else if (mode.include(Mode.update)) {
|
|
|
+ style = "move";
|
|
|
+ } else if (status.hovers.length) {
|
|
|
+ style = "pointer";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (style) {
|
|
|
+ cursor.push(style);
|
|
|
+ onCleanup(() => cursor.pop());
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 自动保存历史及恢复服务
|
|
|
+ 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))
|
|
|
|
|
|
- const toggleHit = () => {
|
|
|
- if (!layers.value) return;
|
|
|
- layers.value.forEach(layer => {
|
|
|
- layer.toggleHitCanvas()
|
|
|
+ if (!history.hasLocal()) return quitHooks;
|
|
|
+
|
|
|
+ let isOpen = true
|
|
|
+ ElMessageBox.confirm("检测到有历史数据,是否要恢复?", {
|
|
|
+ type: "info",
|
|
|
+ confirmButtonText: "恢复",
|
|
|
+ cancelButtonText: "取消",
|
|
|
+ }).then(() => {
|
|
|
+ history.loadLocalStorage();
|
|
|
+ }).catch(() => {
|
|
|
+ history.clearLocal();
|
|
|
+ }).finally (() => {
|
|
|
+ isOpen = false
|
|
|
})
|
|
|
- }
|
|
|
-
|
|
|
- return {
|
|
|
- ...useInteractiveDrawShapeAPI(),
|
|
|
- get stage() {
|
|
|
- const $store = stage.value!.getStage()
|
|
|
- return $store
|
|
|
- },
|
|
|
- exposeBlob,
|
|
|
- toggleHit,
|
|
|
- updateSize,
|
|
|
- history,
|
|
|
- store,
|
|
|
- mode,
|
|
|
- getData() {
|
|
|
- return store.data
|
|
|
- },
|
|
|
- viewer,
|
|
|
- presetAdd: interactiveProps,
|
|
|
- config
|
|
|
- }
|
|
|
-}
|
|
|
+ quitHooks.push(() => isOpen && ElMessageBox.close())
|
|
|
+ return quitHooks;
|
|
|
+ };
|
|
|
+ watchEffect((onCleanup) => {
|
|
|
+ onCleanup(mergeFuns(init(instanceProps.get().id)))
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+export type DrawExpose = ReturnType<typeof useExpose>;
|
|
|
+type PickParams<K extends keyof Stage, O extends string> = Stage[K] extends (
|
|
|
+ ...args: any
|
|
|
+) => any
|
|
|
+ ? Omit<Required<Parameters<Stage[K]>>[0], O>
|
|
|
+ : never;
|
|
|
+
|
|
|
+export const useExpose = () => {
|
|
|
+ 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 config = reactive({
|
|
|
+ showGrid: true,
|
|
|
+ showLabelLine: true,
|
|
|
+ });
|
|
|
+
|
|
|
+ const exposeBlob = (config?: PickParams<"toBlob", "callback">) => {
|
|
|
+ const $stage = stage.value!.getStage();
|
|
|
+ return new Promise<Blob>((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;
|
|
|
+ },
|
|
|
+ exposeBlob,
|
|
|
+ toggleHit,
|
|
|
+ updateSize,
|
|
|
+ history,
|
|
|
+ store,
|
|
|
+ mode,
|
|
|
+ getData() {
|
|
|
+ return store.data;
|
|
|
+ },
|
|
|
+ viewer,
|
|
|
+ presetAdd: interactiveProps,
|
|
|
+ config,
|
|
|
+ };
|
|
|
+};
|