vue 动态渲染表格

29 阅读1分钟

在 Vue 中处理动态字段的表格(表头和内容均根据接口返回数据动态生成)

核心思路

  1. 动态解析表头:从接口返回的数据中提取所有的字段(基于首个对象或合并所有对象的键)。
  2. 动态渲染内容:通过 v-for 循环动态生成表格列,并用作用域插槽处理动态字段内容。
// 示例数据
const mockData = [
  { id: 1, name: "Alice", age: 25, email: "alice@example.com" },
  { id: 2, title: "Engineer", salary: 8000 }, // 缺少某些字段
  { product: "Phone", price: 599.99, stock: 100 } // 完全不同的字段
];

1. 动态提取表头

// 动态生成表头(去重后的所有字段)
const tableHeaders = computed(() => {
  const headers = new Set();
  mockData.forEach(item => {
    Object.keys(item).forEach(key => headers.add(key));
  });
  return Array.from(headers);
});

2. 动态渲染表格

<template>
  <el-table :data="tableData" border style="width: 100%">
    <!-- 动态生成表头 -->
    <el-table-column 
      v-for="header in tableHeaders" 
      :key="header" 
      :prop="header" 
      :label="header"
    >
      <!-- 作用域插槽处理动态内容 -->
      <template #default="{ row }">
        <span v-if="row[header] !== undefined">
          {{ formatField(header, row[header]) }}
        </span>
        <span v-else class="empty-cell">-</span>
      </template>
    </el-table-column>
  </el-table>
</template>

<script setup>
// 可选:字段格式化逻辑(如日期、金额)
const formatField = (key, value) => {
  switch (key) {
    case 'price':
      return `$${value.toFixed(2)}`;
    case 'createdAt':
      return new Date(value).toLocaleDateString();
    default:
      return value;
  }
};
</script>

3. 优化与扩展

表头友好名称映射为动态字段添加中文映射:

<script setup>
// 表头映射配置
const headerMap = {
  id: "ID",
  name: "姓名",
  age: "年龄"
  ……
};

// 修改表头生成逻辑
const tableHeaders = computed(() => {
  /* 同上 */
  return Array.from(headers).map(key => ({
    key,
    label: headerMap[key] || key // 使用映射或默认字段名
  }));
});
</script>

<!-- 调整模板中的 label -->
<el-table-column 
  v-for="header in tableHeaders" 
  :key="header.key" 
  :prop="header.key" 
  :label="header.label"
>

处理嵌套对象

如果接口返回嵌套对象(如 { info: { name: 'Alice' } }),可通过递归展开:

// 递归提取所有字段(平铺嵌套对象)
const flattenObject = (obj, prefix = '') => {
  return Object.keys(obj).reduce((acc, key) => {
    const pre = prefix ? `${prefix}.` : '';
    if (typeof obj[key] === 'object' && obj[key] !== null) {
      Object.assign(acc, flattenObject(obj[key], pre + key));
    } else {
      acc[pre + key] = obj[key];
    }
    return acc;
  }, {});
};

// 在数据预处理阶段平铺数据
const flattenedData = mockData.map(item => flattenObject(item));