当前位置:   article > 正文

el-table树形表格实现 父子联动勾选 并过滤时进行勾选缓存

el-table树形表格实现 父子联动勾选 并过滤时进行勾选缓存

el-table树形表格实现 父子联动勾选 并过滤时进行勾选缓存

需求背景

el-table,支持树形表格,但是多选框并没有自动支持父子联动勾选;

  1. 勾选全选,只有最外层的行被勾选;
  2. 勾选父,子级不会自动勾选;
  3. 勾选部分子,父级不会自动处于半勾选状态;
  4. 依次勾选全部的子级,父级不会自动勾选;
  5. 之前设计的联动勾选方案,不支持分页,再次查询

具体要求:

  1. 勾选全选按钮,表格所有层级 均被勾选,取消勾选全选按钮,表格所有层级 均被取消勾选;
  2. 父级勾选,子级(以及更深层级的子级)全部勾选,父级取消勾选,子级(以及更深层级的子级)全部取消勾选;
  3. 子级部分勾选 或 处于 半勾选的状态,则父级处于半勾选的状态
  4. 依次勾选全部的子级,父级会自动勾选;
  5. 点击【查询】或分页时,之前的勾选数据,还在

解释一下,一般树形表格,很难做分页,但是一般我们都是使用el-table做二次封装,然后封装后的table组件,可能不仅支持数据表格,还支持普通一级数据的分页,甚至还支持所有结构数据的 查询、前端过滤等高级功能。

设计方案

  1. 树形表格,第一次查询到所有数据,并根据此树形结构,进行总的勾选状态联动,获取树形结构数据失败时,不允许后续操作;如果数据量确实很大时,需要后端协助,后端执行联动勾选操作,将哪些数据勾选,半勾选的数据返回给前端,前端只负责勾选即可;
  2. 总的树形结构的数据treeData,页面展示的临时数据list,缓存的总数据cachedSelection,每次勾选时,去操作总树形结构treeData,进行相关的数据联动,然后,根据联动后的总数据,组织缓存的总数据cachedSelection,去修改页面临时展示的数据list,根据list,去依次勾选表格数据
  3. 普通一级数据的分页,也需要将勾选的数据进行缓存,但是无需使用总的树形结构treeData

拓展问题

树形表格,只有一个祖先元素,如何进行性能优化?

开发分析

涉及的属性

@Prop({ default: false }) multiple: boolean; //? 是否开启多选
  /** 树形数据全部展开标识 */
  @Prop({ default: false }) expandAllFlag: boolean
  @Prop({ default: false }) needRelativeSelect: boolean; //? 树形表格,是否需要联动勾选
  • 1
  • 2
  • 3
  • 4

涉及的状态

list = []; //? 页面展示的临时数据
  treeData = null; //? 树形结构的总结构
  cachedSelection = []; //? 缓存的总数据
  currentRecord = null; //? 当前选中的记录 —— 服务于单选
  • 1
  • 2
  • 3
  • 4

1、半勾选状态的实现

