Element-plus虚拟表格 el-table-v2自定义排序功能,添加边框,选择框

由于数据量过大,且需求是不允许分页,需要大量操作数据,需要使用el-table-v2虚拟表格来完成,但是他的样式以及功能与el-table有些差异,查找很多文档,并没有与我的需求相吻合的,需要自己来完成,记录一下,方便大家参考

成果截图

代码放在下面了

基本结构

<div v-loading="loading" style="height: calc(100vh - 84px - 140px)">
      <el-auto-resizer>
        <template #default="{ height, width }">
          <el-table-v2
            :cache="8"
            :columns="columns"
            :data="sysInvoiceList"
            :width="width"
            :height="height"
            :row-height="58"
            fixed
            @scroll="tableScroll"
          />
        </template>
      </el-auto-resizer>
    </div>

ElDicTag是我的内部组件,请注意不要引入、使用 

import { TableV2FixedDir, Column } from 'element-plus';
import ElDicTag from '@/components/DictTag/index.vue';
import { CaretTop, CaretBottom } from '@element-plus/icons-vue';

 自定义选择列单元格组件

function SelectionCell({ value, indeterminate = false, onChange, disabled = false }) {
  return h(ElCheckbox, {
    onChange: onChange,
    modelValue: value,
    preventDefault: true,
    indeterminate,
    disabled,
    onClick: (e) => e.stopPropagation() //阻止冒泡
  });
}

 自定义单元格排序组件

function SortIconCell(title: string, key) {
  let { isAsc, orderByColumn } = queryParams.value;
  return h(
    'div',
    {
      class: 'customize_header',
      onClick: ($event) => {
        customizeSortChange({ $event, prop: key });
      }
    },
    [
      h('div', {}, title),
      h(
        'div',
        {
          class: 'sort_view'
        },
        [
          h('i', {
            class: ['sort-caret ascending', { 'is_active_sort_button': isAsc == 'ascending' && orderByColumn == key }],
            onClick: ($event) => {
              customizeSortChange({ $event, order: 'ascending', prop: key });
            }
          }),
          h('i', {
            class: ['sort-caret descending', { 'is_active_sort_button': isAsc == 'descending' && orderByColumn == key }],
            onClick: ($event) => {
              customizeSortChange({ $event, order: 'descending', prop: key });
            }
          })
        ]
      )
    ]
  );
}

自定义单元格排序方法

const toggleOrder = ({ order, prop }) => {
  let { orderByColumn } = queryParams.value;
  if (orderByColumn != prop) return 'descending';
  let sortOrders = ['descending', 'ascending', undefined];
  const index = sortOrders.indexOf(order);
  if (prop == 'djrq') sortOrders.pop();
  return sortOrders[index == sortOrders.length - 1 ? 0 : index + 1];
};
const customizeSortChange = ({ $event, order, prop }) => {
  event.stopPropagation();
  let { isAsc, orderByColumn } = queryParams.value;
  let isActive = isAsc == order && orderByColumn == prop;
  queryParams.value.isAsc = isActive ? undefined : order || toggleOrder({ order: isAsc, prop });
  queryParams.value.orderByColumn = isActive ? undefined : prop;
  queryParams.value.pageNum = 1;
  getList();
};

colums配置

