当前位置: 首页 > article >正文

可复用表格组件设计与实现:分页、排序、筛选全功能解析

文章目录

    • 一、组件设计思路
      • 1.1 功能需求分析
      • 1.2 技术选型
    • 二、组件架构设计
      • 2.1 组件结构
      • 2.2 数据流设计
    • 三、核心代码实现
      • 3.1 基础表格组件
      • 3.2 状态管理
    • 四、功能模块实现
      • 4.1 分页组件
      • 4.2 排序控制
      • 4.3 筛选控制
    • 五、性能优化方案
      • 5.1 虚拟滚动
      • 5.2 防抖筛选
    • 六、完整测试方案
      • 6.1 测试用例设计
      • 6.2 自动化测试示例

一、组件设计思路

1.1 功能需求分析

20% 25% 25% 20% 10% 表格功能需求 基础展示 分页功能 排序功能 筛选功能 性能优化

1.2 技术选型

功能模块技术方案说明
基础表格HTML5 Table语义化标签
状态管理Vue Composition API响应式数据管理
分页控制自定义分页组件灵活可控
排序算法数组排序支持多列排序
筛选功能组合式筛选器支持多条件筛选

二、组件架构设计

2.1 组件结构

TableComponent
TableHeader
TableBody
Pagination
SortControl
FilterControl
TableRow
TableCell

2.2 数据流设计

用户 表格组件 状态管理 服务端 点击排序 更新排序状态 触发重新渲染 请求排序数据 返回新数据 更新视图 修改筛选条件 更新筛选状态 触发重新渲染 请求筛选数据 返回新数据 更新视图 用户 表格组件 状态管理 服务端

三、核心代码实现

3.1 基础表格组件

<template>
  <div class="table-container">
    <table>
      <thead>
        <tr>
          <th v-for="col in columns" :key="col.key">
            {{ col.label }}
            <SortControl 
              :column="col"
              @sort="handleSort"
            />
          </th>
        </tr>
        <tr>
          <th v-for="col in columns" :key="col.key">
            <FilterControl 
              v-if="col.filterable"
              :column="col"
              @filter="handleFilter"
            />
          </th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="row in paginatedData" :key="row.id">
          <td v-for="col in columns" :key="col.key">
            {{ row[col.key] }}
          </td>
        </tr>
      </tbody>
    </table>
    <Pagination 
      :total="filteredData.length"
      :current-page="currentPage"
      :page-size="pageSize"
      @page-change="handlePageChange"
    />
  </div>
</template>

3.2 状态管理

import { ref, computed } from 'vue'

export function useTable(data, columns) {
  const currentPage = ref(1)
  const pageSize = ref(10)
  const sortState = ref({})
  const filterState = ref({})

  const filteredData = computed(() => {
    return data.value.filter(row => {
      return Object.entries(filterState.value).every(([key, filter]) => {
        if (!filter) return true
        return filter(row[key])
      })
    })
  })

  const sortedData = computed(() => {
    const { key, order } = sortState.value
    if (!key) return filteredData.value
    
    return [...filteredData.value].sort((a, b) => {
      if (order === 'asc') {
        return a[key] > b[key] ? 1 : -1
      } else {
        return a[key] < b[key] ? 1 : -1
      }
    })
  })

  const paginatedData = computed(() => {
    const start = (currentPage.value - 1) * pageSize.value
    const end = start + pageSize.value
    return sortedData.value.slice(start, end)
  })

  return {
    currentPage,
    pageSize,
    sortState,
    filterState,
    filteredData,
    sortedData,
    paginatedData
  }
}

四、功能模块实现

4.1 分页组件

<template>
  <div class="pagination">
    <button 
      v-for="page in pages"
      :key="page"
      :class="{ active: page === currentPage }"
      @click="handlePageChange(page)"
    >
      {{ page }}
    </button>
  </div>
</template>

<script setup>
const props = defineProps({
  total: Number,
  currentPage: Number,
  pageSize: Number
})

const emit = defineEmits(['page-change'])

const totalPages = computed(() => Math.ceil(props.total / props.pageSize))

const pages = computed(() => {
  const range = []
  for (let i = 1; i <= totalPages.value; i++) {
    range.push(i)
  }
  return range
})

function handlePageChange(page) {
  emit('page-change', page)
}
</script>

4.2 排序控制

<template>
  <span class="sort-control" @click="toggleSort">
    <span v-if="sortState.key === column.key">
      {{ sortState.order === 'asc' ? '↑' : '↓' }}
    </span>
  </span>
</template>

<script setup>
import { inject } from 'vue'

const props = defineProps({
  column: Object
})

const sortState = inject('sortState')

function toggleSort() {
  if (sortState.value.key !== props.column.key) {
    sortState.value = { key: props.column.key, order: 'asc' }
  } else {
    sortState.value.order = sortState.value.order === 'asc' ? 'desc' : 'asc'
  }
}
</script>

