在开发者与产品经理的精彩交响曲中,我们常面临着创新与实现的美妙挑战。面对Ant Design Vue的Cascade组件无法直接提供所需插槽的场景,传统解决方案或许指向重新封装组件,但今天,让我们共舞一曲优雅的变奏——利用自定义指令解锁新潜能。
背景
想象一下,您正优化一个界面,其中的级联选择器(Cascade)亟需个性化定制,一个小小的按钮,却因原生支持的缺失而显得遥不可及。
同学们肯定知道,使用antd vue cascade组件是没有提供对应位置插槽,怎么办?通常情况下我会重写个新组件来解决这个问题,但是这次我提供一个新的解题思路。使用自定义指令的方式来实践下。
下面业务中的展示效果:
四步定制法
1. DOM的精妙调整
首先我们知道cascade组件的下拉框是在body元素下面的,为了js操作dom方便,我们使用getPopupContainer把下拉框dom都固定到input输入框一起:
:getPopupContainer="
(triggerNode) => {
return triggerNode.parentNode || document.body;
}
"
2. 自定义指令的华丽登场
然后就是编写自定义指令,在mounted之后,当点击input输入框时创建添加按钮dom,然后添加到指定位置(这里需要查看组件dom结构,然后确定最佳位置):
el.addEventListener('click', () => {
//已经存在就不需要添加,或者数据为空不用添加
if (el.querySelector('.my-btn') || el.querySelector('.ant-cascader-menu-empty')) {
return;
}
let div: any = document.createElement('div');
let node = document.createTextNode('添加');
div.setAttribute('class', 'my-btn');
div.appendChild(node);
div.style =
'color: #0cad7d;padding:10px;width:100%;text-align:right;border-top:solid 1px #383c44';
el.querySelector('.ant-select-dropdown').appendChild(div);
});
3. 未来元素绑定click事件
然后就是未来元素绑定click事件的注意事项,需要绑定到document上然后判断target是否时当前dom,需要绑定点击add按钮的事件,以及点击取消的事件,重点记得removeEventListener:
let handles = {}
handles.el = el;
handles.cancel = (event) => {
if (el.querySelector('.my-btn') || el.querySelector('.ant-cascader-menu-empty')) {
console.log(event.target, 'cancel clicked');
if (event.target !== el.querySelector('.ant-select-selection-overflow')) {
open.value = false;
}
return;
}
};
handles.add = (event) => {
console.log(event.target, 'add');
let div = el.querySelector('.my-btn');
if (event.target == div) {
binding.value();
open.value = false;
}
};
document.removeEventListener('click', handles.cancel);
document.addEventListener('click', handles.cancel);
el.removeEventListener('click', handles.add);
el.addEventListener('click', handles.add);
4. beforeUnmount中remove click事件
beforeUnmount: () => {
handles.el.removeEventListener('click', handles.add);
document.removeEventListener('click', handles.cancel);
},
完整代码: template模板
<a-cascader
:getPopupContainer="
(triggerNode) => {
return triggerNode.parentNode || document.body;
}
"
:open="open"
@click.stop="open = true"
:key="cascaderKey"
v-insertBtn="handlerClick"
placeholder="新增"
@change="handleChange"
multiple
style="width: 100%"
:options="selectData"
/>
js:
let handles: any = {};
const open = ref(false);
// 在模板中启用 v-insertBtn
const vInsertBtn = {
mounted: (el, binding) => {
handles.el = el;
handles.cancel = (event) => {
if (el.querySelector('.my-btn') || el.querySelector('.ant-cascader-menu-empty')) {
console.log(event.target, 'cancel clicked');
if (event.target !== el.querySelector('.ant-select-selection-overflow')) {
open.value = false;
}
return;
}
};
handles.add = (event) => {
console.log(event.target, 'add');
let div = el.querySelector('.my-btn');
if (event.target == div) {
binding.value();
open.value = false;
}
};
document.removeEventListener('click', handles.cancel);
document.addEventListener('click', handles.cancel);
el.removeEventListener('click', handles.add);
el.addEventListener('click', handles.add);
el.addEventListener('click', () => {
if (el.querySelector('.my-btn') || el.querySelector('.ant-cascader-menu-empty')) {
return;
}
let div: any = document.createElement('div');
let node = document.createTextNode('添加');
div.setAttribute('class', 'my-btn');
div.appendChild(node);
div.style =
'color: #0cad7d;padding:10px;width:100%;text-align:right;border-top:solid 1px #383c44';
el.querySelector('.ant-select-dropdown').appendChild(div);
});
},
beforeUnmount: () => {
handles.el.removeEventListener('click', handles.add);
document.removeEventListener('click', handles.cancel);
},
};
感谢各位同学的耐心观赏,希望这场关于自定义指令与Ant Design Vue级联组件融合的表演,能激发您更多灵感的火花。若您在探索的旅途中发现此策略有所助益,不妨留下一枚赞许,让创意与技术的光芒继续照亮前行的道路。