el-table递归表头的坑

错误写法

在编写el-table遇到的坑特此记录

vue 复制代码
<script setup lang="ts">
  const tableProps = [
    {
      label: "来电",
      props: "call",
      children: null,
    },
    {
      label: "姓名",
      props: "name",
      children: null,
    },
    {
      label: "年龄",
      props: "age",
      children: null,
    },
    {
      label: "邮箱",
      props: "email",
      children: null,
    },
    {
      label: "家庭",
      props: null,
      children: [
        {
          label: "父亲",
          props: "fater",
          children: null,
        },
        {
          label: "母亲",
          props: "mother",
          children: null,
        },
      ],
    },
  ];
  </script>
  <template>
    <el-table
        :cell-style="{
          textAlign: 'center',
        }"
        :header-cell-style="{
          textAlign: 'center',
          background: '#f2f3f2',
          color: '#000',
        }"
        :data="data"
      >
        <el-table-column type="index" label="序号" width="100" />
        <TableColumn :tableProps="tableProps" />
      </el-table>
  </template>
vue 复制代码
  TableColumn.vue
  <script setup lang="tsx">
  defineOptions({
    name: "TableColumn",
  });
  type PropsGroup =
    | {
        label: string;
        props: string;
        children: null;
      }
    | {
        label: string;
        props: null;
        children: PropsGroup[];
      };
  interface PropsPaginantion {
    tableProps: PropsGroup[];
  }
  const props = withDefaults(defineProps<PropsPaginantion>(), {
    tableProps: () => [],
  });
  onMounted(() => {
    console.log(props.tableProps);
  });
</script>
<template>
  <template
    v-for="(item, index) in props.tableProps"
    :key="item.props || index"
  >
    <el-table-column
      v-if="typeof item.props === 'string'"
      :label="item.label"
      :prop="item.props"
    />
    <el-table-column
      :label="item.label"
      v-else-if="'children' in item && Array.isArray(item.children)"
    >
      <TableColumn :table-props="item.children" />
    </el-table-column>
  </template>
</template>
<style scoped lang="scss"></style>

该写法总体逻辑是采用递归组件去判断children是否有值进行自我调用,但页面渲染并没有第二层

原因未知,但deubgger编译后的代码中发现第二层级执行过

解决思路

使用tsx编写组件

tsx 复制代码
import { ElTable, ElTableColumn } from "element-plus";
type PropsGroup =
  | {
      label: string;
      props: string;
      children: null;
    }
  | {
      label: string;
      props: null;
      children: PropsGroup[];
    };

const TableHeaderCreate = defineComponent({
  props: {
    data: Array,
    tableProps: {
      type: Array<PropsGroup>,
      require: true,
    },
    cellStyle: Object,
    headerCellStyle: Object,
  },
  setup(props, ctx) {
    const tablePropsConfig = props.tableProps || [];
    const generateColumns = (tableProps: PropsGroup[]) => {
      return tableProps.map((item) => {
        if (item.children) {
          return (
            <ElTableColumn label={item.label}>
              {generateColumns(item.children)}
            </ElTableColumn>
          );
        } else {
          return <ElTableColumn label={item.label} prop={item.props} />;
        }
      });
    };

    return () => (
      <>
        <ElTable
          cellStyle={props.cellStyle}
          headerCellStyle={props.headerCellStyle}
          data={props.data}
        >
          {generateColumns(tablePropsConfig)}
        </ElTable>
      </>
    );
  },
});
export default TableHeaderCreate;

通过封装成单独组件在通过函数递归实现表头递归(tsx还是比template灵活)

方案二 template中使用h函数

vue 复制代码
<script setup lang="tsx">
  defineOptions({
    name: "TableColumn",
  });
  type PropsGroup =
    | {
        label: string;
        props: string;
        children: null;
      }
    | {
        label: string;
        props: null;
        children: PropsGroup[];
      };
  interface PropsPaginantion {
    tableProps: PropsGroup[];
    data: Array<any>;
  }
  const props = withDefaults(defineProps<PropsPaginantion>(), {
    tableProps: () => [],
  });
  const generateColumns = (pro: { table: PropsGroup[] }) => {
    return pro.table.map((item) => {
      if (item.children) {
        return h(
          ElTableColumn,
          { label: item.label },
          h(generateColumns, {
            table: item.children,
          })
        );
      } else {
        return h(ElTableColumn, { label: item.label, prop: item.props });
      }
    });
  };
</script>
<template>
  <el-table>
    <el-table-column type="index" label="序号" width="100" />
    <component :is="generateColumns" :table="props.tableProps" />
  </el-table>
</template>
<style scoped lang="scss"></style>

核心逻辑还是generateColumns函数递归只是使用了component搭配h函数实现,本质上tsx是该写法的便捷版。

相关推荐
天天鸭2 分钟前
都2025了你不会用‘容器查询’适配屏幕?,只知道媒体查询?
前端·javascript·vue.js
晓得迷路了6 分钟前
栗子前端技术周刊第 76 期 - ChatUI 3.0、Tailwind CSS v4.1、Next.js 15.3...
前端·javascript·react.js
植物系青年11 分钟前
前端性能优化全链路实践方案
前端·性能优化
植物系青年14 分钟前
前端 SSE 流式传输技术 🌊 对话大模型最佳拍档!
前端
Kagol15 分钟前
🎉TinyPro v1.2.0 正式发布,趁着 TinyPro 项目刚创建不久,快来参与贡献(蹭 PR)吧!
前端·vue.js·nestjs
洛小豆18 分钟前
想不到吧,这才是浏览器扩展中关闭窗口的正确姿势!
前端·javascript·chrome
Michael.Scofield26 分钟前
vue: router基础用法
前端·javascript·vue.js
excel29 分钟前
webpack 模块 第 五 节
前端
excel38 分钟前
webpack 模块 第 四 节
前端
好_快1 小时前
Lodash源码阅读-take
前端·javascript·源码阅读