4.3 筛选控制

<template>
  <input 
    v-if="column.filterType === 'text'"
    type="text"
    :value="filterValue"
    @input="handleInput"
  />
  <select 
    v-else-if="column.filterType === 'select'"
    :value="filterValue"
    @change="handleChange"
  >
    <option value="">All</option>
    <option v-for="option in column.filterOptions" :key="option">
      {{ option }}
    </option>
  </select>
</template>

<script setup>
import { inject } from 'vue'

const props = defineProps({
  column: Object
})

const filterState = inject('filterState')

const filterValue = computed({
  get: () => filterState.value[props.column.key] || '',
  set: value => {
    filterState.value[props.column.key] = value
  }
})

function handleInput(e) {
  filterValue.value = e.target.value
}

function handleChange(e) {
  filterValue.value = e.target.value
}
</script>

五、性能优化方案

5.1 虚拟滚动

function useVirtualScroll(items, itemHeight, containerHeight) {
  const scrollTop = ref(0)
  const visibleCount = Math.ceil(containerHeight / itemHeight)
  
  const startIndex = computed(() => {
    return Math.floor(scrollTop.value / itemHeight)
  })
  
  const endIndex = computed(() => {
    return startIndex.value + visibleCount
  })
  
  const visibleItems = computed(() => {
    return items.value.slice(startIndex.value, endIndex.value)
  })
  
  const paddingTop = computed(() => {
    return startIndex.value * itemHeight
  })
  
  const paddingBottom = computed(() => {
    return (items.value.length - endIndex.value) * itemHeight
  })
  
  return {
    scrollTop,
    visibleItems,
    paddingTop,
    paddingBottom
  }
}

5.2 防抖筛选

function useDebouncedFilter(filterState) {
  const debouncedFilter = ref({})
  
  watch(filterState, () => {
    clearTimeout(filterTimeout)
    filterTimeout = setTimeout(() => {
      debouncedFilter.value = { ...filterState.value }
    }, 300)
  }, { deep: true })
  
  return debouncedFilter
}

六、完整测试方案

6.1 测试用例设计

测试场景验证目标方法
基础渲染数据展示正确性验证渲染结果
分页功能分页逻辑正确性模拟分页操作
排序功能排序算法正确性验证排序结果
筛选功能筛选条件有效性测试不同筛选条件
性能测试大数据量渲染性能加载10万条数据

6.2 自动化测试示例

describe('TableComponent', () => {
  let wrapper
  const columns = [
    { key: 'name', label: 'Name' },
    { key: 'age', label: 'Age', sortable: true }
  ]
  const data = [
    { id: 1, name: 'Alice', age: 25 },
    { id: 2, name: 'Bob', age: 30 }
  ]

  beforeEach(() => {
    wrapper = mount(TableComponent, {
      props: { columns, data }
    })
  })

  test('renders correct number of rows', () => {
    expect(wrapper.findAll('tbody tr').length).toBe(data.length)
  })

  test('sorts data correctly', async () => {
    const ageHeader = wrapper.find('th', { text: 'Age' })
    await ageHeader.trigger('click')
    const firstRow = wrapper.find('tbody tr:first-child')
    expect(firstRow.text()).toContain('Bob')
  })
})

总结:本文从设计到实现详细讲解了可复用表格组件的完整开发方案,包含分页、排序、筛选等核心功能,并提供了性能优化和测试方案。
在这里插入图片描述


http://www.kler.cn/a/586045.html

相关文章:

  • AI 游戏的创新与挑战都有哪些?
  • Dynamics 365 启用用户安全角色变更的审核功能
  • 【十五】Golang 结构体
  • Docker Compose 使用笔记
  • Leetcode 刷题笔记1 动态规划part13
  • SWPU 2022 新生赛
  • leetcode 102. 二叉树的层序遍历
  • 机器学习神经网络中的损失函数表达的是什么意思
  • 让网站变得更智能!架构标记如何提升SEO并吸引更多流量?
  • CentOS7 服务器安装 Hadoop 和 Hive
  • xlua 运行原理
  • 编程自学指南:java程序设计开发,数组与集合,为什么需要数组和集合?数组的声明与初始化, 数组遍历,多维数组
  • redis工具类
  • 【前端拓展】Canvas性能革命!WebGPU + WebAssembly混合渲染方案深度解析
  • FastAPI复杂查询终极指南:告别if-else的现代化过滤架构
  • 分享vue好用的pdf 工具实测
  • 【redis】发布订阅
  • windows11 的 .gitignore 文件失效(从来没有进行 commit 以及 add 操作,只是 git init 了)
  • 科技快讯 | “垃圾短信”可以被识别了;阿里正式推出AI旗舰应用;OpenAI深夜发布全新Agent工具
  • GC 频率和触发条件