el-table 合并单元格、多选、编辑

1,199 阅读5分钟

表格中,针对相同数据进行合并。这个时候,我们需要对表格进行处理。

1,最简单的合并,如下图:

1666937916163.jpg

2,稍微复杂的合并,如下图:

1666938119279.jpg

3,再稍微复杂的合并【增加了多选框】,如下图:

1666937636899.jpg

4,再稍微复杂的合并【增加了编辑单元格】,如下图:

1666937476297.jpg

【不难发现】这些表格,都是以某个维度(基础列),进行合并单元格的。比如,第4个图,根据“出库单号”进行合并的。就会出现了,“退货理由”这一栏,不会多行合并。

说这么多,现在上代码。

/**
 *  table合并行通用 */
export function mergeTableRow(config) {
  let data = config.data
  const { mergeColNames, firstMergeColNames, firstMerge } = config
  if (!mergeColNames || mergeColNames.length === 0) {
    return data
  }
  mergeColNames.forEach((m) => {
    const mList = {}
    data = data.map((v, index) => {
      // 区分需要合并行的key的值
       const rowVal = v[firstMerge] + '-' + v[m]  // 【重点】
      // 需要合并行的第二行以及之后行会走if
      // m==firstMerge 判断需要合并的列是否是基准列,如果是则只满足前面的条件,如果不是则需要满足前面+后面的条件
      if (mList[rowVal] && mList[rowVal].newIndex === index) {
        const flag = firstMergeColNames.filter((f) => { return f === m }).length !== 0
        const mcFlag = mergeColNames.filter((mc) => { return mc === firstMerge }).length === 0
        if ((mcFlag && flag) || (flag && data[index][firstMerge + '-span'] && data[index][firstMerge + '-span'].rowspan === 1)) {
          v[m + '-span'] = {
            rowspan: 1,
            colspan: 1
          }
        } else {
          data[mList[rowVal].index][m + '-span'].rowspan++
          v[m + '-span'] = {
            rowspan: 0,
            colspan: 0
          }
          mList[rowVal].num++
          mList[rowVal].newIndex++
        }
      } else {
        mList[rowVal] = { num: 1, index: index, newIndex: index + 1 }
        v[m + '-span'] = {
          rowspan: 1,
          colspan: 1
        }
      }
      return v
    })
  })
  return data
}

<1> 还有另一个情况,因为没根据维度区分来进行合并单元格,如下图:

1666938850455.jpg 这个时候,如果不区分维度,只是简单的合并。

  上述方法,【重点】处,改成: const rowVal = v[m]
  
  注释掉:// const rowVal = v[firstMerge] + '-' + v[m]

针对单元格合并的时候,能进行批量选择:
思路:对列表数据进行处理,加个属性


 // 获取列表
    getList() {
      // 添加属性 check 多选框
      issue_records.map(v => {
        v.check = v.saleNo
      })
      setTimeout(() => {
        const mergeColNames = ['check', 'saleNo', 'orderType', 'status', 'issueNo', 'product', 'issueDate']
        const firstMergeColNames = ['saleNo', 'issueNo']
        const firstMerge = 'saleNo'
        this.tableData = cellTableMerge(issue_records, mergeColNames, firstMergeColNames, firstMerge, 'check')
      }, 500);
    },
 // 勾选逻辑
    selectionChange(val) {
      const arr = filterFn(val, this.tableData, 'saleNo')
      this.printList = uniqueFn(arr, 'id')
    },
    rowSelect(rows, row) {
      const flag = rows.length && rows.indexOf(row) !== -1 // 判断当前数据是否勾选
      if (!flag) {
        // 取消勾选
        if (!this.tem.length) {
          // 开始进来
          this.tem = rows.filter(v => v.saleNo != row.saleNo)
        } else {
          // 再次取消勾选
          this.tem = this.tem.filter(v => v.saleNo != row.saleNo)
        }
        setTimeout(() => {
          this.printList = this.tem
        }, 500);
      }
    },
    
    /** 数组A根据某一属性对数组B进行筛选
        * @param {目标数组} a 
        * @param {全量数组} b 
        * @param {变量} name 
        */
     filterFn(a, b, name) {
      const temArr = []
      a.forEach(item => {
        b.forEach(ele => {
          if (item[name] == ele[name]) {
            temArr.push(ele)
          }
        })
      })
      return temArr
    }

