关于二次封装element-plus table的一些归纳总结(3)

486 阅读9分钟

紧接上文,这篇文章将展示最后的几个示例,下一篇文章则着重分析封装的element-table的实现原理。

示例15--表头分组(多级表头)

通过给列数据添加children属性,该属性也是一个对象数组,并和一级一样,添加列属性的配置即可实现表头分组(多级表头),也就是说我们的列数据可以是一个对象数组,同样的也可以是树形结构的数组。配置数据代码如下所示:

import { ElTableColumnProps } from '../components/tableProps';
const column: ElTableColumnProps[] = [
  {
    prop: "date",
    width: 120,
    label: "Date",
  },
  {
    label: "Delivery Info",
    children: [
      {
        prop: "name",
        label: "Name",
      },
      {
        label: "Address Info",
        children: [
          {
            prop: "state",
            label: "State",
          },
          {
            prop: "city",
            label: "City",
            width: 120,
          },
          {
            prop: "address",
            label: "Address",
          },
          {
            prop: "zip",
            width: 120,
            label: "Zip",
          },
        ],
      },
    ],
  },
]
const tableData = [
  /*数据忽略*/
]

html代码没什么变化,这样就可以实现一个多级表头了。

以上代码实现效果展示如下图所示:

19.png

示例16--修改表格的布局

通过设置table-layout的属性可以修改表格的布局,该属性的值可以是fixed或者auto,来看如下一个示例:

<el-radio-group v-model="tableLayout">
    <el-radio-button value="fixed">fixed</el-radio-button>
    <el-radio-button value="auto">auto</el-radio-button>
</el-radio-group>
<element-table
    :column="column"
    :data="tableData"
    :table-layout="tableLayout"
></element-table>

ts代码如下所示:

import { ref } from "vue";

const tableLayout = ref<"fixed" | "auto">("fixed");

const column = [
  // 列数据配置忽略 
];

const tableData = [
  // 数据忽略
];

以上代码实现效果展示如下图所示:

20.png

示例17--多选表格

通过给列数据配置一项type为'selection'的对象,则可以开启表格多选列,并且我们还可以通过表格实例提供的toggleRowSelection方法来设置对应的行数据是否选中,该方法支持传入2个参数,第一个参数为单行数据,第二个参数是一个布尔值,表示是否选中,同样我们也可以使用表格实例提供的clearSelection方法,清除所有选中。

通过selection-change事件可以监听选中事件,该事件方法返回选中项值,应该是一个数组。

接下来我们来看这个示例,我们用一个ref存储表格实例,然后一般多选列都是放在第一列,因此表格列数据的第一项就是配置的多选列对象。

html代码如下所示:

    ref="multipleTableRef"
    :data="tableData"
    style="width: 100%"
    :column="column"
    @selection-change="handleSelectionChange"
  >
    <template #default="scope">Date:{{ scope.row.date }}</template>
  </element-table>
  <div style="margin-top: 20px">
    <el-button @click="toggleSelection([tableData[1], tableData[2]])"
      >Toggle selection status of second and third rows</el-button
    >
    <el-button @click="toggleSelection()">Clear selection</el-button>
  </div>

ts代码如下所示:

import { ref } from "vue";
import type { ElTable } from "element-plus";

interface User {
  date: string;
  name: string;
  address: string;
}

const multipleTableRef = ref<InstanceType<typeof ElTable>>();
const multipleSelection = ref<User[]>([]);
const toggleSelection = (rows?: User[]) => {
  console.log("🚀 ~ file: MultiSelectTable.vue ~ line 32 ~ toggleSelection ~ rows", rows);
  if (rows) {
    rows.forEach(row => multipleTableRef.value!.toggleRowSelection(row, false));
  } else {
    multipleTableRef.value!.clearSelection();
  }
};
const handleSelectionChange = (val: User[]) => {
  multipleSelection.value = val;
};
const column = [
  // 这里是配置多选框
  {
    type: "selection",
    width: 50,
  },
  {
    prop: "date",
    label: "Date",
    width: 120,
    slotName: "default",
  },
  {
    prop: "name",
    label: "Name",
    width: 120,
  },
  {
    prop: "address",
    label: "Address",
    // 表示如果地址名太长,显示不下,会出现提示框
    "show-overflow-tooltip": true,
  },
];
const tableData: User[] = [
   // 数据忽略
];

以上代码实现效果展示如下图所示:

21.png

示例18--合并行或列

通过给table绑定span-method事件可以实现表格的合并行或者列功能,该事件方法的参数是一个对象,分别是行数据,列数据,与对应的行索引值和列索引值。即以下ts类型代码所示:

