自定义指令赋能Ant Design Vue级联组件定制新篇

664 阅读3分钟

image.png

在开发者与产品经理的精彩交响曲中,我们常面临着创新与实现的美妙挑战。面对Ant Design Vue的Cascade组件无法直接提供所需插槽的场景,传统解决方案或许指向重新封装组件,但今天,让我们共舞一曲优雅的变奏——利用自定义指令解锁新潜能。

背景

想象一下,您正优化一个界面,其中的级联选择器(Cascade)亟需个性化定制,一个小小的按钮,却因原生支持的缺失而显得遥不可及。 image.png 同学们肯定知道,使用antd vue cascade组件是没有提供对应位置插槽,怎么办?通常情况下我会重写个新组件来解决这个问题,但是这次我提供一个新的解题思路。使用自定义指令的方式来实践下。

下面业务中的展示效果: image.png

四步定制法

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级联组件融合的表演,能激发您更多灵感的火花。若您在探索的旅途中发现此策略有所助益,不妨留下一枚赞许,让创意与技术的光芒继续照亮前行的道路。