- 作者:老汪软件技巧
- 发表时间:2024-12-17 21:03
- 浏览量:
在前端开发中,一个功能齐全、灵活易用的 Dropdown 组件是必不可少的。本文将带你从零搭建一个基于 Vue3 的 DropdownP 组件,展示从需求分析到代码实现的完整流程。
需求分析确定方案
import type { VNode } from "vue";
import type { TooltipProps } from "../Tooltip/types";
// 需要在Tooltip组件的属性上进行继承和拓展
export interface DropdownProps extends TooltipProps {
// 传入的菜单项,用一个数组保存
menuOptions: MenuOption[];
// 点击某一项之后关闭菜单展示
afterClickItem?: boolean;
}
// 每一项列表具有的属性
export interface MenuOption {
// 字符串或自定义节点
label: string | VNode;
key: string | number;
disabled?: boolean;
// 分割线
divided?: boolean;
}
DropdownP 支持以下事件:
export interface DropdownEmits {
// 打开列表派发事件
(e: "visible-change", value: boolean): void;
// 选中选项派发事件
(e: "select", value: MenuOption): void;
}
通过暴露实例方法,用户可以在外部手动控制菜单显隐状态:
export interface DropdownInstance {
show: () => void;
hide: () => void;
}
<template>
<div class="yv-dropdown">
<Tooltip>
<slot>slot>
<template #content>
<ul>
<template >
<hr >
<li>
<RenderVnode />
li>
template>
ul>
template>
Tooltip>
div>
template>
设计思路
<template>
<div class="jd-dropdown">
<Tooltip
:trigger="trigger"
:placement="placement"
:popper-options="popperOptions"
:open-delay="openDelay"
:close-delay="closeDelay"
@visible-change="visibleChange"
:manual="manual"
ref="tooltipRef"
>
<slot />
<template #content>
<ul class="jd-dropdown__menu">
<template v-for="item in menuOptions" :key="item.key">
<li
v-if="item.divided"
role="separator"
class="divided-placeholder"
/>
<li
class="jd-dropdown__item"
@click="itemClick(item)"
:class="{
'is-disabled': item.disabled,
'is-divided': item.divided,
}"
:id="`dropdown-item-${item.key}`"
>
<RenderVnode :vNode="item.label" />
li>
template>
ul>
template>
Tooltip>
div>
template>
<script setup lang="ts">
import { ref } from "vue";
import type { Ref } from "vue";
import type {
DropdownProps,
DropdownInstance,
DropdownEmits,
MenuOption,
} from "./types";
import type { TooltipInstance } from "../Tooltip/types";
import Tooltip from "../Tooltip/Tooltip.vue";
import RenderVnode from "../Common/RenderVnode";
defineOptions({
name: "JdDropdown",
});
const props = withDefaults(defineProps<DropdownProps>(), {
afterClickItem: true,
});
const emits = defineEmits<DropdownEmits>();
const tooltipRef = ref() as Ref<TooltipInstance>;
const visibleChange = (e: boolean) => {
emits("visible-change", e);
};
const itemClick = (e: MenuOption) => {
if (e.disabled) return;
emits("select", e);
if (props.afterClickItem) {
tooltipRef.value?.hide();
}
};
defineExpose<DropdownInstance>({
// 通过闭包的形式,直接赋值会拿收不到对应的节点,因为是在setup函数中,此时实例还未被挂载
show: () => tooltipRef.value?.show(),
hide: () => tooltipRef.value?.hide(),
});
script>
//types.ts
import type { VNode } from "vue";
import type { TooltipProps } from "../Tooltip/types";
export interface DropdownProps extends TooltipProps {
// 传入的菜单项,用一个数组保存
menuOptions: MenuOption[];
// 点击某一项之后关闭菜单展示
afterClickItem?: boolean;
}
// 每一项列表具有的属性
export interface MenuOption {
// 字符串或自定义节点
label: string | VNode;
key: string | number;// 每个选项的唯一标识
disabled?: boolean;
// 分割线
divided?: boolean;
}
export interface DropdownEmits {
// 打开列表派发事件
(e: "visible-change", value: boolean): void;
// 选中选项派发事件
(e: "select", value: MenuOption): void;
}
export interface DropdownInstance {
show: () => void;
hide: () => void;
}
总结
通过上述步骤,我们成功地从零搭建了一个高度可定制的 Vue 3 Dropdown 组件。该组件不仅继承了 Tooltip 组件的弹出层管理功能,还增加了菜单列表、自定义选项、分割线等特性,能够满足大多数实际项目中的需求。此外,我们还通过 TypeScript 提供了严格的类型检查,确保组件的健壮性和可维护性。