没有复选框效果,用2.72以前的版本的话,是根本没有展开树形数据的效果,也没有复选框效果,

        需求:在2.72以下的老版本上做一个树形展示的效果,并且还要有复选框

        分享下从查找资料到解决问题的心酸之路,如果着急要效果代码的,请直接看最后一步!!!

需求效果图:

element 树形表格文字缩进 element树形表格卡_element

调研之路: 

element 2.72以上的版本效果与入口:点我

如下所示: 只有树形的效果,如果想要复选框效果,点我进入

element 树形表格文字缩进 element树形表格卡_element 树形表格文字缩进_02

 element 2.5.4的版本效果与入口:点我

注意:只能支持一个展开行类似的效果,没有复选框

element 树形表格文字缩进 element树形表格卡_element_03

 解决具体步骤:

1、思路:前提  只有展开行的效果

1、我们要给他加一个复选框的效果

2、我们要在复选框加一个icon  图标来控制他的展开与关闭

3、我们既要实现效果,有不能展示它默认的这个展示开关(因为他每一行都有,不满足树形的要求,我们只要求有子级的才可以有开关)

2、添加复选框的效果

        因为是自定义的,所以我们使用element的header插槽,保证只有一个,这个复选框是总复选框,然后再是每一行的复选框, 用  checks  字段来控制他的复选框是否勾选,这个字段是我们自己加在数据里的。

<el-table-column width="80">
      <template slot="header" slot-scope="scope">
        <el-checkbox
          v-model="allCheckout"
          style="padding-left: 10px"
          @change="checkAllFun()"
        />
      </template>
      <template slot-scope="scope">
        <div style="position: relative">
          <el-checkbox
            v-model="scope.row.checks"
            style="padding-left: 10px"
            @change="checkChange(scope.row)"
          />
        </div>
      </template>
    </el-table-column>

总全选的事件操作:点击总全选,所有的数据都跟着改变

data:
    allCheckout:false

methods:    
// 总全选点击事件
      checkAllFun() {
        this.tableData.forEach(items => {
          this.$set(items, "checks", this.allCheckout);
          if (items.children) {
            items.children.forEach((item) => {
              this.$set(item, "checks", this.allCheckout);
            });
          }
        })
      },

单个复选框的点击事件

单个复选框的点击逻辑相对多分为三种:

1、一级复选框无子级的点击

2、一级复选框有子级的点击

3、二级复选框无子级的点击

以及他们三个点击后,对总复选框的影响

//插槽复选框逻辑
      checkChange(row) {
        // expend 父级复选框
        if (row.children) {
          if (row.checks) {
            row.children.map((item) => {
              this.$set(item, "checks", true);
            });
            this.$set(row, "checks", true);
          } else {
            row.children.map((item) => {
              this.$set(item, "checks", false);
            });
            this.$set(row, "checks", false);
          }
        }else{
          // expend 子级复选框
          if(row.checks == undefined) row.checks = false
          this.$set(row, "checks", row.checks)
          const findTypeArr = this.tableData.filter(v=>v.id == row.id)
          let jb_type = ''
          if(findTypeArr.length>0){
            jb_type = '1'
          }else{
            jb_type = '2'
          }
          // 一级的操作
          if(jb_type == '1' && !row.checks){
            this.allCheckout = false
          }
          if(jb_type == '1' && row.checks){
            this.allCheckSetFun()
          }
          // 二级操作
          if(jb_type == '2' && !row.checks){
            this.allCheckout = false
            this.tableData.forEach(one => {
              if (one.children && one.children.length > 0) {
                one.children.forEach(two => {
                  if (two.id == row.id) {
                    this.$set(one, "checks", false)
                  }
                })
              }
            })
          }
          if(jb_type == '2' && row.checks){
            this.tableData.forEach(one => {
              if (one.children && one.children.length > 0) {
                one.children.forEach(two => {
                  if (two.id == row.id) {
                    let twoSelArr = one.children.filter(v=>v.checks)
                    if(twoSelArr.length == one.children.length){
                      this.$set(one, "checks", true)
                    }else{
                      this.$set(one, "checks", false)
                    }
                  }
                })
              }
            })
          }
        }
        // 刷新外部总全选状态
        this.allCheckSetFun()
        // 刷新
        this.uploadTable = false
        this.$nextTick(()=>{
          this.uploadTable = true
        })

      },
      // 修改外部总勾选
      allCheckSetFun(){
        let oneSelArr = this.tableData.filter(v=>v.checks)
        if(oneSelArr.length == this.tableData.length){
          this.allCheckout = true
        }else{
          this.allCheckout = false
        }
        this.getNowSelDataTwoType()
      },

