首页 > 其他分享 >ant.design-vue 级联多选组件

ant.design-vue 级联多选组件

时间:2022-10-21 14:11:05浏览次数:78  
标签:vue const treeDataListClone treeDataList childrenTag ant item design currentItem

取自doit-ui-web  ,基于ant.design封装的功能组件,

实现多选级联样式表功能,根据自身情况 引入到自己的项目中

 

<template>
  <div
    class="gb-ant-select-multiple-cascader"
    :class="{ 'multiple-cascader-outside': !isClickOutSide }"
    v-clickoutside="handleDropDownOutSide"
  >
    <a-select
      v-bind="$attrs"
      v-on="$listeners"
      mode="multiple"
      :maxTagTextLength="maxTagTextLength"
      :maxTagCount="maxTagCount"
      style="width:100%"
      :placeholder="placeholder"
      :value="selectedValueArr"
      :notFoundContent="null"
      :open="isOpen"
      showArrow
      :allowClear="allowClear"
      optionLabelProp="text"
      :dropdownMatchSelectWidth="false"
      :getPopupContainer="tirggerNode => tirggerNode.parentNode"
      @focus="handleFocus"
      @deselect="handleDeselect"
    >
      <a-icon
        type="close-circle"
        slot="clearIcon"
        theme="filled"
        @click="handleClearAll"
      />
      <template slot="dropdownRender" slot-scope="menu">
        <v-nodes :vnodes="menu" />
        <div :class="dropdownClassName">
          <div class="cascader-not-content" v-if="treeDataList.length <= 0">
            <a-empty :image="simpleImage" :description="noDataText"></a-empty>
          </div>
          <div class="cascader-content-wrap" v-else>
            <div class="cascader-content-container">
              <ul
                class="cascader-content-list"
                v-for="(itemList, levelIndex) in treeDataList"
                :key="levelIndex"
              >
                <!-- <li
                  class="cascader-content-item cascader-content-item-check-all"
                  @click="handleCascaderAllItemClick(isCheckAll)"
                  v-if="levelIndex === 0"
                >
                  <a-checkbox
                    :indeterminate="isIndeterminateAll"
                    v-model="isCheckAll"
                  />
                  <span class="checkbox-text">{{ allText }}</span>
                </li> -->
                <li
                  class="cascader-content-item"
                  v-for="(subItem, subIndex) in itemList"
                  :key="subItem[selectOptionsConfig.key]"
                  :title="subItem[selectOptionsConfig.text]"
                  :class="{ 'cascader-content-item-active': subItem.$active }"
                  @click="
                    handleCascaderItemClick(subItem, subIndex, levelIndex)
                  "
                >
                  <a-checkbox
                    :indeterminate="!!subItem.$indeterminate"
                    :checked="!!subItem.$checked"
                    @click.self="
                      e => handleCheckClick(e, subItem, subIndex, levelIndex)
                    "
                  />
                  <span class="checkbox-text">{{
                    subItem[selectOptionsConfig.text]
                  }}</span>
                  <a-icon
                    type="right"
                    style="font-size:10px;"
                    v-if="
                      subItem[selectOptionsConfig.children] &&
                        subItem[selectOptionsConfig.children].length > 0
                    "
                  />
                </li>
              </ul>
            </div>
          </div>
        </div>
      </template>
      <a-select-option
        v-for="item in treeToList"
        :key="item[selectOptionsConfig.key]"
        :value="item[selectOptionsConfig.value]"
        :text="item[selectOptionsConfig.text]"
      >
        {{ item[selectOptionsConfig.text] }}
      </a-select-option>
    </a-select>
  </div>
</template>

