H5版本-树形多选(treeCheckbox)

父组件引入tree文件调用

<myTree
   :dataTree="dataTree"
    ref="dataTreeRef"
></myTree>
<script>
import myTree from './tree.vue'
components:{myTree}
data() {
    return {
    	//数据结构
    	dataTree:[{
        "id": 1,
        "parentId": null,
        "children": [
            {
                "id": 2,
                "parentId": 1,
                "children": [],
                "name": "戴尔电脑",
                "memo": "",
                "leaf": "2"
            },
            {
                "id": 6,
                "parentId": 1,
                "children": [],
                "name": "联想",
                "memo": "",
                "leaf": "2"
            }
        ],
        "name": "电脑资产",
        "memo": "",
        "leaf": "1"
      }]
    }
   }
</script>

这个是tree.vue文件

<template>
  <div>
    <div
      v-for="(item,index) in tree"
      :key="index"
      class="tree_container"
    >
      <!-- 一级菜单 -->
      <div
        :style="'margin-left:'+ treeListIndex*20+'px'"
        class="tree-item"
      >
        <div class="block_">
          <div
            class="tree-item-name"
            @click="select(item, index)"
          >
            <span class="tree-item-title">{{item.name}}</span>
          </div>
          <template>
            <img
              v-if="item.checked === 1"
              @click="select(item, index)"
              src="@/assets/images/choice.png"
              class="check-box"
            />
            <img
              v-if="item.checked === 0"
              @click="select(item, index)"
              src="@/assets/images/unchoice.png"
              class="check-box"
            />
            <img
              v-if="item.checked === -1"
              @click="select(item, index)"
              src="@/assets/images/unfullChoice.png"
              class="check-box"
            />
          </template>
        </div>
        <div
          class="tree-item-onOff"
          v-if="item.children && item.children.length > 0"
          @click="isOpen(item,index)"
        >
          <van-icon
            name="arrow-down"
            :class="item.open ? 'expand' : 'collapse'"
          />
        </div>
        <div
          class="tree-item-onOff"
          v-else
        >
        </div>
      </div>
      <!-- 二级菜单 -->
      <categoryTree
        v-if="item.children && item.children.length > 0 && item.open"
        :dataTree='item.children'
        :treeListIndex="treeListIndex + 1"
        @backTopComponent="backTopComponent"
      />
    </div>
  </div>