<2> 还有一点: 针对合并单元格+编辑,提交编辑后的数据给后端

思路:合并单元格后,点击某一行,进行编辑内容,提交数据的时候,发现有些合并项的数据(编辑)可能没值,这个时候需要获取,该合并的数据(数组),循环把编辑内容后的行数据的某个属性aa的xxx值,赋值到数组每一项的属性aa。

<3> 如果需要排序:

/**
 * 排序
 * 将传入的数组根据当前系统语言,按照中文或英文名重新排序,会影响原数组
 * @param list 必填要排序的list
 *  @param key 必填 属性
 * @param order  排列顺序 asc正序 desc倒序
 * @returns {*}
 */
export function sortArray(list, key, order = 'asc') {
  if (list === undefined || list === null) return []
  list.sort((a, b) => {
    const strA = order == 'asc' ? a[key] : b[key]
    const strB = order == 'asc' ? b[key] : a[key]
    // 谁为非法值谁在前面
    if (strA === undefined || strA === null || strA === '' || strA === ' ' || strA === ' ') {
      return -1
    }
    if (strB === undefined || strB === null || strB === '' || strB === ' ' || strB === ' ') {
      return 1
    }
    // 如果a和b中全部都是汉字,或者全部都非汉字
    if ((strA.split('').every(char => notChinese(char)) && strB.split('').every(char => notChinese(char))) ||
      (strA.split('').every(char => !notChinese(char)) && strB.split('').every(char => !notChinese(char)))) {
      return strA.localeCompare(strB)
    } else {
      const charAry = strA.split('')
      for (const i in charAry) {
        if ((charCompare(strA[i], strB[i]) !== 0)) {
          return charCompare(strA[i], strB[i])
        }
      }
      // 如果通过上面的循环对比还比不出来,就无解了,直接返回-1
      return -1
    }
  })
  return list
}

function charCompare(charA, charB) {
  // 谁为非法值谁在前面
  if (charA === undefined || charA === null || charA === '' || charA === ' ' || charA === ' ') {
    return -1
  }
  if (charB === undefined || charB === null || charB === '' || charB === ' ' || charB === ' ') {
    return 1
  }
  // 如果都为英文或者都为汉字则直接对比
  if ((notChinese(charA) && notChinese(charB)) || (!notChinese(charA) && !notChinese(charB))) {
    return charA.localeCompare(charB)
  } else {
    // 如果不都为英文或者汉字,就肯定有一个是英文,如果a是英文,返回-1,a在前,否则就是b是英文,b在前
    if (notChinese(charA)) {
      return -1
    } else {
      return 1
    }
  }
}

function notChinese(char) {
  const charCode = char.charCodeAt(0)
  return charCode >= 0 && charCode <= 128
}

<4> 最后,总体的代码,合并单元格 + 排序的代码,如下:

/**
 *  el-table 合并行通用 */
export function mergeTableRow(config) {
  let data = config.data
  const { mergeColNames, firstMergeColNames, firstMerge } = config
  if (!mergeColNames || mergeColNames.length === 0) {
    return data
  }
  mergeColNames.forEach((m) => {
    const mList = {}
    data = data.map((v, index) => {
      // 区分需要合并行的key的值
      // const rowVal = v[m]
      const rowVal = v[firstMerge] + '-' + v[m]
      // 需要合并行的第二行以及之后行会走if
      // m==firstMerge 判断需要合并的列是否是基准列,如果是则只满足前面的条件,如果不是则需要满足前面+后面的条件
      if (mList[rowVal] && mList[rowVal].newIndex === index) {
        const flag = firstMergeColNames.filter((f) => { return f === m }).length !== 0
        const mcFlag = mergeColNames.filter((mc) => { return mc === firstMerge }).length === 0
        if ((mcFlag && flag) || (flag && data[index][firstMerge + '-span'] && data[index][firstMerge + '-span'].rowspan === 1)) {
          v[m + '-span'] = {
            rowspan: 1,
            colspan: 1
          }
        } else {
          data[mList[rowVal].index][m + '-span'].rowspan++
          v[m + '-span'] = {
            rowspan: 0,
            colspan: 0
          }
          mList[rowVal].num++
          mList[rowVal].newIndex++
        }
      } else {
        mList[rowVal] = { num: 1, index: index, newIndex: index + 1 }
        v[m + '-span'] = {
          rowspan: 1,
          colspan: 1
        }
      }
      return v
    })
  })
  return data
}

