import { defineComponent, nextTick, onMounted, reactive, ref } from "vue"; import type { PropType } from "vue"; import { useRefs } from "../../hooks/core"; import { contains } from "../../utils"; import { ContextMenuItem, ContextMenuOptions } from "../../types"; export default defineComponent({ name: "cl-context-menu", props: { visible: Boolean, options: { type: Object as PropType, default: () => [] }, event: Object }, setup(props) { const { refs, setRefs }: any = useRefs(); // 菜单是否可见 const visible2 = ref(props.visible); // 按钮列表 const list = ref>([]); // 菜单样式 const style = reactive({ left: 0, top: 0 }); // 选中值 const ids = ref(""); // 阻止默认事件 function stopDefault(e: any) { if (e.preventDefault) { e.preventDefault(); } if (e.stopPropagation) { e.stopPropagation(); } } // 解析列表 function parseList(list: Array) { const deep = (list: any[]) => { list.forEach((e: any) => { e.showChildren = false; if (e.children) { deep(e.children); } }); }; deep(list); return list; } // 关闭菜单 function close() { visible2.value = false; ids.value = ""; } // 打开菜单 function open(event: any, options?: ContextMenuOptions) { let left: number = event.pageX; let top: number = event.pageY; if (!options) { options = {}; } if (options.list) { list.value = parseList(options.list); } nextTick(() => { const { clientHeight: h1, clientWidth: w1 } = document.body; const { clientHeight: h2, clientWidth: w2 } = refs.value["context-menu"]; if (top + h2 > h1) { top = h1 - h2 - 5; } if (left + w2 > w1) { left = w1 - w2 - 5; } style.left = left + "px"; style.top = top + "px"; }); // 阻止默认事件 stopDefault(event); // 显示菜单 visible2.value = true; return { close }; } // 行点击 function rowClick(e: any, id: string) { ids.value = id; if (e.disabled) { return false; } if (e.callback) { return e.callback(e, () => { close(); }); } if (e.children) { e.showChildren = !e.showChildren; } else { close(); } } onMounted(function () { if (visible2.value) { // 添加到 body 下 document.body.appendChild(refs.value["context-menu"]); // 关闭事件 (document.documentElement || document.body).addEventListener("mousedown", (e) => { const el = refs.value["context-menu"]; if (!contains(el, e.target) && el != e.target) { close(); } }); // 默认打开 open(props.event, props.options); } }); return { refs, visible2, ids, style, list, setRefs, open, close, rowClick, stopDefault }; }, render(ctx: any) { function deep(list: any[], pId: string, level: number) { return (
1 && "is-append"]}> {list .filter((e) => !e.hidden) .map((e, i) => { const id = `${pId}-${i}`; return (
{/* 前缀图标 */} {e["prefix-icon"] && } {/* 标题 */} { ctx.rowClick(e, id); }}> {e.label} {/* 后缀图标 */} {e["suffix-icon"] && } {/* 子集*/} {e.children && e.showChildren && deep(e.children, id, level + 1)}
); })}
); } return ( ctx.visible2 && (
{ctx.$slots.default ? ctx.$slots.default() : deep(ctx.list, "0", 1)}
) ); } });