</template>
<script>
export default {
  name: 'categoryTree',
  props: {
    dataTree: {
      type: Array,
      default: () => []
    },
    treeListIndex: { // 当期树形列表的索引
      type: Number,
      default: 0
    },
    isOpenAll: { // 是否展开全部节点
      type: Boolean,
      default: false
    }
  },
  watch: {
    dataTree: {
      handler: function (v) {
        this.tree = JSON.parse(JSON.stringify(v))
      },
      deep: true
    }
  },
  created() {
    // 处理数据
    this.tree = JSON.parse(JSON.stringify(this.packageData(this.dataTree)))
  },
  data() {
    return {
      tree: [],
      allChoiceIdList: [] // 所有选中的id数组
    }
  },
  methods: {
    // 包装数据
    packageData(tree, idx = null) {
      if (this.treeListIndex != 0) return tree
      return tree.map((its, index) => {
        its.open = false
        its.checked = 0
        its.ancestors = idx == null ? String(index) : idx + '-' + index // 路径
        its.children = this.packageData(its.children, its.ancestors)
        return its
      })
    },
    /**
     * 下级开关按钮
     * @param {*} item 
     *    数据对象
     */
    isOpen(item) {
      // 路径
      let ancestors = item.ancestors.split('-')
      if (this.treeListIndex != 0) {
        this.$emit('backTopComponent', ancestors, 'isOpen')
      } else {
        item.open = !item.open
      }
    },

    /**
     * 修改状态
     * @param {*} item 
     *    数据对象
     */
    select(item) {
      // 路径
      let ancestors = item.ancestors.split('-')
      if (this.treeListIndex != 0) {
        this.$emit('backTopComponent', ancestors, 'select')
      } else {
        if (item.checked === 0 || item.checked === -1) item.checked = 1
        else if (item.checked === 1) item.checked = 0
        // 子数据修改状态
        for (let i = 0; i < item.children.length; i++) {
          this.handleChildsData(item.children[i], item.checked)
        }
        // 子数据调用
        this.handleParentData(ancestors)
      }
      this.allChoiceIdList = this.getAllChoiceId(this.tree)

    },
    // 处理子数据
    handleChildsData(item, checked) {
      item.checked = checked
      for (let i = 0; i < item.children.length; i++) {
        this.handleChildsData(item.children[i], checked)
      }
    },
    /**
     * 处理父数据
     *    子数据状态修改后 父数据状态需要联动
     * @param {*} ancestors 
     */
    handleParentData(ancestors) {
      let treeCopy = this.tree
      for (let i = 0; i < (ancestors.length - 1); i++) {
        // 获取操作的对象
        if (i == 0) treeCopy = treeCopy[ancestors[i]]
        else treeCopy = treeCopy.children[ancestors[i]]
        /**
         * 判断是否已选
         *    check: 1  ->  -1
         */
        if (treeCopy.checked == 1) treeCopy.checked = -1
      }
      // 检索所有上级 更新状态
      this.retrievalAllParent(ancestors, ancestors.length - 1)

    },
    /**
      * 检索所有上级 更新状态
      * @param {*} ancestors 
      *    数据路径  ['0', '0', '0'] -> tree[0][0][0]
      * @param {*} leng 
      *    只检索最后一个, 每次调用 leng - 1, 去掉之前检索过的
      */
    retrievalAllParent(ancestors, leng) {
      let lock = false,
        treeCopy = this.tree

      for (let i = 0; i < leng; i++) {
        // 获取操作的对象
        if (i == 0) treeCopy = treeCopy[ancestors[i]]
        else treeCopy = treeCopy.children[ancestors[i]]

        // 判断子数据是否全部未选
        let isNotCheck = !treeCopy.children.some(its => its.checked == 1)
        if (isNotCheck) treeCopy.checked = 0
        else {
          treeCopy.checked = -1
          lock = true
        }

        // 判断子数据是否全部已选
        let isAllCheck = treeCopy.children.filter(its => its.checked == 1 && its)
        if (isAllCheck.length == treeCopy.children.length) {
          treeCopy.checked = 1
          lock = true
        }
      }

      if (lock) this.retrievalAllParent(ancestors, leng - 1)
    },
    /**
     * 回到顶级组件
     * @param {*} ancestors 
     *    数据路径
     * @param {*} methods 
     *    select: 修改状态
     *    isOpen: 下级开关
     */
    backTopComponent(ancestors, methods) {
      if (this.treeListIndex != 0) {
        this.$emit('backTopComponent', ancestors, methods)
      } else {
        // 将路径拼接 获取需要操作的对象
        let treeCopy = this.tree
        for (let i = 0; i < ancestors.length; i++) {
          // 获取操作的对象
          if (i == 0) treeCopy = treeCopy[ancestors[i]]
          else treeCopy = treeCopy.children[ancestors[i]]
        }
        this[methods](treeCopy)
      }

    },
    // 获取所有选中的节点id
    getAllChoiceId(nodes, res = []) {
      for (let i = 0; i < nodes.length; i++) {
        if (nodes[i].checked === 1) {
          var obj = {}
          obj.id = nodes[i].id
          obj.name = nodes[i].name
          res.push(obj)
        }
        if (nodes[i].children && nodes[i].children.length > 0) this.getAllChoiceId(nodes[i].children, res)
      }
      return res
    }
  }
}
</script>
<style scoped>
.tree_container {
  width: auto;
  box-sizing: border-box;
  background: #fff;
  position: relative;
}
.tree-item {
  width: auto;
  box-sizing: border-box;
  padding: 9px 0;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  border-bottom: 1px solid #cdcdcd;
}

.block_ {
  width: 100% !important;
  display: contents;
  margin-right: 30px;
}

.tree-item-name {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  flex: 8;
  width: 100%;
}

.tree-item-title {
  margin-left: 12px;
  color: #1c2438;
  font-size: 16px;
  word-break: break-all;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 200px;
}

.tree-item-onOff {
  width: 20px;
  height: 20px;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-right: 30px;
}

.collapse {
  width: 16px;
  height: 10px;
  transform: rotate(-90deg);
}

.expand {
  width: 16px;
  height: 10px;
}

.check-box {
  height: 16px !important;
  width: 16px !important;
  margin-right: 30px !important;
}

.tree-item-name-select {
  color: #0079fe;
}
</style>

choice.png
unfullChoice.png
unchoice.png

成果图片

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
uni-data-select的多功能可以通过设置v-model为一个数组来实现。在择项发生变化时,所的数据将会保存在v-model所绑定的数组中。根据引用中的示例代码,你可以按照以下步骤来实现多功能: 1. 在data中声明一个数组变量,用于保存所的数据: ```javascript data() { return { formData: { femMerchantsClassificationId: [] // 用于保存多结果的数组 }, range: [] } } ``` 2. 在uni-data-select标签中添加v-model属性,将其绑定到formData中的数组变量上: ```html <uni-data-select v-model="formData.femMerchantsClassificationId" :localdata="range" @change="change"></uni-data-select> ``` 3. 当下拉框内容调用后端接口时,通过遍历后端返回的数据,将每个项的id和名称添加到range数组中: ```javascript success: (res) => { const All = res.data let myrange = [] All.forEach((val, index) => { myrange.push({ value: val.femMerchantsClassificationId, // 项id text: val.femMerchantsClassificationName // 项名称 }) }) this.range = myrange } ``` 现在,你就可以通过择uni-data-select中的项来实现多功能了。所的数据将会保存在formData.femMerchantsClassificationId数组中。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [uni-app多select组件,兼容多平台小程序、H5](https://blog.csdn.net/qq_41887214/article/details/120855815)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [uniapp 下拉框效果使用 uni-data-select标签](https://blog.csdn.net/2301_76882889/article/details/130267707)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值