3、我们要添加,点击自定义的icon来控制本身的展开与关闭事件

1)添加icon图标

这里只涉及子级的复选框,然后定义  expend  字段来表示是否打开

<template slot-scope="scope">
   <div style="position: relative">
      <el-checkbox
         v-model="scope.row.checks"
         style="padding-left: 10px"
         @change="checkChange(scope.row)"
       />
      <span class="expendIcon" v-if="scope.row.children && scope.row.children.length>0" @click="expendClick(scope.row,scope.row.expend)">
         <i class="el-icon-arrow-right" v-if="!scope.row.expend"></i>
         <i class="el-icon-arrow-down" v-if="scope.row.expend"></i>
       </span>
    </div>
</template>

2)点击图标,展开或者关闭当前行的展开行

data里面需要添加两个属性,第一个是唯一字段,第二个是数组,数组内部是展开的id信息

         row-key="id"
        :expand-row-keys="expandRowArr"

然后点击事件里面,动态修改他的 expandRowArr

data

    expandRowArr:[],//展开内容


methods:

// 是否展开
expendClick(row,title){
   if(!title){
      // 打开
      this.expandRowArr.push(row.id)
      this.tableData.forEach(items => {
        if(items.id == row.id){
          if(items.expend == undefined) items.expend = false
            this.$set(items, "expend", true);
          }
       })
     }else{
      // 关闭
       let oldArr = this.expandRowArr
       let newArr = []
       oldArr.forEach(item=>{
          if(item != row.id){
            newArr.push(item)
          }
       })
       this.expandRowArr = newArr
       this.tableData.forEach(items => {
          // this.$refs.multipleTable.data.forEach(items => {
          if(items.id == row.id){
            this.$set(items, "expend", false);
          }
       })
    }
},

3) 功能实现以后,我们要隐藏掉他的原本的展开与关闭开关

<el-table-column type="expand" width="1">
      <template slot-scope="props">
            //展开后的内容...
        
      </template>
</el-table-column>

4、到目前位置,功能都有了,在我们封装的内容里面增加支持插槽与render函数

<template v-for="(column, index) in columns">
      <!-- slot 添加自定义配置项 -->
      <slot v-if="column.slot" :name="column.slot" :tit='index'></slot>
      <!-- 默认渲染列-渲染每一列的汉字 -->
      <el-table-column
        v-else
        :prop="column.prop"
        :label="column.title"
        :align="column.align"
        :width="column.width"
        :show-overflow-tooltip="true">
        <template slot-scope="scope">
          <!--正常渲染-->
          <template v-if="!column.render">
            <template v-if="column.formatter">
              <span v-html="column.formatter(scope.row, column)"></span>
            </template>
            <template v-else>
              <span>{{scope.row[column.prop]}}</span>
            </template>
          </template>
          <!--render函数-->
          <template v-else>
            <expandDom :column="column" :row="scope.row" :render="column.render" :index="index"></expandDom>
          </template>
        </template>
      </el-table-column>
    </template>
components: {
      /**
       * render函数渲染组件
       * */
      expandDom: {
        functional: true,
        props: {
          row: Object,
          render: Function,
          index: Number,
          column: {
            type: Object,
            default: null
          }
        },
        render: (h, ctx) => {
          const params = {
            row: ctx.props.row,
            index: ctx.props.index
          }
          if (ctx.props.column) params.column = ctx.props.column
          return ctx.props.render(h, params)
        }
      }
    },

源码分享:

注意:展开内容请根据自己的需要进行改动,我这里是私有功能

引入代码:

<checkTable ref="checkTable" :columns="productColumns" :tableData="productList" >
     <el-table-column slot="col_slot_1" label="商品列表" width="180">
        <template slot-scope="scope">
           <img :src="scope.row.goods_image" class="goods-image" />
        </template>
     </el-table-column>
</checkTable>


data:
productColumns:{
    slot:'col_slot_1'
    
}

productList:[{
    id:1,
    name:'张三',
    children:[{ //有该字段的有子级
        id:1,
        name:'张三儿子',
    }]

}]

封装代码:checkTable.vue