当前只有标题头的勾选框有半勾选状态,行上没有,需要手动实现
首先是半勾选的样式,需要从标题头勾选框的半勾选状态的样式复制;当父级处于半勾选状态时,去使用这个类的样式;当行的某一数据标识为true时,勾选框的列 的类名添加indeterminate,否则勾选框的列 的类名为空;
可以使用cell-class-name属性,进行实现< el-table :row-key=“valueKey”>< /el-table>


  tableCellClassName ({row, column}) {
    let cellClassName = ''
    if (row.indeterminate && column.type === 'selection') {
      cellClassName = 'indeterminate'
    }
    return cellClassName
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
<style lang="scss" scoped>
::v-deep .lov_table {
  .indeterminate {
    .el-checkbox__input {
      .el-checkbox__inner {
        background-color: #5f4efd;
        border-color: #5f4efd;
        &::before {
          content: '';
          position: absolute;
          display: block;
          background-color: #fff;
          height: 2px;
          -webkit-transform: scale(.5);
          transform: scale(.5);
          left: 0;
          right: 0;
          top: 5px;
        }
      }
    }
  }
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

2、编辑初始化时,要给保存前的数据自动勾选上

获取到数据后,根据上次的cachedSelection,修改树形总结构treeData;
同时给树形结构的数据初始化setTreeMetaData,使得每一个子节点都有parentId,方便后续的联动操作

async getList(type?) {
    if (type !== 'first' && !this.treeData) { //* 第一次,必须获取到树形结构,才能继续执行
      this.$alert('树形表格,第一次接口异常,不允许后续操作').finally(() => {
        this.closeModal()
      })
      return
    }
    this.loading = true
    try {
      const res = await this.lovRemote.request({
        ...this.remoteParams,
        ...this.queryForm,
        pageIndex: this.pageIndex,
        pageSize: this.pageSize
      })
      if (res?.data) {
        const remoteResult: any = this.$attrs.remoteResult
        const result = remoteResult ? remoteResult(res) : res.data.list || res.data
        setTreeMetaData(result, this.valueKey, 'children')
        if (type === 'first') { //* 第一次,必须获取到树形结构,才能继续执行
          if (!this.treeData) this.treeData = result;
        }
        this.list = result
        this.totalRows = res.data.total
        if (this.needRelativeSelect) this.batchUpdateTreeData(this.cachedSelection); //* 给树形结构设置初始的勾选状态
      }
    } catch (err) {
      this.$message.error(err.message)
    } finally {
      this.loading = false
    }
  }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

3、监听treeData的变化,去执行后续操作

@Watch('treeData', { deep: true, immediate: true })
  onTableConfigChange(newValue) {
    if (newValue) {
      this.$nextTick(() => {
        this.afterTreeDataChanged()
      })
    }
  }
  afterTreeDataChanged() { //todo 树形结构的数据发生变化时的后续
    if (this.needRelativeSelect) {
      this.getListFormTreeData();
      this.getCachedSelectionFromTreeData();
      this.selectRowFromList();
    }
  }
  getListFormTreeData() { //todo 从树形结构,获取当前页面的临时数据
    const expandedTreeData = getExpandedTreeData(this.treeData, 'children')
    const updateList = (tree) => {
      tree.forEach(record => {
        const currentRecord = expandedTreeData.find(item => item[this.valueKey] == record[this.valueKey]); //? 树形结构中该行 对应的数据
        this.$set(record, 'selected', currentRecord.selected)
        this.$set(record, 'selectedString', currentRecord.selected ? 'Y' : 'N')
        this.$set(record, 'indeterminate', currentRecord.indeterminate)
        if (Array.isArray(record.children) && record.children.length) {
          updateList(record.children)
        }
      })
    }
    updateList(this.list)
  }
  getCachedSelectionFromTreeData() { //todo 从树形结构,获取总的勾选数据
    const expandedTreeData = getExpandedTreeData(this.treeData, 'children')
    this.cachedSelection = expandedTreeData.filter(item => item.selected)
  }
  selectRowFromList() { //todo 根据当前页面的临时数据,勾选表格的行
    const expandedList = getExpandedTreeData(this.list, 'children')
    const modalTableRef: any = this.$refs.modalTableRef
    modalTableRef.clearSelection(); //* 打扫干净屋子再请客
    expandedList.forEach(record => {
      modalTableRef.toggleRowSelection(record, !!record.selected); //* 调用el-table内置方法,进行勾选
    })
  }
  batchUpdateTreeData(list, isSelected: boolean = true) { //todo 批量的根据勾选的数据,动态计算树形结构的数据联动
    this.treeData = batchUpdateTreeData({
      treeData: this.treeData,
      list,
      treeKey: 'id',
      childrenKey: 'children',
      isSelected
    })
  }
  updateTreeData(row, isSelected: boolean = true) { //todo 根据勾选的数据,动态计算树形结构的数据联动
    this.treeData = updateTreeData({
      treeData: this.treeData,
      row: row,
      treeKey: 'id',
      childrenKey: 'children',
      isSelected
    })
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

3、父子联动勾选

使用selection-change事件(当选择项发生变化时会触发该事件)监听的话,只有一个selection参数,并不知道当前勾选的是哪一行,于是使用select(当用户手动勾选数据行的 Checkbox 时触发的事件)和select-all(当用户手动勾选全选 Checkbox 时触发的事件)

/** 勾选全选操作(多选模式) */
  selectAllOnClick() {
    const modalTableRef: any = this.$refs.modalTableRef
    const isAllSelected = modalTableRef?.store.states.isAllSelected; //? 是否全部勾选
    if (this.needRelativeSelect) {
      this.batchUpdateTreeData(this.list, isAllSelected)
      return
    }
    if (isAllSelected) {
      this.list.forEach((cur) => {
        const isUnselect = !this.cachedSelection.map((sel) => sel[this.valueKey]).includes(cur[this.valueKey])
        if (isUnselect) {
          this.cachedSelection.push(cur)
        }
      })
    } else {
      this.list.forEach((item) => {
        const cacheIndex = this.cachedSelection.findIndex(
          (cache) => cache[this.valueKey] === item[this.valueKey]
        )
        if (cacheIndex !== -1) {
          this.cachedSelection.splice(cacheIndex, 1)
        }
      })
    }
  }

  /** 勾选单条操作(多选模式) */
  selectOnClick(selection, row) {
    const modalTableRef: any = this.$refs.modalTableRef
    const curSelIndex = selection.findIndex((cache) => cache[this.valueKey] === row[this.valueKey])
    if (this.needRelativeSelect) {
      this.updateTreeData(row, curSelIndex > -1)
      //todo 通过勾选的数据selection,去总树结构 treeData,设置相关联动勾选状态,并将当前表格的相关数据进行勾选
      return
    }
    if (curSelIndex > -1) { //* 被勾选
      this.cachedSelection.push(row)
    } else {
      this.cachedSelection.splice(curSelIndex, 1)
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

util.js

//! zhy start 树形表格 父子联动 + 勾选缓存
/**
 * todo 对树形结构的数据进行加工,直接子节设置parentValue
 * @param data 树形结构的数据
 * @param treeKey 树形结构的id字段
 * @param childrenKey 树形结构子级的字段
 * @param parentNode 上级节点
 */
const setTreeMetaData = (data, treeKey: string = 'id', childrenKey: string = 'children' ,parentNode?: any) => {
  data.forEach(item => {
    if (parentNode) item.parentValue = parentNode[treeKey];
    if (Array.isArray(item[childrenKey]) && item[childrenKey].length) {
      item[childrenKey].forEach(child => {
        child.parentValue = item[treeKey];
        if (Array.isArray(child[childrenKey]) && child[childrenKey].length) {
          setTreeMetaData(child[childrenKey], treeKey, childrenKey, child)
        }
      });
    }
  })
}
/**
 * todo 获取展开后的树形表格数据 深层 变成 一层
 * @param treeData 树形结构的数据
 * @param childrenKey 树形结构子级的字段
 * @returns 展开后的树形表格数据
 */
const getExpandedTreeData = (treeData, childrenKey: string = 'children') => {
  return treeData.reduce((accumulator, curItem) => {
    if (Array.isArray(curItem[childrenKey]) && curItem[childrenKey].length) {
      return accumulator.concat(curItem).concat(getExpandedTreeData(curItem[childrenKey]))
    }
    return accumulator.concat(curItem)
  }, [])
}
/**
 * todo 将当前行下的所有子级切换勾选状态
 * @param record 单前行
 * @param toggleRowSelection 切换行的勾选状态的内置方法
 * @param childrenKey 树形结构子级的字段
 * @param selected 统一设置的 勾选的状态
 */

/**
 * todo 将当前行下的所有子级切换勾选状态
 * @param record 单前行
 * @param toggleRowSelection 切换行的勾选状态的内置方法
 * @param childrenKey 树形结构子级的字段
 * @param selected 统一设置的 勾选的状态
 */
const toggleSubAll = ({record, childrenKey = 'children', selected = true}) => {
  if (Array.isArray(record[childrenKey]) && record[childrenKey].length) { //* 有子级
    record[childrenKey].forEach(subRecord => {
      subRecord.selected = selected;
      if (Array.isArray(subRecord[childrenKey]) && subRecord[childrenKey].length) { //* 子级还有下级
        toggleSubAll({record: subRecord, childrenKey, selected})
      }
    })
    
  }
}
/**
 * todo 设置树形表格父级的勾选状态
 * @param parentValue 父级的id
 * @param expandedList 树形表格展开后的数据
 * @param userSelection 用户勾选的所有数据
 * @param tableRef 表格的ref
 * @param treeKey 树形结构的id字段
 */
const setTableTreeParentSelection = ({parentValue, expandedList, treeKey = 'id'}) => {
  const parentRecord = expandedList.find(item => item[treeKey] === parentValue); //? 当前的父级
  const subList = expandedList.filter(item => item.parentValue === parentValue); //? 当前父级下的所有的子级
  const selectedList = subList.filter(subRecord => subRecord.selected); //? 所有子级中,被勾选的数据
  const halfSelectedList = subList.filter(subRecord => subRecord.indeterminate === 'Y'); //? 所有子级中,半勾选状态的子级
  parentRecord.indeterminate = undefined;
  if (subList.length === selectedList.length) { //* 所有子级全部勾选,父级勾选
    parentRecord.selected = true;
  } else if (!selectedList.length && !halfSelectedList.length) { //* 所有子级 全部没有勾选也没有半勾选
    parentRecord.selected = false;
  } else {
    //* 子级部分勾选,
    parentRecord.selected = false;
    parentRecord.indeterminate = 'Y'
  }
  if (parentRecord.parentValue) setTableTreeParentSelection({parentValue: parentRecord.parentValue, expandedList, treeKey})
}
//todo 根据用户勾选的行,去修改树形结构的数据
const updateTreeData = ({treeData, row, treeKey = 'id', childrenKey = 'children', isSelected = true}) => {
  const treeDataClone = clone(treeData)
  const expandedTreeData = getExpandedTreeData(treeDataClone, childrenKey);
  const record = expandedTreeData.find(item => item[treeKey] === row[treeKey])
  record.indeterminate = undefined;
  record.selected = isSelected;
  toggleSubAll({record, childrenKey, selected: isSelected})
  if (record.parentValue) setTableTreeParentSelection({parentValue: record.parentValue, expandedList: expandedTreeData, treeKey})
  return treeDataClone
}
//todo 根据勾选的数据,动态计算树形结构的数据联动
const batchUpdateTreeData = ({treeData, list, treeKey = 'id', childrenKey = 'children', isSelected = true}) => {
  let treeDataClone = clone(treeData)
  list.forEach(item => {
    treeDataClone = updateTreeData({
      treeData: treeDataClone,
      row: item,
      treeKey, childrenKey, isSelected
    })
  })
  return treeDataClone
}
//! zhy end 树形表格 RelativeSelect
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110

完整代码

<template>
  <el-dialog
    class="lov_modal"
    :width="`${width || '60vw'}`"
    :title="title"
    :visible="visible"
    @close="closeModal"
    destroy-on-close
    append-to-body
    :close-on-click-modal="false"
  >
    <base-query-form
      :defaultQuery="remoteParams"
      :fieldProps="queryItems"
      :loading="loading"
      @query="getQuery"
    />
    <el-table
      border
      :height="500"
      ref="modalTableRef"
      :data="list"
      class="lov_table"
      v-loading="loading"
      :row-key="valueKey"
      highlight-current-row
      :default-expand-all="expandAllFlag"
      :cell-class-name="tableCellClassName"
      @current-change="currentChange"
      @select="selectOnClick"
      @select-all="selectAllOnClick"
      @row-dblclick="rowOnDoubleClick"
    >
      <el-table-column
        v-if="multiple"
        type="selection"
        width="55"
        reserve-selection
        align="center"
      />
      <el-table-column
        :label="colLabel(column)"
        :prop="column.descKey || column.key"
        show-overflow-tooltip
        v-for="column in tableColumns"
        :key="column.key"
        :formatter="column.format"
        v-bind="column"
      >
      </el-table-column>
    </el-table>
    <el-pagination
      v-if="!$attrs.remoteResult"
      class="table_pagination"
      :disabled="loading"
      @size-change="listSizeOnChange"
      @current-change="listCurrentOnChange"
      :current-page="pageIndex"
      :page-sizes="[10, 20, 50, 100]"
      :page-size="pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="totalRows"
    >
    </el-pagination>
    <span slot="footer" class="dialog-footer">
      <el-button @click="closeModal">取 消</el-button>
      <el-button type="primary" @click="confirmOnClick">确 定</el-button>
    </span>
  </el-dialog>
</template>

<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator'
import BaseQueryForm from '@/components/BaseQueryForm/index.vue'
import { ColumnProp } from '@/utils/interface'
import * as LOV_CONFIG from './lovModalConfig'
import { getType, isNil } from '@/utils/util'
import { batchUpdateTreeData, getExpandedTreeData, setTreeMetaData, updateTreeData } from '../BaseSearchTable/utils'

@Component({
  components: {
    BaseQueryForm
  },
  name: 'lovModal'
})
export default class extends Vue {
  @Prop({ required: true }) lovCode: string
  @Prop() remoteParams: any
  @Prop() valueKey: string
  @Prop() cached: any[]
  @Prop() width: string
  @Prop({ default: '弹窗' }) title: string
  @Prop({ default: false }) multiple: boolean; //? 是否开启多选
  /** 树形数据全部展开标识 */
  @Prop({ default: false }) expandAllFlag: boolean
  @Prop({ default: false }) needRelativeSelect: boolean; //? 树形表格,是否需要联动勾选

  list = []; //? 页面展示的临时数据
  treeData = null; //? 树形结构的总结构
  cachedSelection = []; //? 缓存的总数据
  currentRecord = null; //? 当前选中的记录 —— 服务于单选
  totalRows: number = 0
  loading: boolean = false
  queryForm: any = {}
  pageIndex = 1
  pageSize = 10
  visible = false

  get lovRemote() {
    return LOV_CONFIG[this.lovCode].lovRemote
  }

  get lovModalColumns(): ColumnProp[] {
    return LOV_CONFIG[this.lovCode].columns
  }

  get colLabel() {
    return (col) => {
      return col.i18nKey ? this.$t(`table.${col.i18nKey}`) : col.name
    }
  }

  get selectedIds() {
    return (this.cachedSelection || []).map((sel) => sel[this.valueKey])
  }

  get tableColumns() {
    return this.lovModalColumns.filter((item) => item.showInTable)
  }

  get queryItems() {
    return this.lovModalColumns.filter((item) => item.showInQuery)
  }

  getQuery(params) {
    this.queryForm = params
    this.initList()
  }

  initList() {
    this.pageIndex = 1
    this.totalRows = 0
    this.list = []
    this.getList()
  }
  @Watch('treeData', { deep: true, immediate: true })
  onTableConfigChange(newValue) {
    if (newValue) {
      this.$nextTick(() => {
        this.afterTreeDataChanged()
      })
    }
  }

  initSelection() {
    const modalTableRef: any = this.$refs.modalTableRef
    modalTableRef.clearSelection(); //* 打扫干净屋子再请客
    if (this.cachedSelection.length) {
      const checkSelect = (data) => {
        data.forEach((item) => {
          this.cachedSelection.forEach((sel) => {
            if (item[this.valueKey] === sel[this.valueKey]) {
              this.multiple
                ? modalTableRef.toggleRowSelection(item, true)
                : modalTableRef.setCurrentRow(item)
            }
          })
          if (getType(item.children) === 'Array' && item.children.length) {
            checkSelect(item.children)
          }
        })
      }
      // 递归选中勾选缓存数据
      checkSelect(this.list)
    }
  }

  afterTreeDataChanged() { //todo 树形结构的数据发生变化时的后续
    if (this.needRelativeSelect) {
      this.getListFormTreeData();
      this.getCachedSelectionFromTreeData();
      this.selectRowFromList();
    }
  }
  getListFormTreeData() { //todo 从树形结构,获取当前页面的临时数据
    const expandedTreeData = getExpandedTreeData(this.treeData, 'children')
    const updateList = (tree) => {
      tree.forEach(record => {
        const currentRecord = expandedTreeData.find(item => item[this.valueKey] == record[this.valueKey]); //? 树形结构中该行 对应的数据
        this.$set(record, 'selected', currentRecord.selected)
        this.$set(record, 'selectedString', currentRecord.selected ? 'Y' : 'N')
        this.$set(record, 'indeterminate', currentRecord.indeterminate)
        if (Array.isArray(record.children) && record.children.length) {
          updateList(record.children)
        }
      })
    }
    updateList(this.list)
  }
  getCachedSelectionFromTreeData() { //todo 从树形结构,获取总的勾选数据
    const expandedTreeData = getExpandedTreeData(this.treeData, 'children')
    this.cachedSelection = expandedTreeData.filter(item => item.selected)
  }
  selectRowFromList() { //todo 根据当前页面的临时数据,勾选表格的行
    const expandedList = getExpandedTreeData(this.list, 'children')
    const modalTableRef: any = this.$refs.modalTableRef
    modalTableRef.clearSelection(); //* 打扫干净屋子再请客
    expandedList.forEach(record => {
      modalTableRef.toggleRowSelection(record, !!record.selected); //* 调用el-table内置方法,进行勾选
    })
  }
  batchUpdateTreeData(list, isSelected: boolean = true) { //todo 批量的根据勾选的数据,动态计算树形结构的数据联动
    this.treeData = batchUpdateTreeData({
      treeData: this.treeData,
      list,
      treeKey: 'id',
      childrenKey: 'children',
      isSelected
    })
  }
  updateTreeData(row, isSelected: boolean = true) { //todo 根据勾选的数据,动态计算树形结构的数据联动
    this.treeData = updateTreeData({
      treeData: this.treeData,
      row: row,
      treeKey: 'id',
      childrenKey: 'children',
      isSelected
    })
  }

  async getList(type?) {
    if (type !== 'first' && !this.treeData) { //* 第一次,必须获取到树形结构,才能继续执行
      this.$alert('树形表格,第一次接口异常,不允许后续操作').finally(() => {
        this.closeModal()
      })
      return
    }
    this.loading = true
    try {
      const res = await this.lovRemote.request({
        ...this.remoteParams,
        ...this.queryForm,
        pageIndex: this.pageIndex,
        pageSize: this.pageSize
      })
      if (res?.data) {
        const remoteResult: any = this.$attrs.remoteResult
        const result = remoteResult ? remoteResult(res) : res.data.list || res.data
        setTreeMetaData(result, this.valueKey, 'children')
        if (type === 'first') { //* 第一次,必须获取到树形结构,才能继续执行
          if (!this.treeData) this.treeData = result;
        }
        this.list = result
        this.totalRows = res.data.total
        this.$nextTick(() => {
          this.initSelection()
        })
        if (this.needRelativeSelect) this.batchUpdateTreeData(this.cachedSelection); //* 给树形结构设置初始的勾选状态
      }
    } catch (err) {
      this.$message.error(err.message)
    } finally {
      this.loading = false
    }
  }

  listSizeOnChange(val) {
    this.pageIndex = 1
    this.pageSize = val
    this.$nextTick(() => {
      this.initList()
    })
  }

  listCurrentOnChange(val) {
    this.pageIndex = val
    this.$nextTick(() => {
      this.getList()
    })
  }

  toggleRowExpanAll(isExpan) {
    this.toggleRowExpan(this.list, isExpan)
  }

  toggleRowExpan(data, isExpan) {
    const tree: any = this.$refs.modalTableRef
    data.forEach((item) => {
      tree.toggleRowExpansion(item, isExpan)
      if (!isNil(item.children) && item.children.length) {
        this.toggleRowExpan(item.children, isExpan)
      }
    })
  }

  async showModal() {
    this.visible = true
    this.cachedSelection = JSON.parse(JSON.stringify(this.cached))
    this.queryForm = { ...this.queryForm, ...this.remoteParams }
    await this.getList('first')
    // this.expandAllFlag && this.toggleRowExpanAll(this.expandAllFlag)
  }

  closeModal() {
    this.pageIndex = 1
    this.pageSize = 10
    this.totalRows = 0
    this.visible = false
    this.treeData = null
    this.list = []
    this.cachedSelection = []
    this.queryForm = {}
    this.currentRecord = null
  }

  /** 点击单行操作 */
  currentChange(val) {
    this.currentRecord = val
  }
  tableCellClassName ({row, column}) {
    let cellClassName = ''
    if (row.indeterminate && column.type === 'selection') {
      cellClassName = 'indeterminate'
    }
    return cellClassName
  }
  /** 勾选全选操作(多选模式) */
  selectAllOnClick() {
    const modalTableRef: any = this.$refs.modalTableRef
    const isAllSelected = modalTableRef?.store.states.isAllSelected; //? 是否全部勾选
    if (this.needRelativeSelect) {
      this.batchUpdateTreeData(this.list, isAllSelected)
      return
    }
    if (isAllSelected) {
      this.list.forEach((cur) => {
        const isUnselect = !this.cachedSelection.map((sel) => sel[this.valueKey]).includes(cur[this.valueKey])
        if (isUnselect) {
          this.cachedSelection.push(cur)
        }
      })
    } else {
      this.list.forEach((item) => {
        const cacheIndex = this.cachedSelection.findIndex(
          (cache) => cache[this.valueKey] === item[this.valueKey]
        )
        if (cacheIndex !== -1) {
          this.cachedSelection.splice(cacheIndex, 1)
        }
      })
    }
  }

  /** 勾选单条操作(多选模式) */
  selectOnClick(selection, row) {
    const curSelIndex = selection.findIndex((cache) => cache[this.valueKey] === row[this.valueKey])
    if (this.needRelativeSelect) {
      this.updateTreeData(row, curSelIndex > -1)
      //todo 通过勾选的数据selection,去总树结构 treeData,设置相关联动勾选状态,并将当前表格的相关数据进行勾选
      return
    }
    if (curSelIndex > -1) { //* 被勾选
      this.cachedSelection.push(row)
    } else {
      this.cachedSelection = this.cachedSelection.filter(item => item[this.valueKey] !== row[this.valueKey])
    }
  }


  /** 双击行 */
  rowOnDoubleClick(row) {
    this.currentChange(row)
    if (!this.multiple) { //* 单选时,双击行时执行【确认】操作
      this.$nextTick(() => {
        this.confirmOnClick()
      })
    }
  }

  /** 点击确认按钮 */
  confirmOnClick() {
    if (this.multiple) {
      if(this.cachedSelection.length == 0){
        let message=(this.$t('documentation.pleaseSelect') as any)+this.title
        this.$message({
          type:'warning',
          message
        })
      }
      this.$emit('onOk', this.cachedSelection)
    } else {
      if(!this.currentRecord) {
        let message=(this.$t('documentation.pleaseSelect') as any)+this.title
        this.$message({
          type:'error',
          message
        })
        return
      }
      this.$emit('onOk', this.currentRecord)
    }
  }
}
</script>

<style lang="scss" scoped>
.show-box {
  height: 100%;
}

.fold-box {
  height: 58px;
}
.table_pagination {
  margin-top: 10px;
  text-align: right;
}
::v-deep {
  .el-card {
    border: none;
    .el-card__body {
      padding: 0;
    }
  }
}
::v-deep .lov_table {
  .indeterminate {
    .el-checkbox__input {
      .el-checkbox__inner {
        background-color: #5f4efd;
        border-color: #5f4efd;
        &::before {
          content: '';
          position: absolute;
          display: block;
          background-color: #fff;
          height: 2px;
          -webkit-transform: scale(.5);
          transform: scale(.5);
          left: 0;
          right: 0;
          top: 5px;
        }
      }
    }
  }
}
</style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/152248
推荐阅读
相关标签
  

闽ICP备14008679号