Vue+Ant Design开发简单表格组件的实战指南

 更新时间:2025年06月25日 10:34:58   作者:码农阿豪@新空间  
在现代前端开发中,数据表格是展示信息最常用的组件之一,本文主要为大家介绍了一个基于Vue和Ant Design的表格组件开发过程,感兴趣的可以了解下

前言

在现代前端开发中,数据表格是展示信息最常用的组件之一。本文将详细记录一个基于Vue和Ant Design的表格组件开发过程,从最初的需求实现到遇到问题,再到最终优化方案的完整思考过程。通过这个实际案例,我们将探讨如何构建一个高效、用户友好的数据表格组件,特别是处理固定列和滚动区域的复杂交互。

一、项目背景与需求分析

我们的项目需要开发一个媒体广告抓取记录查看功能,主要需求包括:

  • 展示媒体广告位的抓取记录数据
  • 支持分页和排序功能
  • 关键信息需要固定显示(左右两侧)
  • 中间区域可横向滚动查看更多数据字段
  • 良好的性能表现,支持大数据量

基于这些需求,我们选择了Ant Design Vue作为UI组件库,其强大的Table组件非常适合这类需求。

二、基础实现

2.1 组件结构设计

首先我们创建了GraspingRecordModal.vue组件,基础结构如下:

<template>
  <a-modal
    title="抓取记录"
    :visible="visible"
    width="90%"
    :footer="null"
    @cancel="handleCancel"
  >
    <a-table
      rowKey="id"
      :columns="columns"
      :dataSource="data"
      :pagination="pagination"
      :loading="loading"
      @change="handleTableChange"
    >
      <!-- 自定义渲染插槽 -->
    </a-table>
  </a-modal>
</template>

2.2 数据获取与处理

数据获取使用异步请求,处理函数如下:

async fetchData() {
  this.loading = true;
  try {
    const { data: res } = await getGraspingRecords({
      mediaAdId: this.mediaAdId,
      page: this.pagination.current,
      pageSize: this.pagination.pageSize
    });
    
    if (res.code === '000000') {
      this.data = res.data.aaData || [];
      this.pagination.total = res.data.iTotalRecords || 0;
    } else {
      throw new Error(res.msg || '获取数据失败');
    }
  } catch (error) {
    console.error('获取抓取记录失败:', error);
    this.$message.error(error.message);
  } finally {
    this.loading = false;
  }
}

2.3 初始列配置

最初的列配置尝试固定左右两侧的关键信息:

columns: [
  {
    title: '任务ID',
    dataIndex: 'graspingTaskId',
    width: 180,
    fixed: 'left'
  },
  // ...其他中间列...
  {
    title: '状态',
    dataIndex: 'graspingStatus',
    scopedSlots: { customRender: 'graspingStatus' },
    width: 100,
    fixed: 'right'
  }
]

三、遇到的问题与初步解决方案

3.1 空白表格区域问题

在初步实现中,我们发现表格左右两侧出现了不必要的空白区域,这严重影响了用户体验和视觉效果。

问题表现:

  • 固定列与非固定列之间存在间隙
  • 滚动区域两侧出现空白
  • 表格整体布局不紧凑

3.2 原因分析

经过调试,我们发现了几个关键问题:

  • 宽度计算不准确:固定列和非固定列的宽度总和与表格容器宽度不匹配
  • 滚动设置不当:scroll.x值设置不合理
  • CSS样式冲突:Ant Design默认样式与我们的需求有冲突

3.3 初步修复尝试

我们首先尝试调整列宽和滚动设置:

:scroll="{ x: 1800 }"  // 根据列宽总和设置固定值

同时调整了一些列的宽度:

{
  title: '任务ID',
  dataIndex: 'graspingTaskId',
  width: 150,  // 缩小宽度
  fixed: 'left',
  ellipsis: true  // 添加省略号
}

四、深度优化方案

4.1 完美的解决方案

经过多次尝试,我们找到了最合适的配置方案:

<a-table
  :scroll="{ x: 'max-content' }"
  :columns="columns"
  bordered
  size="middle"
>

配合以下CSS修正:

.grasping-record-modal >>> .ant-table {
  min-width: 100%;
}
.grasping-record-modal >>> .ant-table-container {
  overflow-x: auto !important;
}
.grasping-record-modal >>> .ant-table-body {
  overflow-x: auto !important;
}

4.2 优化后的列配置

columns: [
  {
    title: '任务ID',
    dataIndex: 'graspingTaskId',
    width: 180,
    fixed: 'left',
    ellipsis: true
  },
  {
    title: '总日志数',
    dataIndex: 'totalCount',
    width: 100,
    fixed: 'left',
    align: 'center'
  },
  // 中间可滚动列...
  {
    title: '状态',
    dataIndex: 'graspingStatus',
    scopedSlots: { customRender: 'graspingStatus' },
    width: 100,
    fixed: 'right',
    align: 'center'
  },
  {
    title: '抓取时间',
    dataIndex: 'graspingTime',
    scopedSlots: { customRender: 'time' },
    width: 180,
    fixed: 'right'
  }
]