<template>
  <el-table
    v-if="uploadTable"
    class="multipleTable"
    ref="multipleTable"
    border
    row-key="id"
    :expand-row-keys="expandRowArr"
    :data="tableData"
  >
    <el-table-column width="80">
      <template slot="header" slot-scope="scope">
        <el-checkbox
          v-model="allCheckout"
          style="padding-left: 10px"
          @change="checkAllFun()"
        />
      </template>
      <template slot-scope="scope">
        <div style="position: relative">
          <el-checkbox
            v-model="scope.row.checks"
            style="padding-left: 10px"
            @change="checkChange(scope.row)"
          />
          <span class="expendIcon" v-if="scope.row.children && scope.row.children.length>0" @click="expendClick(scope.row,scope.row.expend)">
            <i class="el-icon-arrow-right" v-if="!scope.row.expend"></i>
            <i class="el-icon-arrow-down" v-if="scope.row.expend"></i>
          </span>
        </div>
      </template>
    </el-table-column>
    <el-table-column type="expand" width="1">
      <template slot-scope="props">
            //展开的内容展示
             ...
    

      </template>
    </el-table-column>
    <template v-for="(column, index) in columns">
      <!-- slot 添加自定义配置项 -->
      <slot v-if="column.slot" :name="column.slot" :tit='index'></slot>
      <!-- 默认渲染列-渲染每一列的汉字 -->
      <el-table-column
        v-else
        :prop="column.prop"
        :label="column.title"
        :align="column.align"
        :width="column.width"
        :show-overflow-tooltip="true">
        <template slot-scope="scope">
          <!--正常渲染-->
          <template v-if="!column.render">
            <template v-if="column.formatter">
              <span v-html="column.formatter(scope.row, column)"></span>
            </template>
            <template v-else>
              <span>{{scope.row[column.prop]}}</span>
            </template>
          </template>
          <!--render函数-->
          <template v-else>
            <expandDom :column="column" :row="scope.row" :render="column.render" :index="index"></expandDom>
          </template>
        </template>
      </el-table-column>
    </template>



  </el-table>

</template>