import type { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";
interface SpanMethodProps {
  row: User;
  column: TableColumnCtx<User>;
  rowIndex: number;
  columnIndex: number;
}

该事件方法可以返回一个包含两个元素的数组,第一个元素代表 rowspan,第二个元素代表 colspan。 也可以返回一个键名为 rowspan 和 colspan 的对象。

来看示例具体代码如下所示:

<div>
    <element-table
      :column="column"
      :data="tableData"
      :span-method="arraySpanMethod"
      border
      style="width: 100%"
    ></element-table>

    <element-table
      :column="column2"
      :data="tableData"
      :span-method="objectSpanMethod"
      border
      style="width: 100%; margin-top: 20px"
    ></element-table>
</div>

ts代码如下所示:

// ...

// 返回数组
const arraySpanMethod = ({  rowIndex, columnIndex }: SpanMethodProps) => {
  if (rowIndex % 2 === 0) {
    if (columnIndex === 0) {
      return [1, 2];
    } else if (columnIndex === 1) {
      return [0, 0];
    }
  }
};
// 返回对象
const objectSpanMethod = ({ rowIndex, columnIndex }: SpanMethodProps) => {
  if (columnIndex === 0) {
    if (rowIndex % 2 === 0) {
      return {
        rowspan: 2,
        colspan: 1,
      };
    } else {
      return {
        rowspan: 0,
        colspan: 0,
      };
    }
  }
};

const column = [
   // 略
];

const column2 = [
   // 略
];

const tableData: User[] = [
 // 略
];

以上代码实现效果展示如下图所示:

22.png

示例19--表格单选

选择单行数据时使用色块表示。

Table 组件提供了单选的支持, 只需要配置 highlight-current-row 属性即可实现单选。 之后由 current-change 事件来管理选中时触发的事件,它会传入 currentRowoldCurrentRow。 如果需要显示索引,可以增加一列数据配置,设置 type 属性为 index 即可显示从 1 开始的索引号。

同时表格实例也提供了setCurrentRow方法来设置当前选中,如果传入对应行数据,则会选中对应行,如果不传,则会取消对应选中。

来看具体的示例代码,如下所示:

 <element-table
    ref="singleTableRef"
    :data="tableData"
    :column="column"
    highlight-current-row
    style="width: 100%"
    @current-change="handleCurrentChange"
  ></element-table>
  <div style="margin-top: 20px">
    <el-button @click="setCurrent(tableData[1])">Select second row</el-button>
    <el-button @click="setCurrent()">Clear selection</el-button>
  </div>

ts代码如下所示:

import { ref } from "vue";
import type { ElTable } from "element-plus";
const column = [
  // 略
];

// ...
// 核心逻辑在这里
const currentRow = ref();
const singleTableRef = ref<InstanceType<typeof ElTable>>();

const setCurrent = (row?: User) => {
  singleTableRef.value!.setCurrentRow(row);
};
const handleCurrentChange = (val: User | undefined) => {
  currentRow.value = val;
};
const tableData: User[] = [
  // 略
];

以上代码实现效果展示如下图所示:

23.png

示例20--排序

在列中设置 sortable 属性即可实现以该列为基准的排序, 接受一个 Boolean,默认为 false。 可以通过 Table 的 default-sort 属性设置默认的排序列和排序顺序。 可以使用 sort-method 或者 sort-by 使用自定义的排序规则。 如果需要后端排序,需将 sortable 设置为 custom,同时在 Table 上监听 sort-change 事件, 在事件回调中可以获取当前排序的字段名和排序顺序,从而向接口请求排序后的表格数据。 我们还可以使用了 formatter 属性,它用于格式化指定列的值, 接受一个 Function,会传入两个参数:row 和 column, 可以根据自己的需求进行处理。

来看一个简单的示例,如下所示:

  <element-table
    :data="tableData"
    :column="column"
    :default-sort="{ prop: 'date', order: 'descending' }"
    style="width: 100%"
  ></element-table>

ts代码如下所示:

interface User {
  date: string;
  name: string;
  address: string;
}
const formatter = (row: User) => row.address;

const column = [
  {
    prop: "date",
    label: "Date",
    width: 120,
    // sortable还可以设置为custom
    sortable: true,
  },
  {
    prop: "name",
    label: "Name",
    width: 120,
  },
  {
    prop: "address",
    label: "Address",
    formatter,
  },
];

const tableData: User[] = [
  // 略
];

以上代码实现效果展示如下图所示:

24.png

示例21--带状态表格

我们还可将表格内容 highlight 显示,方便区分「成功、信息、警告、危险」等内容。

具体就是通过指定 Table 组件的 row-class-name 属性来为 Table 中的某一行添加 class, 这样就可以自定义每一行的样式了。

如以下这个示例:

<element-table :column="column" :data="data" :row-class-name="tableRowClassName"></element-table>

ts代码如下所示:

const data = [
  // 略
]
const column = [
  // 略
]
function tableRowClassName({ rowIndex }: { rowIndex: number }) {
  if (rowIndex === 1) {
    return "warning-row";
  } else if (rowIndex === 3) {
    return "success-row";
  }
  return "";
}

css代码如下所示:

.el-table .warning-row {
  background: oldlace;
}

.el-table .success-row {
  background: #f0f9eb;
}

以上代码实现效果展示如下图所示:

25.png

示例22--斑马纹表格

使用带斑马纹的表格,可以更容易区分出不同行的数据。

stripe 可以创建带斑马纹的表格。 如果 true, 表格将会带有斑马纹。

示例代码如下所示:

<element-table stripe :column="column" :data="tableData" />

ts代码略,以上代码实现效果展示如下图所示:

26.png

示例23--表尾合计行

若表格展示的是各类数字,可以在表尾显示各列的合计。

将 show-summary 设置为true就会在表格尾部展示合计行。 默认情况下,对于合计行,第一列不进行数据求合操作,而是显示「合计」二字(可通过sum-text配置),其余列会将本列所有数值进行求合操作,并显示出来。当然,你也可以定义自己的合计逻辑。使用 summary-method 并传入一个方法,这个方法的参数返回一个配置列和表格数据的对象,方法结果返回一个数组,这个数组中的各项就会显示在合计行的各列中。

来看具体的示例代码:

<element-table
    :column="column"
    :data="tableData"
    border
    show-summary
    style="width: 100%"
  ></element-table>
  <element-table
    :column="column2"
    :data="tableData"
    border
    height="200"
    :summary-method="getSummaries"
    show-summary
    style="width: 100%; margin-top: 20px"
  ></element-table>

ts代码如下所示:

import type { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";

interface Product {
  id: string;
  name: string;
  amount1: string;
  amount2: string;
  amount3: number;
}

interface SummaryMethodProps<T = Product> {
  columns: TableColumnCtx<T>[];
  data: T[];
}
// 核心代码
const getSummaries = (param: SummaryMethodProps) => {
  const { columns, data } = param;
  const sums: string[] = [];
  columns.forEach((column, index) => {
    if (index === 0) {
      sums[index] = "Total Cost";
      return;
    }
    const values = data.map(item => Number(item[column.property]));
    if (!values.every(value => isNaN(value))) {
      sums[index] = `$ ${values.reduce((prev, curr) => {
        const value = Number(curr);
        if (!isNaN(value)) {
          return prev + curr;
        } else {
          return prev;
        }
      }, 0)}`;
    } else {
      sums[index] = "N/A";
    }
  });

  return sums;
};

const column = [
  // 略
];

const column2 = [
  // 略
];

const tableData: Product[] = [
   // 略
];

以上代码实现效果展示如下图所示:

27.png

示例24--树形数据与懒加载

当 row 中包含 children 字段时,被视为树形数据。渲染嵌套数据需要 prop 的 row-key。 此外,子行数据可以异步加载。设置 Table 的lazy属性为 true 与加载函数 load 。 通过指定 row 中的hasChildren字段来指定哪些行是包含子节点。 children 与hasChildren都可以通过 tree-props 配置。同样的也可以设置default-expand-all属性,表示默认展开所有数据。

来看示例代码如下所示:

 <div>
    <element-table
      :column="column"
      :data="tableData"
      style="width: 100%; margin-bottom: 20px"
      row-key="id"
      border
      default-expand-all
    ></element-table>

    <element-table
      :column="column2"
      :data="tableData1"
      style="width: 100%"
      row-key="id"
      border
      lazy
      :load="load"
      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
    >
    </element-table>
  </div>

ts代码如下所示:

interface User {
  id: number;
  date: string;
  name: string;
  hasChildren?: boolean;
  children?: User[];
}
// 加载函数
const load = (_row: User, _treeNode: unknown, resolve: (date: User[]) => void) => {
  setTimeout(() => {
    resolve([
      {
        id: 31,
        date: "2016-05-01",
        name: "wangxiaohu",
      },
      {
        id: 32,
        date: "2016-05-01",
        name: "wangxiaohu",
      },
    ]);
  }, 1000);
};
const column = [
  // ...
];
const column2 = [
  //...
];
const tableData: User[] = [
  //...
  {
    id: 3,
    date: "2016-05-01",
    name: "wangxiaohu",
    children: [
      {
        id: 31,
        date: "2016-05-01",
        name: "wangxiaohu",
      },
      {
        id: 32,
        date: "2016-05-01",
        name: "wangxiaohu",
      },
    ],
  },
  //...
];

const tableData1: User[] = [
  // ...
  {
    id: 3,
    date: "2016-05-01",
    name: "wangxiaohu",
    hasChildren: true,
  },
  // ...
];

以上代码实现效果展示如下图所示:

28.png

所有示例介绍完毕,下一篇文章我将介绍封装的实现原理,让我们下篇文章再见。