4.3 关键优化点

  • 使用’max-content’:让表格根据内容自动计算宽度
  • 强制溢出设置:确保滚动行为符合预期
  • 最小宽度保证:防止容器意外收缩
  • 列对齐统一:所有数值列居中对齐
  • 固定列优化:左右两侧固定列宽度适当加大

五、完整优化代码

以下是经过全面优化后的完整组件代码:

<template>
  <a-modal
    title="抓取记录"
    :visible="visible"
    width="90%"
    :footer="null"
    @cancel="handleCancel"
    :destroyOnClose="true"
    class="grasping-record-modal"
  >
    <a-table
      rowKey="id"
      :columns="columns"
      :dataSource="data"
      :pagination="pagination"
      :loading="loading"
      :scroll="{ x: 'max-content' }"
      @change="handleTableChange"
      bordered
      size="middle"
    >
      <template slot="graspingStatus" slot-scope="text">
        <a-tag :color="getStatusColor(text)">
          {{ getStatusText(text) }}
        </a-tag>
      </template>
      <template slot="time" slot-scope="text">
        {{ formatDateTime(text) }}
      </template>
    </a-table>
  </a-modal>
</template>

<script>
import dayjs from 'dayjs'
import { getGraspingRecords } from '@/api/ad-api/media'

export default {
  name: 'GraspingRecordModal',
  data() {
    return {
      loading: false,
      data: [],
      pagination: {
        current: 1,
        pageSize: 10,
        total: 0,
        showSizeChanger: true,
        pageSizeOptions: ['10', '20', '50', '100'],
        showTotal: total => `共 ${total} 条记录`
      },
      columns: [
        {
          title: '任务ID',
          dataIndex: 'graspingTaskId',
          width: 180,
          fixed: 'left',
          ellipsis: true
        },
        {
          title: '总日志数',
          dataIndex: 'totalCount',
          width: 100,
          fixed: 'left',
          align: 'center'
        },
        {
          title: '设备ID',
          dataIndex: 'deviceIdCount',
          width: 100,
          align: 'center'
        },
        {
          title: '启动时间',
          dataIndex: 'bootTimeSecCount',
          width: 100,
          align: 'center'
        },
        {
          title: '系统更新时间',
          dataIndex: 'osUpdateTimeSecCount',
          width: 120,
          align: 'center'
        },
        {
          title: '初始化时间',
          dataIndex: 'birthTimeCount',
          width: 100,
          align: 'center'
        },
        {
          title: 'caids',
          dataIndex: 'caidsCount',
          width: 100,
          align: 'center'
        },
        {
          title: '系统编译时间',
          dataIndex: 'sysComplingTimeCount',
          width: 120,
          align: 'center'
        },
        {
          title: 'IDFA',
          dataIndex: 'idfaCount',
          width: 100,
          align: 'center'
        },
        {
          title: 'IMSI',
          dataIndex: 'imsiCount',
          width: 100,
          align: 'center'
        },
        {
          title: '安装包列表',
          dataIndex: 'appListCount',
          width: 120,
          align: 'center'
        },
        {
          title: '状态',
          dataIndex: 'graspingStatus',
          scopedSlots: { customRender: 'graspingStatus' },
          width: 100,
          fixed: 'right',
          align: 'center'
        },
        {
          title: '抓取时间',
          dataIndex: 'graspingTime',
          scopedSlots: { customRender: 'time' },
          width: 180,
          fixed: 'right'
        }
      ]
    }
  },
  props: {
    visible: {
      type: Boolean,
      default: false
    },
    mediaAdId: {
      type: [Number, String],
      required: true
    }
  },
  methods: {
    formatDateTime(timeStr) {
      return timeStr ? dayjs(timeStr).format('YYYY-MM-DD HH:mm:ss') : '-'
    },
    getStatusText(status) {
      const map = { 0: '失败', 1: '成功', 2: '部分成功' }
      return map[status] || '未知'
    },
    getStatusColor(status) {
      const map = { 0: 'red', 1: 'green', 2: 'orange' }
      return map[status] || 'default'
    },
    handleTableChange(pagination) {
      this.pagination.current = pagination.current
      this.pagination.pageSize = pagination.pageSize
      this.fetchData()
    },
    async fetchData() {
      this.loading = true
      try {
        const { data: res } = await getGraspingRecords({
          mediaAdId: this.mediaAdId,
          page: this.pagination.current,
          pageSize: this.pagination.pageSize
        })
        if (res.code === '000000') {
          this.data = res.data.aaData || []
          this.pagination.total = res.data.iTotalRecords || 0
        } else {
          throw new Error(res.msg || '获取数据失败')
        }
      } catch (error) {
        console.error('获取抓取记录失败:', error)
        this.$message.error(error.message)
      } finally {
        this.loading = false
      }
    },
    handleCancel() {
      this.$emit('close')
    }
  }
}
</script>

