mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2025-12-16 00:22:53 +00:00
217 lines
4.1 KiB
TypeScript
217 lines
4.1 KiB
TypeScript
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<ContextMenuOptions>,
|
|
default: () => []
|
|
},
|
|
event: Object
|
|
},
|
|
|
|
setup(props) {
|
|
const { refs, setRefs }: any = useRefs();
|
|
|
|
// 菜单是否可见
|
|
const visible2 = ref<boolean>(props.visible);
|
|
|
|
// 按钮列表
|
|
const list = ref<Array<ContextMenuItem>>([]);
|
|
|
|
// 菜单样式
|
|
const style = reactive<any>({
|
|
left: 0,
|
|
top: 0
|
|
});
|
|
|
|
// 选中值
|
|
const ids = ref<string>("");
|
|
|
|
// 阻止默认事件
|
|
function stopDefault(e: any) {
|
|
if (e.preventDefault) {
|
|
e.preventDefault();
|
|
}
|
|
|
|
if (e.stopPropagation) {
|
|
e.stopPropagation();
|
|
}
|
|
}
|
|
|
|
// 解析列表
|
|
function parseList(list: Array<ContextMenuItem>) {
|
|
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 (
|
|
<div class={["cl-context-menu__box", level > 1 && "is-append"]}>
|
|
{list
|
|
.filter((e) => !e.hidden)
|
|
.map((e, i) => {
|
|
const id = `${pId}-${i}`;
|
|
|
|
return (
|
|
<div
|
|
class={{
|
|
"is-active": ctx.ids.includes(id),
|
|
"is-ellipsis": e.ellipsis,
|
|
"is-disabled": e.disabled
|
|
}}>
|
|
{/* 前缀图标 */}
|
|
{e["prefix-icon"] && <i class={e["prefix-icon"]}></i>}
|
|
|
|
{/* 标题 */}
|
|
<span
|
|
onClick={() => {
|
|
ctx.rowClick(e, id);
|
|
}}>
|
|
{e.label}
|
|
</span>
|
|
|
|
{/* 后缀图标 */}
|
|
{e["suffix-icon"] && <i class={e["suffix-icon"]}></i>}
|
|
|
|
{/* 子集*/}
|
|
{e.children &&
|
|
e.showChildren &&
|
|
deep(e.children, id, level + 1)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
ctx.visible2 && (
|
|
<div
|
|
class="cl-context-menu"
|
|
ref={ctx.setRefs("context-menu")}
|
|
style={ctx.style}
|
|
onContextmenu={ctx.stopDefault}>
|
|
{ctx.$slots.default ? ctx.$slots.default() : deep(ctx.list, "0", 1)}
|
|
</div>
|
|
)
|
|
);
|
|
}
|
|
});
|