<script>
  /**
   * @封装的复选框加列表expend展开功能列表
   * @createTime 2023.6
   * @auth ry
   * */
  export default {
    name: "checkTable.vue",
    props:{
      // 表格数据
      tableData:{
        type: Array,
        deep:true,
        immediate:true,
        default:()=>[]

      },
      //表头
      columns: {
        type:Array,
        default:()=>[]
      },
      tableClass: {
        type: String,
        default: 'checkTable'
      },
      ifChangeTrue:{
        type:String | Number,
      }
    },
    watch:{
      tableData(arr){
        this.tableData.map(item=>{
          if(item.children && item.children.length>0) item.expend = false
          return item
        })
      }
    },
    components: {
      /**
       * render函数渲染组件
       * */
      expandDom: {
        functional: true,
        props: {
          row: Object,
          render: Function,
          index: Number,
          column: {
            type: Object,
            default: null
          }
        },
        render: (h, ctx) => {
          const params = {
            row: ctx.props.row,
            index: ctx.props.index
          }
          if (ctx.props.column) params.column = ctx.props.column
          return ctx.props.render(h, params)
        }
      }
    },
    data(){
      return{
        allCheckout: false, //全选
        expandRowArr:[],//展开内容
        uploadTable:true,
      }
    },
    mounted(){
      this.clearCheckFun()
    },
    methods:{
      // 获取当前选中的数据,有子级的,子级+父级
      getNowSelData(){
        let selData = []
        this.tableData.forEach(item=>{
          // 一级无子级
          if(item.checks && !item.children){
            selData.push(item)
          }
          // 一级有子级
          if(item.checks && item.children && item.children.length){
            selData.push(item)
            selData = selData.concat(item.children)
          }
          // 二级无子级
          if(!item.checks && item.children && item.children.length){
            item.children.forEach(two=>{
              if(two.checks){
                selData.push(two)
              }
            })
          }
        })
        return selData
      },
      // 获取当前选中的数据-有子级的,父级(内部有children)
      getNowSelDataOneType(){
        let selData = []
        this.tableData.forEach(item=> {
          // 一级无子级
          if(item.checks && !item.children){
            selData.push(item)
          }
          // 一级有子级
          if(item.checks && item.children && item.children.length){
            selData.push(item)
          }
          // 二级无子级
          if(!item.checks && item.children && item.children.length){
            item.children.forEach(two=>{
              if(two.checks){
                selData.push(two)
              }
            })
          }
        })
        return selData
      },
      // 获取当前选中的数据-有子级是子级,没有则是他自己
      getNowSelDataTwoType(){
        let selData = []
        this.tableData.forEach(item=> {
          // 一级无子级
          if(item.checks && !item.children){
            selData.push(item)
          }
          // 一级有子级
          if(item.checks && item.children && item.children.length){
            selData = selData.concat(item.children)
          }
          // 二级无子级
          if(!item.checks && item.children && item.children.length){
            item.children.forEach(two=>{
              if(two.checks){
                selData.push(two)
              }
            })
          }
        })
        this.$emit('changeShopNum',selData)
        return selData
      },
      // 清理选中效果
      clearCheckFun(){
        setTimeout(()=>{
          if(this.ifChangeTrue=='1'){
            this.allCheckout = true
          }else{
            this.allCheckout = false
          }

          this.checkAllFun()
        },500)
      },
      // 总全选点击事件
      checkAllFun() {
        this.tableData.forEach(items => {
          this.$set(items, "checks", this.allCheckout);
          if (items.children) {
            items.children.forEach((item) => {
              this.$set(item, "checks", this.allCheckout);
            });
          }
        })
        this.getNowSelDataTwoType()
      },
      //插槽复选框逻辑
      checkChange(row) {
        // expend 父级复选框
        if (row.children) {
          if (row.checks) {
            row.children.map((item) => {
              this.$set(item, "checks", true);
            });
            this.$set(row, "checks", true);
          } else {
            row.children.map((item) => {
              this.$set(item, "checks", false);
            });
            this.$set(row, "checks", false);
          }
        }else{
          // expend 子级复选框
          if(row.checks == undefined) row.checks = false
          this.$set(row, "checks", row.checks)
          const findTypeArr = this.tableData.filter(v=>v.id == row.id)
          let jb_type = ''
          if(findTypeArr.length>0){
            jb_type = '1'
          }else{
            jb_type = '2'
          }
          // 一级的操作
          if(jb_type == '1' && !row.checks){
            this.allCheckout = false
          }
          if(jb_type == '1' && row.checks){
            this.allCheckSetFun()
          }
          // 二级操作
          if(jb_type == '2' && !row.checks){
            this.allCheckout = false
            this.tableData.forEach(one => {
              if (one.children && one.children.length > 0) {
                one.children.forEach(two => {
                  if (two.id == row.id) {
                    this.$set(one, "checks", false)
                  }
                })
              }
            })
          }
          if(jb_type == '2' && row.checks){
            this.tableData.forEach(one => {
              if (one.children && one.children.length > 0) {
                one.children.forEach(two => {
                  if (two.id == row.id) {
                    let twoSelArr = one.children.filter(v=>v.checks)
                    if(twoSelArr.length == one.children.length){
                      this.$set(one, "checks", true)
                    }else{
                      this.$set(one, "checks", false)
                    }
                  }
                })
              }
            })
          }
        }
        // 刷新外部总全选状态
        this.allCheckSetFun()
        // 刷新
        this.uploadTable = false
        this.$nextTick(()=>{
          this.uploadTable = true
        })

      },
      // 修改外部总勾选
      allCheckSetFun(){
        let oneSelArr = this.tableData.filter(v=>v.checks)
        if(oneSelArr.length == this.tableData.length){
          this.allCheckout = true
        }else{
          this.allCheckout = false
        }
        this.getNowSelDataTwoType()
      },
      // 是否展开
      expendClick(row,title){
        if(!title){
          // 打开
          this.expandRowArr.push(row.id)
          this.tableData.forEach(items => {
            if(items.id == row.id){
              if(items.expend == undefined) items.expend = false
              this.$set(items, "expend", true);
            }
          })
        }else{
          // 关闭
          let oldArr = this.expandRowArr
          let newArr = []
          oldArr.forEach(item=>{
            if(item != row.id){
              newArr.push(item)
            }
          })
          this.expandRowArr = newArr
          this.tableData.forEach(items => {
            // this.$refs.multipleTable.data.forEach(items => {
            if(items.id == row.id){
              this.$set(items, "expend", false);
            }
          })
        }
      },

    }
  }
</script>

<style scoped>
  .expendIcon{
    position: absolute;
    right:10px;
    cursor: pointer;
    margin-left:10px;
  }
  .expandUl{
    display: flex;
    padding:10px 0 10px 30px;
  }
  .expandUlSon{
    width: 95%;
    display: flex;
    justify-content: space-around;
  }
  .expandUlSonDiv{
    min-width: 100px;
    margin-left:20px;
  }
</style>