相关文章推荐
叛逆的脸盆  ·  SQL Server ...·  6 月前    · 
含蓄的大海  ·  为什么不能import ...·  1 年前    · 

使用过antd表格组件的,应该会被他那种风格吸引,一个表格只需要传入一个column就可以了,我在使用element表格组件的时候就有点不习惯,所以自己封装了一个类似与antd表格组件。

封装这个table表格的思路

这个组件是用vue3.0+ts+jsx语法实现的,因为vue3.0官网上实现了支持JSX语法。

  • 首先得在tsconfig.json文件中配置jsx。
  • 封装table时怎么模拟antd表格中的render方法。
  • JSX语法中怎么使用vue中的插槽。
  • 首先我们这个table需要外部传入什么? 表格的配置项:tableOptions 表格每一项的配置项: tableItemOptions 表格需要的数据:tableData <script lang= "tsx" > import { defineComponent, ref, nextTick } from "vue" ; import moment from "moment" ; import { ElTable } from "element-plus" ; export default defineComponent ({ props : { tableItemOptions : { type : Array , required : true , tableData : { type : Array , required : true , tableOptions : { type : Object , setup ( props ) { // 类型处理事件Map 在map中定义好类型及处理类型的方法,直接根据传入的配置项进行事件调用 const mapHandler = new Map ([ [ "render" , renderHandler], [ "text" , textHandler], [ "operation" , operationHandler], [ "date" , dateHandler], [ "switch" , switchHandler], [ "number" , numberHandler], // 判断类型调用方法 const typeRenderHandler = ( item: tableItemBaseType ) => { let type = item. type as string; if (mapHandler. has (type)) { return mapHandler. get (type)?. call ( null , item); } else { return mapHandler. get ( "text" )?. call ( null , item); return () => ( < el-table data = {props.tableData} border = {false} { ...props.tableOptions } style = {{ width: " 100 %", height: " 100 %" }} {tableItemOptions.map((item) => typeRenderHandler(item))} </ el-table > </script>

    下一步开始写每一种数组类型的事件处理

    // 文本类型的处理事件
        const textHandler = (item: tableItemBaseType) => {
          const cb = (scope: { row: { [x: string]: any } }) => (
            <span>{scope.row[item.key]}</span>
          return elTableColumnTpl(item, cb);
        // 时间类型的处理方法
        const dateHandler = (item: tableItemBaseType) => {
          const cb = (scope: { row: { [x: string]: any } }) => (
              {scope.row[item.key]
                ? moment(scope.row[item.key]).format(item.format || "YYYY-MM-DD")
                : ""}
            </span>
          return elTableColumnTpl(item, cb);
        // switch开关的处理方法
        const switchHandler = (item: tableItemBaseType) => {
          const cb = (scope: { row: { [x: string]: any } }) => (
            <el-switch
              v-model={scope.row[item.key]}
              {...(item.switchOptions && item.switchOptions)}
          return elTableColumnTpl(item, cb);
        // 操作按钮的处理方法   尽量不要用any 我这里忘记定义类型了,所以直接使用了any
        const operationHandler = (item: any) => {
          const cb = (scope: { row: any; $index: any }) => {
            return (
              <div class="table-button-box">
                {item.operationList.map((buttonItem: operationListType) => (
                  <el-button
                    type={buttonItem.type || null}
                    onClick={buttonItem.handler.bind(
                      null,
                      scope.row,
                      scope.$index,
                      buttonItem?.key
                    {buttonItem.title}
                  </el-button>
              </div>
          return elTableColumnTpl(item, cb);
        // 数字类型的处理方法 尽量不要用any 我这里忘记定义类型了,所以直接使用了any
        const numberHandler = (item: any) => {
          const cb = (scope: { row: any; $index: any }) => {
            return (
                {scope.row[item.key]
                      scope.row[item.key] / (item.numberOptions?.divide || 100)
                    ).toFixed(item.numberOptions?.keepPoint || 2)
                  : "0.00"}
              </span>
          return elTableColumnTpl(item, cb);
        // 表格渲染模板的处理方法 通过回调函数的方式将vue插槽中的数据传入
        const elTableColumnTpl = (item: any, cb: Function) => {
          return (
            <el-table-column
              {...item}
              prop={item.key}
              label={item.title}
              width={item.width || null}
              key={item.key}
              fixed={item.fixed || (item.type === "operation" ? "right" : false)}
                default: (scope: { row: any }) => {
                  if (scope && scope.row) {
                    return cb(scope);
            </el-table-column>
        // 模拟antd中render方法,这里我使用的是组件进行的替换,可以通过传入组件的形式进行个性化的处理
        const renderHandler = (item: any) => {
          return (
            <el-table-column
              {...item}
              prop={item.key}
              label={item.title}
              width={item.width || null}
              key={item.key}
              fixed={item.fixed || (item.type === "operation" ? "right" : false)}
                default: (scope: { row: any }) => {
                  if (scope && scope.row) {
                    return (
                      <item.component
                        scope={scope.row}
                        componentPorps={item.componentPorps}
            </el-table-column>
    

    下边是全部的代码,希望大佬们多多提出意见。

    <script lang="tsx">
    import { defineComponent, ref, nextTick } from "vue";
    import moment from "moment";
    import { ElTable } from "element-plus";
    export default defineComponent({
      props: {
        tableItemOptions: {
          type: Array,
          required: true,
        tableData: {
          type: Array,
          required: true,
        tableOptions: {
          type: Object,
      setup(props) {
        const tableItemOptions: tableItemBaseType[] =
          props.tableItemOptions as unknown as tableItemBaseType[];
        // const tableOptions: tableOptionsType =
        //   props.tableOptions as tableOptionsType;
        const textHandler = (item: tableItemBaseType) => {
          const cb = (scope: { row: { [x: string]: any } }) => (
            <span>{scope.row[item.key]}</span>
          return elTableColumnTpl(item, cb);
        const dateHandler = (item: tableItemBaseType) => {
          const cb = (scope: { row: { [x: string]: any } }) => (
              {scope.row[item.key]
                ? moment(scope.row[item.key]).format(item.format || "YYYY-MM-DD")
                : ""}
            </span>
          return elTableColumnTpl(item, cb);
        const switchHandler = (item: tableItemBaseType) => {
          const cb = (scope: { row: { [x: string]: any } }) => (
            <el-switch
              v-model={scope.row[item.key]}
              {...(item.switchOptions && item.switchOptions)}
          return elTableColumnTpl(item, cb);
        const operationHandler = (item: any) => {
          const cb = (scope: { row: any; $index: any }) => {
            return (
              <div class="table-button-box">
                {item.operationList.map((buttonItem: operationListType) => (
                  <el-button
                    type={buttonItem.type || null}
                    onClick={buttonItem.handler.bind(
                      null,
                      scope.row,
                      scope.$index,
                      buttonItem?.key
                    {buttonItem.title}
                  </el-button>
              </div>
          return elTableColumnTpl(item, cb);
        const numberHandler = (item: any) => {
          const cb = (scope: { row: any; $index: any }) => {
            return (
                {scope.row[item.key]
                      scope.row[item.key] / (item.numberOptions?.divide || 100)
                    ).toFixed(item.numberOptions?.keepPoint || 2)
                  : "0.00"}
              </span>
          return elTableColumnTpl(item, cb);
        const elTableColumnTpl = (item: any, cb: Function) => {
          return (
            <el-table-column
              {...item}
              prop={item.key}
              label={item.title}
              width={item.width || null}
              key={item.key}
              fixed={item.fixed || (item.type === "operation" ? "right" : false)}
                default: (scope: { row: any }) => {
                  if (scope && scope.row) {
                    return cb(scope);
            </el-table-column>
        const renderHandler = (item: any) => {
          return (
            <el-table-column
              {...item}
              prop={item.key}
              label={item.title}
              width={item.width || null}
              key={item.key}
              fixed={item.fixed || (item.type === "operation" ? "right" : false)}
                default: (scope: { row: any }) => {
                  if (scope && scope.row) {
                    return (
                      <item.component
                        scope={scope.row}
                        componentPorps={item.componentPorps}
            </el-table-column>
        // 类型处理事件Map
        const mapHandler = new Map([
          ["render", renderHandler],
          ["text", textHandler],
          ["operation", operationHandler],
          ["date", dateHandler],
          ["switch", switchHandler],
          ["number", numberHandler],
        // 判断类型调用方法
        const typeRenderHandler = (item: tableItemBaseType) => {
          let type = item.type as string;
          if (mapHandler.has(type)) {
            return mapHandler.get(type)?.call(null, item);
          } else {
            return mapHandler.get("text")?.call(null, item);
        const multipleTableRef = ref<InstanceType<typeof ElTable>>();
        const toggleSelection = (rows?: any[]) => {
          if (rows) {
            rows.forEach((row) => {
              // TODO: improvement typing when refactor table
              // eslint-disable-next-line @typescript-eslint/ban-ts-comment
              nextTick(() => {
                multipleTableRef.value!.toggleRowSelection(row, true);
          } else {
            multipleTableRef.value!.clearSelection();
        const filterTableData = () => {
          console.log(props.tableData, ">>>>>>>>>>>>>");
          const arr = props.tableData.filter((item: any) => item.checked);
          toggleSelection(arr);
        if (props.tableOptions?.ref === "multipleTableRef") {
          filterTableData();
        console.log(props.tableOptions);
        return () => (
          <el-table
            data={props.tableData}
            border={false}
            style={{ width: "100%", height: "100%" }}
            {...props.tableOptions}
            {props.tableOptions?.ref === "multipleTableRef" ? (
              <el-table-column type="selection" width="55" />
            ) : null}
            {tableItemOptions.map((item) => typeRenderHandler(item))}
          </el-table>
    </script>
    <style lang="scss" scoped>
    .table-button-box {
      display: flex;
    </style>
    

    下边是ts类型定义文件

    // 会导致全局声明文件失效
    // import { DefineComponent } from "vue";
    declare interface operationListType {
      key?: string;
      type: string;
      handler: (a: any, b: number, c?: string) => void;
      title: string;
    declare interface tableOptionsType {
      height?: number;
      "max-height"?: number;
      border?: boolean;
      stripe?: boolean;
      size?: string;
      fit?: boolean;
    declare interface switchOptionsType {
      "active-text"?: string;
      "inactive-text"?: string;
      "active-color"?: string;
      "inactive-color"?: string;
      disabled?: boolean;
      loading?: boolean;
      size?: string;
      "inline-prompt"?: boolean;
      change?: () => {};
      class: string;
    declare interface numberOptionsType {
      keepPoint: number;
      divide: number;
    declare interface tableItemBaseType {
      key: string;
      title: string;
      type?: string;
      width?: number;
      "min-width"?: number;
      fixed?: string | boolean;
      sortable?: boolean | string;
      resizable?: boolean;
      format?: string;
      componentPorps?: any;
      component?: import("vue").DefineComponent<any, any, any>;
      switchOptions?: switchOptionsType;
      operationList?: operationListType[];
      numberOptions?: numberOptionsType;
    复制代码
  • 私信