const columns: Column[] = [
  {
    key: 'selection',
    width: 55,
    fixed: TableV2FixedDir.LEFT,
    align: 'center',
    cellRenderer: ({ rowData }) => {
      //必须添加这一句不然出现渲染错误,页面展示的就是复选框选中跟着滚动条一起动
      if (rowData.checked == undefined) rowData.checked = false;
      const onChange = (value: boolean) => {
        rowData.checked = value;
        value ? ids.value.push(rowData.id) : (ids.value = ids.value.filter((e) => e != rowData.id));
        single.value = ids.value.length != 1;
        multiple.value = !ids.value.length;
      };
      return h(SelectionCell, { value: rowData.checked, onChange });
    },
    headerCellRenderer: () => {
      const _data = sysInvoiceList.value;
      const onChange = (value) => {
        sysInvoiceList.value = _data.map((row) => {
          row.checked = value;
          return row;
        });
        ids.value = sysInvoiceList.value.filter((item) => item.checked).map((item) => item.id);
        single.value = ids.value.length != 1;
        multiple.value = !ids.value.length;
      };
      const allSelected = _data.length > 0 ? _data.every((row) => row.checked) : false;
      const containsChecked = _data.some((row) => row.checked);
      return h(SelectionCell, {
        value: allSelected,
        intermediate: containsChecked && !allSelected,
        onChange,
        disabled: _data.length > 0 ? false : true
      });
    }
  },
  {
    dataKey: 'djbh',
    key: 'djbh',
    width: 150,
    align: 'center',
    class: 'table_djbh',
    fixed: TableV2FixedDir.LEFT,
    headerCellRenderer: ({ column }) => SortIconCell('单据编号', column.dataKey)
  },
  {
    dataKey: 'khmc',
    key: 'khmc',
    width: 200,
    align: 'center',
    headerCellRenderer: ({ column }) => SortIconCell('购方名称', column.dataKey)
  },
  { dataKey: 'khsh', key: 'khsh', width: 200, align: 'center', headerCellRenderer: ({ column }) => SortIconCell('购方税号', column.dataKey) },
  {
    dataKey: 'fplxdm',
    key: 'fplxdm',
    title: '发票类型',
    width: 150,
    align: 'center',
    cellRenderer: ({ rowData }) => {
      return h(ElDicTag, {
        options: sys_invoice_type.value,
        value: rowData.fplxdm
      });
    }
  },
  {
    dataKey: 'status',
    key: 'status',
    title: '开票状态',
    width: 100,
    align: 'center',
    cellRenderer: ({ rowData }) => {
      return h(ElDicTag, {
        options: sys_invoice_kpzt.value,
        value: rowData.status
      });
    }
  },
  {
    dataKey: 'kz1',
    key: 'kz1',
    title: '渠道',
    width: 100,
    align: 'center',
    cellRenderer: ({ rowData }) => {
      return h(ElDicTag, {
        options: sys_invoice_vtweg.value,
        value: rowData.kz1
      });
    }
  },
  {
    dataKey: 'kz2',
    key: 'kz2',
    title: '工厂',
    width: 100,
    align: 'center',
    cellRenderer: ({ rowData }) => {
      return h(ElDicTag, {
        options: sys_invoice_werks.value,
        value: rowData.kz2
      });
    }
  },
  { dataKey: 'khlxfs', key: 'khlxfs', title: '购方联系方式', width: 140, align: 'center' },
  { dataKey: 'xfsh', key: 'xfsh', title: '销方税号', width: 200, align: 'center' },
  {
    dataKey: 'djrq',
    key: 'djrq',
    title: '单据日期',
    width: 170,
    align: 'center',
    headerCellRenderer: ({ column }) => SortIconCell('单据日期', column.dataKey)
  },
  { dataKey: 'je', key: 'je', title: '净值', width: 100, align: 'center', headerCellRenderer: ({ column }) => SortIconCell('净值', column.dataKey) },
  {
    dataKey: 'jshj',
    key: 'jshj',
    title: '价税合计',
    width: 100,
    align: 'center',
    headerCellRenderer: ({ column }) => SortIconCell('价税合计', column.dataKey)
  },
  { dataKey: 'se', key: 'se', title: '税额', width: 100, align: 'center', headerCellRenderer: ({ column }) => SortIconCell('税额', column.dataKey) },
  {
    dataKey: 'tax',
    key: 'tax',
    title: '税率',
    width: 100,
    align: 'center',
    cellRenderer: ({ rowData }) => {
      return h(ElDicTag, {
        options: sys_invoice_sl.value,
        value: rowData.tax
      });
    }
  },
  {
    dataKey: 'yxj',
    key: 'yxj',
    title: '操作',
    width: 146,
    align: 'center',
    style: { border: 'none' },
    fixed: TableV2FixedDir.RIGHT,
    headerCellRenderer: ({ column }) => {
      return h('div', { class: 'small-padding' }, ['操作', h('div', { class: 'icon_view', onClick: () => (drawer.value = true) }, h(Filter))]);
    },
    cellRenderer: ({ rowData }) => {
      return h('div', {}, [
        h(ElButton, { type: 'primary', size: 'small', onClick: () => handleLook(rowData) }, () => '查看'),
        h(ElButton, { type: 'primary', size: 'small', onClick: () => handleUpdate(false, rowData) }, () => '编辑')
      ]);
    }
  }
];

