相信大家对于下面的列表形式应该不陌生吧,至少我个人在后台OA系统的时候就用到了,那么我来聊下这样形式的列表,应该怎么封装成为一个公共的组件。(代码参考了iview用于个人学习之用)

一.列表组件参数设置
每一个公共组件都应该约定好参数,定义好回调函数所传递参数。不应该与业务组件和业务逻辑相互耦合。
这个组件的约定参数和使用方法如下:

	<Collapse v-model="value1" @on-change="changFun">
      <Panel name="1">
        <span>标题1</span>
        <p slot="content">标题1的内容</p>
      </Panel>
      <Panel name="2">
        <span>标题2</span>
        <p slot="content">标题2的内容</p>
      </Panel>
      <Panel name="3">
        <span>标题3</span>
        <p slot="content">标题3的内容</p>
      </Panel>
      <Panel name="4">
        <span>标题4</span>
        <p slot="content">标题4的内容</p>
      </Panel>
    </Collapse>

Collapse参数

属性说明
value激活面板的name
accordion是否开启手风琴模式

Collaspe事件

事件名说明
on-change返回面板的key,格式为数组

Panel参数

属性说明
name对应面板的name

二.建立文件夹存放Collapse和Panel组件
在这里插入图片描述
在src/components下分别建立collaspse.vue,panel.vue和index.js
index.js

import Panel from './panel.vue'
import Collapse from './collapse'
Collapse.Panel = Panel
export default Collapse

在main.js中
在这里插入图片描述
这里在main.js中导入,全局注册组件,那么我们就不需要在某个vue文件中单独引入。
Collapse.vue

<template>
  <div :class="classes">
    <slot></slot>
  </div>
</template>

<script>
const prefixCls = "ka-collapse";
export default {
  name: "collapse",
  props: {
    // 是否是手风琴模式
    accordion: {
      type: Boolean,
      default: false
    },
    // 传递进来的值
    value: [Array, String]
  },
  computed: {
    classes() {
      // `${prefixCls-simple}` 是变量需要添加[]
      return [`${prefixCls}`];
    }
  },
  data() {
    return {
      currentValue: this.value // 传递进来当前的值
    };
  },
  mounted() {
    this.setActive();
  },
  methods: {
    setActive() {
      // 为它下面的子元素都设置一个index值
      // console.log("setActive");
      const activeKey = this.getActiveKey();
      this.$children.forEach((child, index) => {
        const name = child.name || index.toString(); // toString 1=>"1"整数转换成为字符串
        child.isActive = activeKey.indexOf(name) > -1; // 给选中的元素赋值活跃状态
        // console.log(child);
        child.index = index;
      });
    },
    toggle(data) {
      // console.log("toggle");
      const name = data.name.toString(); // 强行转换成为字符串
      let newActivekey = [];
      if (this.accordion) {
        // 如果是手风琴模式
        if (!data.isActive) {
          newActivekey.push(name);
        }
      } else {
        let activeKey = this.getActiveKey();
        const nameIndex = activeKey.indexOf(name);
        if (data.isActive) {
          // 如果当前是展开状态
          if (nameIndex > -1) {
            activeKey.splice(nameIndex, 1);
          }
        } else {
          if (nameIndex < 0) {
            activeKey.push(name);
          }
        }
        newActivekey = activeKey;
      }
      this.currentValue = newActivekey;
      // console.log(data);
      this.$emit("input", newActivekey);
      this.$emit("on-change", newActivekey);
    },
    getActiveKey() {
      // 获取当前展开的元素,并且做成数组的形式 1 => ["1"]
      let activeKey = this.currentValue || [];
      const accordion = this.accordion;
      if (!Array.isArray(activeKey)) {
        // 判断 activeKey 是不是数组
        activeKey = [activeKey]; // 不是数组则让它变成数组
      }
      if (accordion && activeKey.length > 1) {
        // 如果是手风琴模式,必定是只会有一个元素
        activeKey = [activeKey[0]];
      }

      for (let i = 0; i < activeKey.length; i++) {
        activeKey[i] = activeKey[i].toString();
      }
      return activeKey;
    }
  },
  watch: {
    value(val) {
      console.log(val);
      this.currentValue = val;
    },
    currentValue() {
      console.log("currentValue");
      this.setActive();
    }
  }
};
</script>

<style lang="scss" scoped>
// border-top: 1px solid #dcdee2;
</style>

panel.vue

<template>
  <div :class="itemClasses">
    <div :class="headerClasses" @click="toggle">
      <Icon type="icon-right" v-if="!hideArrow"></Icon>
      <slot></slot>
    </div>
    <div :class="contentClass" v-show="isActive">
      <div :class="boxClasses">
        <slot name="content"></slot>
      </div>
    </div>
  </div>
</template>

<script>
const prefixCls = "ka-collapse";
export default {
  name: "Panel",
  data() {
    return {
      index: 0,
      isActive: false
    };
  },
  props: {
    name: {
      type: String
    },
    hideArrow: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    itemClasses() {
      return [
        `${prefixCls}-item`,
        {
          [`${prefixCls}-item-active`]: this.isActive
        }
      ];
    },
    // 定义header样式
    headerClasses() {
      return `${prefixCls}-header`;
    },
    contentClass() {
      return `${prefixCls}-content`;
    },
    boxClasses() {
      return `${prefixCls}-content-box`;
    }
  },
  methods: {
    toggle() {
      // console.log(this);
      this.$parent.toggle({
        name: this.name || this.index,
        isActive: this.isActive
      });
    }
  }
};
</script>

<style lang="scss" scoped>
.ka-collapse-header {
  color: red;
}
</style>

这个组件大概的思路为,在panel子组件直接触发父组件的tooggle,这样做的好处就是可以把当前点击panel对象的状态传递到父组件,有父组件去完成切换的逻辑。
最后给上github地址:
https://github.com/whenTheMorningDark/workinteresting/tree/master/src/components/collapse

Logo

前往低代码交流专区

更多推荐