<script>
import { Empty } from "ant-design-vue";
import { cloneDeep } from "lodash";
const clickoutside = {
  // 初始化指令
  bind(el, binding) {
    function documentHandler(e) {
      if (el.contains(e.target)) {
        return false;
      }
      if (binding.expression) {
        binding.value(e);
      }
    }
    el.__vueClickOutside__ = documentHandler;
    document.addEventListener("click", documentHandler);
  },
  update() {},
  unbind(el) {
    document.removeEventListener("click", el.__vueClickOutside__);
    delete el.__vueClickOutside__;
  }
};
export default {
  name: "gbAntSelectMultipleCascader",
  components: {
    VNodes: {
      functional: true,
      render: (h, ctx) => {
        return ctx.props.vnodes;
      }
    }
  },
  directives: { clickoutside },
  props: {
    allowClear: {
      type: Boolean,
      default: true
    },
    allText: {
      type: String,
      default: "全部"
    },
    noDataText: {
      type: String,
      default: "暂无数据"
    },
    treeData: {
      type: Array,
      default: () => {
        return [];
      }
    },
    dropdownClassName: {
      type: String
    },
    selectOptionsConfig: {
      type: Object,
      default: () => {
        return {
          key: "id",
          value: "value",
          text: "title",
          children: "children"
        };
      }
    },
    maxTagCount: {
      type: Number
    },
    maxTagTextLength: {
      type: Number
    },
    placeholder: {
      type: String,
      default: "请搜索"
    },
    checkAll: {
      type: Boolean,
      default: false
    },
    value: {
      type: Array
    }
  },
  beforeCreate() {
    this.simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
  },
  destroyed() {
    this.timer && clearInterval(this.timer);
  },
  data() {
    return {
      isOpen: false,
      isCheckAll: this.checkAll,
      isIndeterminateAll: false,
      isClickOutSide: true,
      selectedValueArr: this.value || undefined,
      treeDataList: cloneDeep(this.treeData),
      treeToList: []
    };
  },
  watch: {
    checkAll: {
      handler(newVal) {
        if (newVal) {
          this.timer = setInterval(() => {
            if (this.treeDataList.length > 0) {
              clearInterval(this.timer);
              this.handleCascaderAllItemClick(!newVal);
            }
          }, 30);
        }
      },
      immediate: true
    },
    value: {
      handler(newVal) {
        this.selectedValueArr = newVal;
        this.initDefaultKeys(newVal, this.treeDataList);
        return newVal;
      },
      immediate: true
    },
    treeData(newVal) {
      this.treeListToArrList(newVal[0]);
      this.initDefaultKeys(this.selectedValueArr, newVal);
      this.treeDataList = newVal;
      return newVal;
    }
  },
  methods: {
    handleClearAll() {
      this.treeDataList = this.treeData;
      this.isOpen = false;
      this.isCheckAll = false;
      this.isClickOutSide = true;
      this.isIndeterminateAll = false;
      this.updatePostData();
    },
    initDefaultKeys(selectedArr, treeDataList) {
      if (
        selectedArr &&
        selectedArr.length > 0 &&
        treeDataList &&
        treeDataList.length > 0
      ) {
        const treeDataListClone = cloneDeep(treeDataList);
        const treeToListClone = cloneDeep(this.treeToList);
        selectedArr.forEach(item => {
          const curretnItemList = [];
          this.findKeysItem(treeDataListClone[0], item, curretnItemList);
          let currentItem = {};
          if (curretnItemList.length > 0) {
            currentItem = curretnItemList[0];
          }
          if (
            !treeToListClone.find(
              listItem =>
                listItem[this.selectOptionsConfig.value] ===
                currentItem[this.selectOptionsConfig.value]
            )
          ) {
            treeToListClone.push(currentItem);
          }
          currentItem.$checked = true;
          if (
            currentItem[this.selectOptionsConfig.children] &&
            currentItem[this.selectOptionsConfig.children].length > 0
          ) {
            this.setAllDataTreeChecked(
              currentItem[this.selectOptionsConfig.children],
              true
            );
          }
        });
        this.setCheckBoxIndeteriminate(treeDataListClone[0]);
        this.$nextTick(() => {
          this.treeToList = treeToListClone;
          this.treeDataList = treeDataListClone;
        });
      }
    },
    handleCascaderAllItemClick(checkAll) {
      const treeDataListClone = cloneDeep(this.treeDataList);
      // 触发事件的时候v-model中的数据还没有改变,所以要取反
      this.setAllDataTreeChecked(treeDataListClone[0], !checkAll);
      this.clearAllDataActive(treeDataListClone[0]);
      this.setCheckBoxIndeteriminate(treeDataListClone[0]);
      this.treeDataList = treeDataListClone;
      this.updatePostData();
    },
    handleDeselect(value) {
      const treeDataListClone = cloneDeep(this.treeDataList);
      const currentItemList = [];
      this.findKeysItem(treeDataListClone[0], value, currentItemList);
      let currentItem = {};
      if (currentItemList.length > 0) {
        currentItem = currentItemList[0];
      }
      currentItem.$checked = false;
      currentItem.$active = false;
      if (
        currentItem[this.selectOptionsConfig.children] &&
        currentItem[this.selectOptionsConfig.children].length > 0
      ) {
        this.setAllDataTreeChecked(
          currentItem[this.selectOptionsConfig.children],
          false
        );
      }
      this.setCheckBoxIndeteriminate(treeDataListClone[0]);
      this.treeDataList = treeDataListClone;
      this.updatePostData();
    },
    handleCheckClick(e, item, index, levelIndex) {
      const treeDataListClone = cloneDeep(this.treeDataList);
      const childrenTag = this.selectOptionsConfig.children;
      const valueTag = this.selectOptionsConfig.value;
      const currentItemList = [];
      this.findKeysItem(treeDataListClone[0], item[valueTag], currentItemList);
      let currentItem = {};
      if (currentItemList.length > 0) {
        currentItem = currentItemList[0];
      }
      e.stopPropagation();
      e.cancelBubble = true;
      const checked = e.target.checked;
      if (!currentItem.$active && levelIndex !== treeDataListClone.length - 1) {
        // 清空所有选中状态
        this.clearAllDataActive(treeDataListClone[0]);
      }
      if (currentItem[childrenTag] && currentItem[childrenTag].length > 0) {
        // 可展开
        if (!currentItem.$active) {
          const nextIndex = levelIndex + 1;
          if (treeDataListClone[nextIndex]) {
            treeDataListClone.splice(nextIndex);
          }
        }
      } else {
        // 不可展开
        const nextIndex = levelIndex + 1;
        if (treeDataListClone[nextIndex]) {
          treeDataListClone.splice(nextIndex);
        }
      }
      if (currentItem[childrenTag] && currentItem[childrenTag].length > 0) {
        this.setAllDataTreeChecked(currentItem[childrenTag], checked);
      } else {
        currentItem.$checked = checked;
      }
      this.setCheckBoxIndeteriminate(treeDataListClone[0]);
      this.treeDataList = treeDataListClone;
      this.$emit("handleCheckboxChange", {
        event: e,
        item,
        index,
        levelIndex
      });
      this.updatePostData();
    },
    // 单个item点击事件
    handleCascaderItemClick(item, index, levelIndex) {
      const treeDataListClone = cloneDeep(this.treeDataList);
      const childrenTag = this.selectOptionsConfig.children;
      const valueTag = this.selectOptionsConfig.value;
      const currentItemList = [];
      this.findKeysItem(treeDataListClone[0], item[valueTag], currentItemList);
      let currentItem = {};
      if (currentItemList.length > 0) {
        currentItem = currentItemList[0];
      }
      const parentItemsList = [];
      this.findAllParentItems(
        currentItem[valueTag],
        treeDataListClone[0],
        treeDataListClone[0],
        parentItemsList
      );
      parentItemsList.push(currentItem);
      this.clearAllDataActive(treeDataListClone[0]);
      parentItemsList.forEach(activeItem => {
        if (activeItem[childrenTag] && activeItem[childrenTag].length > 0) {
          activeItem.$active = true;
        }
      });
      if (currentItem[childrenTag] && currentItem[childrenTag].length > 0) {
        const nextIndex = levelIndex + 1;
        if (treeDataListClone[nextIndex]) {
          treeDataListClone.splice(nextIndex);
          treeDataListClone[nextIndex] = currentItem[childrenTag];
        } else {
          treeDataListClone.push(currentItem[childrenTag]);
        }
        this.addHideSelectedList(currentItem[childrenTag]);
      } else {
        const nextIndex = levelIndex + 1;
        if (treeDataListClone[nextIndex]) {
          treeDataListClone.splice(nextIndex);
        }
        currentItem.$checked = !currentItem.$checked;
      }
      this.setCheckBoxIndeteriminate(treeDataListClone[0]);
      this.treeDataList = treeDataListClone;
      this.updatePostData();
    },
    handleFocus() {
      this.isOpen = true;
      this.isClickOutSide = false;
    },
    updatePostData() {
      const childrenTag = this.selectOptionsConfig.children;
      const valueTag = this.selectOptionsConfig.value;
      let selectedValueArr = [];
      _getAllItemChecked(this.treeDataList[0]);
      this.$emit("change", selectedValueArr);
      function _getAllItemChecked(treeDataList) {
        let parentItem;
        treeDataList.forEach(item => {
          if (item[childrenTag] && item[childrenTag].length > 0) {
            parentItem = item;
            const currentCheckedAll = [];
            const currentCheckedAllItems = [];
            _getSubChecked(parentItem[childrenTag], currentCheckedAll);
            _getSubCheckedItems(
              parentItem[childrenTag],
              currentCheckedAllItems
            );
            if (currentCheckedAll.every(checkItem => checkItem === true)) {
              if (parentItem[valueTag]) {
                selectedValueArr.push(parentItem[valueTag]);
              }
            } else if (
              currentCheckedAll.some(checkItem => checkItem === true)
            ) {
              currentCheckedAllItems.forEach(selectedItem => {
                if (selectedItem[valueTag]) {
                  selectedValueArr.push(selectedItem[valueTag]);
                }
              });
            }
          } else {
            if (item.$checked) {
              selectedValueArr.push(item[valueTag]);
            }
          }
        });
      }
      function _getSubChecked(itemList, arr) {
        itemList.forEach(item => {
          arr.push(!!item.$checked);
          if (item[childrenTag] && item[childrenTag].length > 0) {
            _getSubChecked(item[childrenTag], arr);
          }
        });
      }
      function _getSubCheckedItems(itemList, arr) {
        itemList.forEach(item => {
          if (item.$checked) {
            arr.push(item);
          } else {
            if (item[childrenTag] && item[childrenTag].length > 0) {
              _getSubCheckedItems(item[childrenTag], arr);
            }
          }
        });
      }
    },
    // 点击组件外部区域
    handleDropDownOutSide() {
      this.isOpen = false;
      this.isClickOutSide = true;
    },
    // 设置当前的选中框的全选等状态
    setCheckBoxIndeteriminate(treeDataList) {
      const childrenTag = this.selectOptionsConfig.children;
      _getAllItemChecked(treeDataList);
      const checkedAllArr = [];
      _getAllItemCheckedAll(treeDataList, checkedAllArr);
      if (checkedAllArr.every(item => item === true)) {
        this.isCheckAll = true;
        this.isIndeterminateAll = false;
      } else if (checkedAllArr.some(item => item === true)) {
        this.isCheckAll = false;
        this.isIndeterminateAll = true;
      } else if (checkedAllArr.every(item => item === false)) {
        this.isCheckAll = false;
        this.isIndeterminateAll = false;
      }
      function _getAllItemCheckedAll(itemList, arr) {
        itemList.forEach(item => {
          arr.push(!!item.$checked);
          if (item[childrenTag] && item[childrenTag].length > 0) {
            _getAllItemCheckedAll(item[childrenTag], arr);
          }
        });
      }
      function _getAllItemChecked(treeDataList) {
        let parentItem;
        treeDataList.forEach(item => {
          if (item[childrenTag] && item[childrenTag].length > 0) {
            parentItem = item;
            _getAllItemChecked(item[childrenTag]);
            const currentCheckedAll = [];
            _getSubChecked(parentItem[childrenTag], currentCheckedAll);
            if (currentCheckedAll.every(checkItem => checkItem === true)) {
              parentItem.$checked = true;
              parentItem.$indeterminate = false;
            } else if (
              currentCheckedAll.some(checkItem => checkItem === true)
            ) {
              parentItem.$checked = false;
              parentItem.$indeterminate = true;
            } else if (
              currentCheckedAll.every(checkItem => checkItem === false)
            ) {
              parentItem.$checked = false;
              parentItem.$indeterminate = false;
            }
          }
        });
      }
      function _getSubChecked(itemList, arr) {
        itemList.forEach(item => {
          arr.push(!!item.$checked);
          if (item[childrenTag] && item[childrenTag].length > 0) {
            _getSubChecked(item[childrenTag], arr);
          }
        });
      }
    },
    // 设置所有选中和非选中
    setAllDataTreeChecked(treeList, checked) {
      const childrenTag = this.selectOptionsConfig.children;
      treeList.forEach(item => {
        item.$checked = checked;
        if (item[childrenTag] && item[childrenTag].length > 0) {
          this.setAllDataTreeChecked(item[childrenTag], checked);
        }
      });
    },
    // 清空所有的active状态
    clearAllDataActive(treeList) {
      const childrenTag = this.selectOptionsConfig.children;
      treeList.forEach(item => {
        item.$active = false;
        if (item[childrenTag] && item[childrenTag].length > 0) {
          this.clearAllDataActive(item[childrenTag]);
        }
      });
    },
    findKeysItem(treeDataList, currentKey, currentItem) {
      const childrenTag = this.selectOptionsConfig.children;
      treeDataList.forEach(item => {
        if (item[this.selectOptionsConfig.value] === currentKey) {
          currentItem.push(item);
        } else {
          if (item[childrenTag] && item[childrenTag].length > 0) {
            this.findKeysItem(item[childrenTag], currentKey, currentItem);
          }
        }
      });
    },
    findAllParentItems(
      currentKey,
      treeDataList,
      originTreeDataList,
      parentItemsList,
      parentItem,
      parentItemsHasKey
    ) {
      if (parentItem && parentItemsHasKey) {
        parentItemsList.unshift(parentItem);
        this.findAllParentItems(
          parentItem[this.selectOptionsConfig.value],
          treeDataList,
          originTreeDataList,
          parentItemsList
        );
      } else {
        this.findKeysAndParentItem(
          currentKey,
          treeDataList,
          originTreeDataList,
          parentItemsList,
          parentItem,
          parentItemsHasKey
        );
      }
    },
    findKeysAndParentItem(
      currentKey,
      treeDataList,
      originTreeDataList,
      parentItemsList,
      parentItem
    ) {
      const childrenTag = this.selectOptionsConfig.children;
      treeDataList.forEach(item => {
        if (item[this.selectOptionsConfig.value] === currentKey && parentItem) {
          this.findAllParentItems(
            parentItem[this.selectOptionsConfig.value],
            originTreeDataList,
            originTreeDataList,
            parentItemsList,
            parentItem,
            true
          );
        } else {
          if (item[childrenTag] && item[childrenTag].length > 0) {
            this.findKeysAndParentItem(
              currentKey,
              item[childrenTag],
              originTreeDataList,
              parentItemsList,
              item
            );
          }
        }
      });
    },
    // 将树形结构数据转换成平面结构
    treeListToArrList(treeList) {
      const arrList = [];
      if (treeList && treeList[0]) {
        treeList.forEach(item => {
          arrList.push(item);
        });
        this.treeToList = arrList;
      }
    },
    addHideSelectedList(treeList) {
      const treeToListClone = Array.from(this.treeToList);
      treeList.forEach(item => {
        if (
          !treeToListClone.find(
            listItem =>
              listItem[this.selectOptionsConfig.value] ===
              item[this.selectOptionsConfig.value]
          )
        ) {
          treeToListClone.push(item);
        }
      });
      this.treeToList = treeToListClone;
    }
  }
};
</script>
<style lang="less">
.gb-ant-select-multiple-cascader {
  .ant-select-dropdown-menu-item {
    display: none;
  }
  .cascader-content-item {
    .checkbox-text {
      padding-left: 8px;
      font-size: 12px;
      color: #4a4a4a;
      cursor: default;
      flex: 1;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      text-align: left;
    }
    &.cascader-content-item-active {
      background-color: rgba(66, 70, 86, 0.05);
      .checkbox-text {
        color: #1d47aa;
      }
    }
    .ant-checkbox-wrapper {
      .ant-checkbox-indeterminate {
        .ant-checkbox-inner {
          &:after {
            background-color: #1d47aa;
            left: 48%;
          }
        }
      }
      .ant-checkbox-inner {
        width: 14px;
        height: 14px;
        background-color: #fff;
        border-color: #7d8292;
        &:after {
          border-color: #1d47aa;
          left: 20%;
        }
      }
    }
  }
  .ant-select-dropdown--multiple {
    min-width: auto !important;
  }
  &.multiple-cascader-outside {
    .ant-select-arrow {
      transform: rotate(180deg);
    }
  }
  .ant-select-arrow {
    margin-top: -8px;
    transition: transform 0.3s;
  }
  .ant-select-dropdown-menu-root {
    display: none;
  }
  .cascader-content-right {
    .right-wrap {
      .ant-checkbox {
        top: 0;
      }
    }
  }
}
</style>
<style lang="less" scoped>
.gb-ant-select-multiple-cascader {
  position: relative;
  .cascader-content-wrap {
    height: 100%;
    .cascader-content-container {
      display: flex;
      flex-direction: row;
      .cascader-content-list {
        list-style: none;
        padding: 0;
        margin: 0;
        width: 153px;
        box-sizing: border-box;
        padding: 11px 0;
        max-height: 160px;
        overflow-y: auto;
        border-right: 1px solid rgba(222, 223, 226, 0.3);
        .cascader-content-item {
          padding: 5px 16px;
          margin-top: 5px;
          display: flex;
          flex-direction: row;
          align-items: center;
          line-height: 1;
          &:hover {
            background-color: rgba(66, 70, 86, 0.05);
          }
        }
      }
    }
  }
  .cascader-not-content {
    padding: 10px;
  }
}
</style>

 