计算滚动条位置来判断是否展示固定列的阴影样式

let tableV2Style = {
  none: null,
  left: '2px 0 4px 0 rgba(0, 0, 0, 0.06)',
  right: '-2px 0 4px 0 rgba(0, 0, 0, 0.06)',
  clientWidth: 0,
  scrollWidth: 0
};
const tableScroll = ({ scrollLeft }) => {
  if (!scrollLeft) {
    let domLeft = document.getElementsByClassName('el-table-v2__left')[0];
    domLeft.style.boxShadow = tableV2Style.none = 'none';
  } else if (scrollLeft + tableV2Style.scrollWidth >= tableV2Style.clientWidth) {
    let domRight = document.getElementsByClassName('el-table-v2__right')[0];
    domRight.style.boxShadow = tableV2Style.none = 'none';
  } else {
    if (!tableV2Style.none) return;
    let domLeft = document.getElementsByClassName('el-table-v2__left')[0];
    let domRight = document.getElementsByClassName('el-table-v2__right')[0];
    tableV2Style.none = null;
    domLeft.style.boxShadow = tableV2Style.left;
    domRight.style.boxShadow = tableV2Style.right;
  }
};
onMounted(async () => {
  await getList();
  // 通过异步获取dom元素,防止获取为空
  setTimeout((v) => {
    let dom = document.getElementsByClassName('el-table-v2__header-wrapper')[0];
    tableV2Style.clientWidth = dom.children[0].clientWidth;
    tableV2Style.scrollWidth = dom.clientWidth;
  }, 100);
});

设置样式

::v-deep {
  .el-auto-resizer {
    border: 1px solid #ebeef5;
  }
  .el-table-v2__row-cell.is-align-center {
    border-right: 1px solid #ebeef5;
  }
  .el-table-v2__header-wrapper {
    position: relative;
    background-color: #f8f8f9;
    .icon_view {
      position: absolute;
      background-color: #fff;
      width: 27px;
      height: 27px;
      border-bottom-left-radius: 20px;
      top: 0;
      right: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      cursor: pointer;
      :first-child {
        height: 15px;
        width: 15px;
        margin-left: 3px;
      }
    }
  }
  .el-table-v2__header-cell {
    border-right: 1px solid #ebeef5;
    background-color: #f8f8f9;
    color: #515a6e !important;
  }
  .el-table-v2__cell-text {
    color: #606266 !important;
  }
  .el-table-v2__left {
  }
  .el-table-v2__right {
    box-shadow: -2px 0 4px 0 rgba(0, 0, 0, 0.06);
  }
  .customize_header {
    width: 100%;
    height: 100%;
    display: flex;
    cursor: pointer;
    justify-content: center;
    align-items: center;
  }
  .sort_view {
    align-items: center;
    display: flex;
    flex-direction: column;
    height: 14px;
    overflow: initial;
    position: relative;
    vertical-align: middle;
    width: 24px;
    top: 1px;
  }
  .sort-caret {
    border: 5px solid transparent;
    height: 0;
    left: 7px;
    position: absolute;
    width: 0;
  }
  .sort-caret.ascending {
    border-bottom-color: #a8abb2;
    top: -5px;
  }
  .sort-caret.descending {
    border-top-color: #a8abb2;
    bottom: -3px;
  }
  .is_active_sort_button.ascending {
    border-bottom-color: #409eff;
  }
  .is_active_sort_button.descending {
    border-top-color: #409eff;
  }
}

目前功能全部实现,Element-plus内部有bug我已经向官方提出,正在修改中

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值