<style scoped>
.grasping-record-modal >>> .ant-table {
  min-width: 100%;
}
.grasping-record-modal >>> .ant-table-container {
  overflow-x: auto !important;
}
.grasping-record-modal >>> .ant-table-body {
  overflow-x: auto !important;
}
</style>

六、总结与最佳实践

通过这个案例,我们总结出以下Ant Design Table组件的最佳实践:

1.固定列设计:

  • 关键信息固定在左右两侧
  • 固定列宽度适当加大
  • 添加ellipsis防止长文本溢出

2.滚动区域优化:

  • 使用scroll="{ x: 'max-content' }"
  • 配合CSS强制溢出设置
  • 确保表格宽度自适应

3.性能考虑:

  • 合理设置分页大小
  • 使用loading状态提升用户体验
  • 大数据量时考虑虚拟滚动

4.视觉一致性:

  • 数值列居中对齐
  • 状态使用标签颜色区分
  • 时间统一格式化

5.健壮性保障:

  • 数据获取错误处理
  • 空状态处理
  • 分页参数校验

这个案例展示了如何通过迭代优化解决实际问题,最终实现了一个既美观又实用的数据表格组件。希望这些经验能帮助你在未来的项目中更好地使用Ant Design Table组件。

到此这篇关于Vue+Ant Design开发简单表格组件的实战指南的文章就介绍到这了,更多相关Vue Ant Design组件开发内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 一篇带你搞懂Vue项目里的权限控制

    一篇带你搞懂Vue项目里的权限控制

    这篇文章主要为大家介绍了vue项目里的权限控制,文中有详细的代码示例供大家参考,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2023-06-06
  • Vue3组件挂载之创建组件实例详解

    Vue3组件挂载之创建组件实例详解

    这篇文章主要为大家介绍了Vue3组件挂载之创建组件实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • web面试MVC与MVVM区别及Vue为什么不完全遵守MVVM解答

    web面试MVC与MVVM区别及Vue为什么不完全遵守MVVM解答

    这篇文章主要介绍了web面试中常问问题,MVC与MVVM区别以及Vue为什么不完全遵守MVVM的难点解答,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-09-09
  • 100行代码实现一个vue分页组功能

    100行代码实现一个vue分页组功能

    今天用vue来实现一个分页组件,总体来说,vue实现比较简单,样式部分模仿了elementUI。接下来本文通过实例代码给大家介绍100行代码实现一个vue分页组功能,感兴趣的朋友跟随小编一起看看吧
    2018-11-11
  • 一文详解vue3项目实战中的接口调用

    一文详解vue3项目实战中的接口调用

    在企业开发过程中,往往有着明确的前后端的分工,前端负责接收、使用接口,后端负责编写、处理接口,下面这篇文章主要给大家介绍了关于vue3项目实战中的接口调用的相关资料,需要的朋友可以参考下
    2022-12-12
  • Axios学习笔记之使用方法教程

    Axios学习笔记之使用方法教程

    axios是用来做数据交互的插件,最近正在学习axios,所以想着整理成笔记方便大家和自己参考学习,下面这篇文章主要跟大家介绍了关于Axios使用方法的相关资料,需要的朋友们下面来一起看看吧。
    2017-07-07
  • 详解在Vue中如何使用axios跨域访问数据

    详解在Vue中如何使用axios跨域访问数据

    本篇文章主要介绍了在Vue中如何使用axios跨域访问数据,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • 解决Vue2 axios发请求报400错误"Error: Request failed with status code 400"

    解决Vue2 axios发请求报400错误"Error: Request failed with s

    这篇文章主要给大家介绍了关于如何解决Vue2 axios发请求报400错误"Error: Request failed with status code 400"的相关资料,在Vue应用程序中我们通常会使用axios作为网络请求库,需要的朋友可以参考下
    2023-07-07
  • 详解Vue.js动态绑定class

    详解Vue.js动态绑定class

    Vue.js的核心是一个响应的数据绑定系统,它允许我们在普通 HTML 模板中使用特殊的语法将 DOM “绑定”到底层数据。被绑定的DOM 将与数据保持同步,每当数据有改动,相应的DOM视图也会更新。基于这种特性,通过vue.js动态绑定class就变得非常简单。一起来看下吧
    2016-12-12
  • Vue组件传值方式(props属性,父到子,子到父,兄弟传值)

    Vue组件传值方式(props属性,父到子,子到父,兄弟传值)

    这篇文章主要介绍了Vue组件传值方式(props属性,父到子,子到父,兄弟传值),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06

最新评论