From 32552d03a5efd2bf95fa52b9abf8798866d095c5 Mon Sep 17 00:00:00 2001 From: pipipi-pikachu <1171051090@qq.com> Date: Sat, 12 Dec 2020 16:56:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=B3=E9=94=AE=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E5=92=8Cclickoutside=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 - public/index.html | 2 + .../Contextmenu/ContextmenuContent.vue | 179 ++++++++++++++++++ src/components/Contextmenu/index.vue | 124 ++++++++++++ src/components/Contextmenu/types.ts | 16 ++ src/components/IconFont.ts | 5 - src/components/IconFont.vue | 28 +++ src/main.ts | 7 + src/plugins/clickOutside.ts | 34 ++++ src/plugins/contextmenu.ts | 62 ++++++ src/views/Editor/Canvas/index.vue | 35 ++++ 11 files changed, 487 insertions(+), 6 deletions(-) create mode 100644 src/components/Contextmenu/ContextmenuContent.vue create mode 100644 src/components/Contextmenu/types.ts delete mode 100644 src/components/IconFont.ts create mode 100644 src/components/IconFont.vue create mode 100644 src/plugins/clickOutside.ts create mode 100644 src/plugins/contextmenu.ts diff --git a/package.json b/package.json index 344e4712..507b8d5e 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "lint": "vue-cli-service lint" }, "dependencies": { - "@ant-design/icons-vue": "^5.1.7", "ant-design-vue": "^2.0.0-rc.3", "clipboard": "^2.0.6", "core-js": "^3.6.5", diff --git a/public/index.html b/public/index.html index 5e98cfd2..f1ced8e1 100644 --- a/public/index.html +++ b/public/index.html @@ -14,5 +14,7 @@
+ + diff --git a/src/components/Contextmenu/ContextmenuContent.vue b/src/components/Contextmenu/ContextmenuContent.vue new file mode 100644 index 00000000..b2835d92 --- /dev/null +++ b/src/components/Contextmenu/ContextmenuContent.vue @@ -0,0 +1,179 @@ + + + + + \ No newline at end of file diff --git a/src/components/Contextmenu/index.vue b/src/components/Contextmenu/index.vue index e69de29b..1b6f4029 100644 --- a/src/components/Contextmenu/index.vue +++ b/src/components/Contextmenu/index.vue @@ -0,0 +1,124 @@ + + + + + \ No newline at end of file diff --git a/src/components/Contextmenu/types.ts b/src/components/Contextmenu/types.ts new file mode 100644 index 00000000..4bc0e77d --- /dev/null +++ b/src/components/Contextmenu/types.ts @@ -0,0 +1,16 @@ +export interface ContextmenuItem { + text?: string; + subText?: string; + icon?: string; + divider?: boolean; + disable?: boolean; + hide?: boolean; + iconPlacehoder?: boolean; + children?: ContextmenuItem[]; + action?: (el: HTMLElement) => void; +} + +export interface Axis { + x: number; + y: number; +} \ No newline at end of file diff --git a/src/components/IconFont.ts b/src/components/IconFont.ts deleted file mode 100644 index 599524d1..00000000 --- a/src/components/IconFont.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createFromIconfontCN } from '@ant-design/icons-vue' - -export default createFromIconfontCN({ - scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js', -}) \ No newline at end of file diff --git a/src/components/IconFont.vue b/src/components/IconFont.vue new file mode 100644 index 00000000..2352a755 --- /dev/null +++ b/src/components/IconFont.vue @@ -0,0 +1,28 @@ + + + + + \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 1bae6314..ef3dea84 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,7 +5,14 @@ import store from './store' import '@/assets/styles/global.scss' +import IconFont from '@/components/IconFont.vue' +import contextmenu from './plugins/contextmenu' +import clickOutside from './plugins/clickOutside' + const app = createApp(App) +app.component('IconFont', IconFont) +app.use(contextmenu) +app.use(clickOutside) app.use(store) app.use(router) app.mount('#app') diff --git a/src/plugins/clickOutside.ts b/src/plugins/clickOutside.ts new file mode 100644 index 00000000..59500248 --- /dev/null +++ b/src/plugins/clickOutside.ts @@ -0,0 +1,34 @@ +import { Directive, App, DirectiveBinding } from 'vue' + +const CTX_CLICK_OUTSIDE_HANDLER = 'CTX_CLICK_OUTSIDE_HANDLER' + +const clickListener = (el: HTMLElement, event: MouseEvent, binding: DirectiveBinding) => { + const handler = binding.value + + const path = event.composedPath() + const isClickOutside = path ? path.indexOf(el) < 0 : !el.contains(event.target as HTMLElement) + + if(!isClickOutside) return + handler(event) +} + +const ClickOutsideDirective: Directive = { + mounted(el: HTMLElement, binding) { + el[CTX_CLICK_OUTSIDE_HANDLER] = (event: MouseEvent) => clickListener(el, event, binding) + document.addEventListener('mousedown', el[CTX_CLICK_OUTSIDE_HANDLER]) + }, + + unmounted(el: HTMLElement) { + if(el && el[CTX_CLICK_OUTSIDE_HANDLER]) { + document.removeEventListener('mousedown', el[CTX_CLICK_OUTSIDE_HANDLER]) + delete el[CTX_CLICK_OUTSIDE_HANDLER] + } + }, +} + +export default { + install(app: App) { + app.directive('click-outside', ClickOutsideDirective) + }, + directive: ClickOutsideDirective, +} \ No newline at end of file diff --git a/src/plugins/contextmenu.ts b/src/plugins/contextmenu.ts new file mode 100644 index 00000000..a9a2121d --- /dev/null +++ b/src/plugins/contextmenu.ts @@ -0,0 +1,62 @@ +import { Directive, App, createVNode, render, DirectiveBinding } from 'vue' +import ContextmenuComponent from '@/components/Contextmenu/index.vue' + +const CTX_CONTEXTMENU_HANDLER = 'CTX_CONTEXTMENU_HANDLER' + +const contextmenuListener = (el: HTMLElement, event: MouseEvent, binding: DirectiveBinding) => { + event.stopPropagation() + event.preventDefault() + + const menus = binding.value(el) + if(!menus) return + const isDark = binding.modifiers.dark + + let container: HTMLDivElement | null = null + + const removeContextMenu = () => { + if(container) { + document.body.removeChild(container) + container = null + } + el.classList.remove('contextmenu-active') + document.body.removeEventListener('scroll', removeContextMenu) + window.removeEventListener('resize', removeContextMenu) + } + + const options = { + axis: { x: event.x, y: event.y }, + el, + menus, + isDark, + removeContextMenu, + } + container = document.createElement('div') + const vm = createVNode(ContextmenuComponent, options, null) + render(vm, container) + document.body.appendChild(container) + + el.classList.add('contextmenu-active') + + document.body.addEventListener('scroll', removeContextMenu) + window.addEventListener('resize', removeContextMenu) +} + +const ContextmenuDirective: Directive = { + mounted(el: HTMLElement, binding) { + el[CTX_CONTEXTMENU_HANDLER] = (event: MouseEvent) => contextmenuListener(el, event, binding) + el.addEventListener('contextmenu', el[CTX_CONTEXTMENU_HANDLER]) + }, + + unmounted(el: HTMLElement) { + if(el && el[CTX_CONTEXTMENU_HANDLER]) { + el.removeEventListener('contextmenu', el[CTX_CONTEXTMENU_HANDLER]) + delete el[CTX_CONTEXTMENU_HANDLER] + } + }, +} + +export default { + install(app: App) { + app.directive('contextmenu', ContextmenuDirective) + } +} \ No newline at end of file diff --git a/src/views/Editor/Canvas/index.vue b/src/views/Editor/Canvas/index.vue index 1c78f01a..778d01ef 100644 --- a/src/views/Editor/Canvas/index.vue +++ b/src/views/Editor/Canvas/index.vue @@ -3,6 +3,7 @@ class="canvas" ref="canvasRef" @mousedown="$event => handleClickBlankArea($event)" + v-contextmenu="contextmenus" >
{ + return [ + { + text: '全选', + subText: 'Ctrl + A', + }, + { + text: '粘贴', + subText: 'Ctrl + V', + }, + { divider: true }, + { + text: '参考线', + children: [ + { + text: '打开', + }, + { + text: '关闭', + }, + ], + }, + { + text: '背景设置', + }, + { divider: true }, + { + text: '清空页面', + }, + ] + } + return { canvasRef, viewportRef, viewportStyles, mouseSelectionState, handleClickBlankArea, + contextmenus, } }, })