标签:vue,const,treeDataListClone,treeDataList,childrenTag,ant,item,design,currentItem
From: https://www.cnblogs.com/bomdeyada/p/16813252.html

相关文章

  • 微信小程序基于vant和springboot实现附件上传和预览
    前言图片上传和预览在移动端应用非常广泛和频繁,vant组件库van-uploader组件已经帮我们实现了大部分功能,但是在系统中频繁使用还是有点麻烦,我们根据自身的业务系统重新封装了......
  • vue 处理打包后加载慢的问题
    在开发项目时引入过多的插件时,容易导致项目打包后chunk-vendors.js文件过大,导致浏览器加载慢的问题,目前有两种解决方式方式一:由于:chunk-vendors文件过大、js没有压缩、服......
  • vue项目封装api接口
    前言:vue的axios基于promise的http库,可以运行在浏览器和node环境中一:api封装文件request..jsimport'axios'from'axios'//引用axios插件 npminstallaxios--s//创......
  • Vue中使用Switch开关用来控制商品的上架与下架情况、同时根据数据库商品的状态反应到
    一般后台对商品的信息管理、包含商品的上架与下架。为了提高用户的体验、将商品上下架的操作做成开关的形式。同时后台数据库中保存的商品状态能够根据开关状态改变。1......
  • Vue虚拟dom是如何被创建的
    先来看生成虚拟dom的入口文件:...import{parse}from'./parser/index'import{optimize}from'./optimizer'import{generate}from'./codegen/inde......
  • Vue3快速上手
    Vue3快速上手1.Vue3简介2020年9月18日,Vue.js发布3.0版本,代号:OnePiece(海贼王)耗时2年多、2600+次提交、30+个RFC、600+次PR、99位贡献者github上的tags地址:https://gi......
  • 基于tauri+vue3.x多开窗口|Tauri创建多窗体实践
    最近一种在捣鼓Tauri集成Vue3技术开发桌面端应用实践,tauri实现创建多窗口,窗口之间通讯功能。开始正文之前,先来了解下tauri结合vue3.js快速创建项目。tauri在......
  • danted
    #日志文件存放位置logoutput:syslogstdout/var/log/sockd/sockd.log#配置网络internal:eth0port=1080external:eth0#配置服务器身份user.privileged:r......
  • vue3使用echarts插件并实现点击下载图表功能
    接到一个新的需求,就是用vue3制作一幅世界地图,并实现点击下载按钮将图表转变为图片下载到本地。使用插件: html2canvasnpm安装: npminstallhtml2canvas组件引入: import......
  • Vue和后端服务进行数据交互
    1、安装npminstallaxios--save2、在main.js文件引入importAxiosfrom'axios';//后台交互Vue.prototype.$http=Axios//defaults设置全局默认路径Axios.defaults.ba......