diff --git a/package.json b/package.json index 3b7ab37ce..f60ca7ecd 100644 --- a/package.json +++ b/package.json @@ -122,6 +122,5 @@ "*.md": [ "prettier --write" ] - }, - "target": "web" + } } diff --git a/src/components/basic/context-menu/index.ts b/src/components/basic/context-menu/index.ts new file mode 100644 index 000000000..ed294d7bb --- /dev/null +++ b/src/components/basic/context-menu/index.ts @@ -0,0 +1,3 @@ +export { createContextMenu, destroyContextMenu } from './src/createContextMenu'; + +export * from './src/typing'; diff --git a/src/components/basic/context-menu/index.vue b/src/components/basic/context-menu/index.vue deleted file mode 100644 index 213c6bbf9..000000000 --- a/src/components/basic/context-menu/index.vue +++ /dev/null @@ -1,157 +0,0 @@ - - diff --git a/src/components/basic/context-menu/props.ts b/src/components/basic/context-menu/props.ts deleted file mode 100644 index 6f66c2db1..000000000 --- a/src/components/basic/context-menu/props.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Prop } from 'vue'; -import { Axis, ContextMenuItem } from './types'; -export const props = { - width: { - type: Number, - default: 180, - }, - customEvent: { - type: Object, - default: null, - } as Prop, - // 样式 - styles: { - type: Object, - default: null, - } as Prop, - showIcon: { - // 是否显示icon - type: Boolean, - default: true, - } as Prop, - axis: { - // 鼠标右键点击的位置 - type: Object, - default() { - return { x: 0, y: 0 }; - }, - } as Prop, - items: { - // 最重要的列表,没有的话直接不显示 - type: Array, - default() { - return []; - }, - } as Prop, - resolve: { - type: Function, - default: null, - } as Prop, -}; diff --git a/src/components/basic/context-menu/src/ContextMenu.vue b/src/components/basic/context-menu/src/ContextMenu.vue new file mode 100644 index 000000000..4c181f3ed --- /dev/null +++ b/src/components/basic/context-menu/src/ContextMenu.vue @@ -0,0 +1,207 @@ + + diff --git a/src/components/basic/context-menu/src/createContextMenu.ts b/src/components/basic/context-menu/src/createContextMenu.ts new file mode 100644 index 000000000..b9b10e7ab --- /dev/null +++ b/src/components/basic/context-menu/src/createContextMenu.ts @@ -0,0 +1,75 @@ +import contextMenuVue from './ContextMenu.vue'; +import { isClient } from '@/utils/is'; +import { CreateContextOptions, ContextMenuProps } from './typing'; +import { createVNode, render } from 'vue'; + +const menuManager: { + domList: Element[]; + resolve: Fn; +} = { + domList: [], + resolve: () => {}, +}; + +export const createContextMenu = function (options: CreateContextOptions) { + const { event } = options || {}; + + event && event?.preventDefault(); + + if (!isClient) { + return; + } + return new Promise((resolve) => { + const body = document.body; + + const container = document.createElement('div'); + const propsData: Partial = {}; + if (options.styles) { + propsData.styles = options.styles; + } + + if (options.items) { + propsData.items = options.items; + } + + if (options.event) { + propsData.customEvent = event; + propsData.axis = { x: event.clientX, y: event.clientY }; + } + + const vm = createVNode(contextMenuVue, propsData); + render(vm, container); + + const handleClick = function () { + menuManager.resolve(''); + }; + + menuManager.domList.push(container); + + const remove = function () { + menuManager.domList.forEach((dom: Element) => { + try { + dom && body.removeChild(dom); + } catch (error) {} + }); + body.removeEventListener('click', handleClick); + body.removeEventListener('scroll', handleClick); + }; + + menuManager.resolve = function (arg) { + remove(); + resolve(arg); + }; + remove(); + body.appendChild(container); + body.addEventListener('click', handleClick); + body.addEventListener('scroll', handleClick); + }); +}; + +export const destroyContextMenu = function () { + if (menuManager) { + menuManager.resolve(''); + menuManager.domList = []; + } +}; diff --git a/src/components/basic/context-menu/types.ts b/src/components/basic/context-menu/src/typing.ts similarity index 55% rename from src/components/basic/context-menu/types.ts rename to src/components/basic/context-menu/src/typing.ts index 5da6bc1cd..899d36b26 100644 --- a/src/components/basic/context-menu/types.ts +++ b/src/components/basic/context-menu/src/typing.ts @@ -7,29 +7,29 @@ export interface ContextMenuItem { label: string; icon?: string; disabled?: boolean; - handler?: (...arg: any) => any; + handler?: Fn; divider?: boolean; children?: ContextMenuItem[]; } -export interface Options { +export interface CreateContextOptions { event: MouseEvent; icon?: string; styles?: any; items?: ContextMenuItem[]; } -export interface Instance extends Props { - $el: HTMLDivElement; -} - -export type Props = { - resolve: (...arg: any) => void; - event: MouseEvent; - icon?: string; +export interface ContextMenuProps { + event?: MouseEvent; styles?: any; items: ContextMenuItem[]; - customEvent: MouseEvent; - axis: Axis; - width: number; + customEvent?: MouseEvent; + axis?: Axis; + width?: number; showIcon?: boolean; -}; +} + +export interface ItemContentProps { + showIcon: boolean | undefined; + item: ContextMenuItem; + handler: Fn; +} diff --git a/src/hooks/functions/useContextMenu.ts b/src/hooks/functions/useContextMenu.ts index ec0d11000..9e369b698 100644 --- a/src/hooks/functions/useContextMenu.ts +++ b/src/hooks/functions/useContextMenu.ts @@ -1,41 +1,12 @@ -import { createApp, h, App } from 'vue'; - -import { Props } from '@/components/basic/context-menu/types'; - -import ContentMenu from '@/components/basic/context-menu/index.vue'; - -let ContentMenuInstance: App | null = null; -let wrapperEl: HTMLElement | null = null; - -export const useContextMenu = (props: Props) => { - const { event } = props; - props.customEvent = event; - props.axis = { x: event.clientX, y: event.clientY }; - - const bodyClick = () => { - ContentMenuInstance && - wrapperEl && - ContentMenuInstance.unmount() && - document.body.removeChild(wrapperEl) && - wrapperEl.remove(); - document.body.removeEventListener('click', bodyClick); - document.body.removeEventListener('scroll', bodyClick); - ContentMenuInstance = null; - }; - - bodyClick(); - - if (!ContentMenuInstance) { - ContentMenuInstance = createApp({ - render() { - return h(ContentMenu, props); - }, +import { onUnmounted, getCurrentInstance } from 'vue'; +import { createContextMenu, destroyContextMenu } from '@/components/basic/context-menu'; +import type { ContextMenuItem } from '@/components/basic/context-menu'; +export type { ContextMenuItem }; +export function useContextMenu(authRemove = true) { + if (getCurrentInstance() && authRemove) { + onUnmounted(() => { + destroyContextMenu(); }); - wrapperEl = document.createElement('div'); - document.body.appendChild(wrapperEl); - ContentMenuInstance.mount(wrapperEl); } - - document.body.addEventListener('click', bodyClick); - document.body.addEventListener('scroll', bodyClick); -}; + return [createContextMenu, destroyContextMenu]; +} diff --git a/src/views/demos/tables/lol-table/index.vue b/src/views/demos/tables/lol-table/index.vue index 84ed6bfba..d6d1177ad 100644 --- a/src/views/demos/tables/lol-table/index.vue +++ b/src/views/demos/tables/lol-table/index.vue @@ -10,6 +10,7 @@ :data-request="loadData" :columns="columns" rowKey="heroid" + :customRow="customRow" /> @@ -26,6 +27,36 @@ import { DynamicTable } from '@/components/core/dynamic-table'; import { getLolHeroList } from '@/api/demos/hero'; import { columns } from './columns'; + import { useContextMenu } from '@/hooks/functions/useContextMenu'; + import { useRouter } from 'vue-router'; + + const router = useRouter(); + const [createContextMenu] = useContextMenu(); + + const customRow = (record) => { + return { + onContextmenu: (e: MouseEvent) => { + createContextMenu({ + event: e, + items: [ + { + label: '查看', + handler: () => { + console.log('record', record); + router.push({ name: 'demos-table-lol-info', params: { id: record.heroId } }); + }, + }, + { + label: '编辑', + handler: () => { + console.log('record', record); + }, + }, + ], + }); + }, + }; + }; const loadData = async (params) => { const { data } = await getLolHeroList(params); diff --git a/types/utils.d.ts b/types/utils.d.ts index 91b893133..0f0e11eed 100644 --- a/types/utils.d.ts +++ b/types/utils.d.ts @@ -1 +1,2 @@ +/** 提取Promise返回值 */ type UnboxPromise> = T extends Promise ? U : never; diff --git a/vue.config.js b/vue.config.js index 89680d610..9caef5904 100644 --- a/vue.config.js +++ b/vue.config.js @@ -135,6 +135,14 @@ module.exports = defineConfig({ }, }, }); + config.cache({ + // 将缓存类型设置为文件系统,默认是memory + type: 'filesystem', + buildDependencies: { + // 更改配置文件时,重新缓存 + config: [__filename], + }, + }); // https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk config.optimization.runtimeChunk('single'); }); @@ -144,7 +152,6 @@ module.exports = defineConfig({ config.experiments = { topLevelAwait: true, }; - config.resolve.fallback = { path: require.resolve('path-browserify') }; if (IS_PROD) {