vue 自定义指令封装一个手风琴嵌套组件

很多教程的手风琴组件都是一个v-for数组来实现手风琴组件,v-for封装起来很简单,但是我认为并不好。
理由如下:

  • 这种方式很不优雅
  • 其实我没用过这种实现方式的手风琴,不知道到底怎么实现配合router来实现展开和激活?感觉v-for这种实现手风琴的方式实现这个功能不太容易。
  • 无法灵活嵌套

所以自己用自定义指令实现了一个手风琴组件。

代码很长,不想学习的可以直接github下载代码run serve直接使用。

  • 先看效果:

效果图
  • 支持初始化撑开多个折叠版,点击任意一个之后会将其他撑开的都关闭。
  • 初始撑开为props 参数visible=true
  • 后续会更新支持多个不同手风琴,支持开启手风琴模式

结构

    • 边侧导航栏结构


  
    Nejinn
  
  
    Nejinn
  
  
    
      lerity
    
    
      blog
    
  

    • 只有手风琴折叠版的结构
<任意元素 v-nly-accordion.collapseId>


    ...嵌套元素,随意插入


demo:
点击我收起或展开 collapse1
我是折叠版中的元素
点击我收起或展开collapse2
我是折叠版中的元素 使用这种结构的时候,请注意自己写css。可以在accordion.vue中修改就行。

组件目录结构

目录结构.jpg

自定义指令 v-nly-accordion

  • nlyaccordion.js
// nlyaccordion.js
import Vue from "vue";

/**
 * 差集函数
 */
function getDifference(allCollapseId, idKeys) {
  let mixArray = [];
  allCollapseId.forEach(item => {
    if (idKeys.indexOf(item) == -1) {
      mixArray.push(item);
    }
  });
  return mixArray;
}

Vue.directive("nly-accordion", function(el, binding, vnode) {
  /**
   * 初始化指令时监控collapseStatus事件,collapseStatus事件由NLY-accordionNavCollapse组件发出,有2个参数,
   * 一个是NLY-accordionNavCollapse事件props参数id,
   * 一个是NLY-accordionNavCollapse折叠状态show
   * function(a,b)中a是show,b是id
   */
  vnode.context.$root.$on("collapseStatus", function(a, b) {
    // 将所有提交collapseStatus事件的NLY-accordionNavCollapse组件的id放入allCollapseId
    if (allCollapseId.indexOf(b) == -1) {
      allCollapseId.push(b);
    }

    /**
     * 获取当前指令的modifiers,如果当前指令的modifiers中包含提交collapseStatus事件的NLY-accordionNavCollapse组件的id
     * 则初始化挂载指令的组件或者element的class,且修改当前指令modifiers为对应的show值
     * 对应的初始化show为true,则在当前挂载指令的element的class中添加open
     * 对应的初始化show为false,则在当前挂载指令的element的class中移除open
     */
    let idKeys = Object.keys(binding.modifiers);
    if (idKeys.indexOf(b) != -1) {
      binding.modifiers[String(b)] = a;
      if (a) {
        el.classList.add("open");
      } else {
        el.classList.remove("open");
      }
    }
  });

  /**
   * 新建一个array储存组件NLY-accordionNavCollapse的id
   * 注意会先执行这里的代码再执行上面的代码。
   */
  let allCollapseId = [];

  /**
   * 点击事件
   */
  el.onclick = function() {
    // 获取指令的modifiers
    let idKeys = Object.keys(binding.modifiers);
    // 求出modifiers和储存所有id的数组的差集
    let mixArray = getDifference(allCollapseId, idKeys);

    /**
     * 循环当前指令的modifiers,并循环当前挂载指令实例的父组件的所有子组件
     * 以组件的id找出指令对应的组件,执行对应的展开折叠动作
     */
    idKeys.forEach(idKeysItem => {
      vnode.componentInstance.$parent.$children.forEach(childrenItem => {
        if (childrenItem.id == idKeysItem) {
          if (binding.modifiers[idKeysItem]) {
            childrenItem.show = false;
            el.classList.remove("open");
          } else {
            childrenItem.show = true;
            el.classList.add("open");
          }
        }
      });
    });
    /**
     * 判断当前指令的modifiers是否有对应的NLY-accordionNavCollapse组件
     * 如果有就执行关闭其他NLY-accordionNavCollapse组件的动作,如果没有,就不进行操作
     */
    idKeys.forEach(idKeysItem => {
      if (allCollapseId.indexOf(idKeysItem) != -1) {
        mixArray.forEach(mixArrayItem => {
          vnode.componentInstance.$parent.$children.forEach(childrenItem => {
            if (childrenItem.id == mixArrayItem) {
              childrenItem.show = false;
              el.classList.remove("open");
            }
          });
        });
      }
    });
  };
});

动画过渡组件

  • 动画过渡组件是借鉴element-ui的,但是说实话,我不是很喜欢这个。
  • collapse动画在NLY-accordionNavCollapse组件中引入

NLY-accordionNavItem

// AccordionNavItem.vue



NLY-accordionNavTree

// AccordionNavTree.vue



NLY-accordionNavCollapse

// AccordionNavCollapse.vue



注册组件

  • index.js
import AccordionNav from "./AccordionNav.vue";
import AccordionNavItem from "./AccordionNavItem.vue";
import AccordionNavTree from "./AccordionNavTree.vue";
import AccordionNavCollapse from "./AccordionNavCollapse.vue";

export default {
  install: Vue => {
    Vue.component("NLY-accordionNav", AccordionNav);
    Vue.component("NLY-accordionNavItem", AccordionNavItem);
    Vue.component("NLY-accordionNavTree", AccordionNavTree);
    Vue.component("NLY-accordionNavCollapse", AccordionNavCollapse);
  }
};

全局注册指令和组件

  • main.js
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";

Vue.config.productionTip = false;

// 自定义图标,阿里巴巴矢量图标库
import "./assets/nlyblogfont/iconfont.css";

// 全局注册组件
import NLYblog from "./nlyaccordion";
Vue.use(NLYblog);

// 全局注册指令
import "./nlyaccordion/nlyaccordion.js";

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app");

Less

  • 请把less放到app.vue或者手风琴的父组件中
  • 也可以编译成css,然后再引入

Demo






这时候运行就可以看到效果图的大手风琴折叠板。

你可能感兴趣的:(vue 自定义指令封装一个手风琴嵌套组件)