/**
 * 排序
 * 将传入的数组根据当前系统语言,按照中文或英文名重新排序,会影响原数组
 * @param list 必填要排序的list
 *  @param key 必填 属性
 * @param order  排列顺序 asc正序 desc倒序
 * @returns {*}
 */
export function sortArray(list, key, order = 'asc') {
  if (list === undefined || list === null) return []
  list.sort((a, b) => {
    const strA = order == 'asc' ? a[key] : b[key]
    const strB = order == 'asc' ? b[key] : a[key]
    // 谁为非法值谁在前面
    if (strA === undefined || strA === null || strA === '' || strA === ' ' || strA === ' ') {
      return -1
    }
    if (strB === undefined || strB === null || strB === '' || strB === ' ' || strB === ' ') {
      return 1
    }
    // 如果a和b中全部都是汉字,或者全部都非汉字
    if ((strA.split('').every(char => notChinese(char)) && strB.split('').every(char => notChinese(char))) ||
      (strA.split('').every(char => !notChinese(char)) && strB.split('').every(char => !notChinese(char)))) {
      return strA.localeCompare(strB)
    } else {
      const charAry = strA.split('')
      for (const i in charAry) {
        if ((charCompare(strA[i], strB[i]) !== 0)) {
          return charCompare(strA[i], strB[i])
        }
      }
      // 如果通过上面的循环对比还比不出来,就无解了,直接返回-1
      return -1
    }
  })
  return list
}

function charCompare(charA, charB) {
  // 谁为非法值谁在前面
  if (charA === undefined || charA === null || charA === '' || charA === ' ' || charA === ' ') {
    return -1
  }
  if (charB === undefined || charB === null || charB === '' || charB === ' ' || charB === ' ') {
    return 1
  }
  // 如果都为英文或者都为汉字则直接对比
  if ((notChinese(charA) && notChinese(charB)) || (!notChinese(charA) && !notChinese(charB))) {
    return charA.localeCompare(charB)
  } else {
    // 如果不都为英文或者汉字,就肯定有一个是英文,如果a是英文,返回-1,a在前,否则就是b是英文,b在前
    if (notChinese(charA)) {
      return -1
    } else {
      return 1
    }
  }
}

function notChinese(char) {
  const charCode = char.charCodeAt(0)
  return charCode >= 0 && charCode <= 128
}

/**
 * 合并处理
 * @param data 原始数组
 *  @param mergeColNames 必填  [需要合并的列,默认合并列相同的数据]
 *  @param firstMergeColNames 可选 [受影响的列,只合并以firstMerge为首的同类型数据]
 *  @param firstMerge 可选 [以哪列为基础进行合并,一般为第一列]
 *  @param property 按照xx属性排序
 * @returns {*}
 */
export function cellTableMerge(data, mergeColNames, firstMergeColNames, firstMerge, property, order) {
  return mergeTableRow({
    // 给table赋值,重新遍历新增rowSpan属性,checkRoom,appointmentTime为table里面需要合并的属性名称
    data: sortArray(data, property, order),
    mergeColNames,
    firstMergeColNames,
    firstMerge
  })
}

完结,记录下遇到的表格处理。例外,一些参考文献:
vxe-table处理复杂的表格,一般满足业务中的场景