在这里插入图片描述

后台管理系统里,表格绝对是出镜率最高的组件。用户列表、订单列表、日志列表,几乎所有的数据展示都离不开表格。这篇文章来聊聊在 Electron for OpenHarmony 项目中如何实现一个功能完善的 Table 表格组件。

表格的核心概念

Element Plus 的 el-table 组件采用声明式的写法,通过 el-table-column 来定义每一列的配置。这种写法比传统的配置对象更直观,列的顺序、宽度、格式化方式一目了然。

表格的数据通过 :data 属性传入,是一个对象数组,每个对象代表一行数据。列通过 prop 属性指定要显示对象的哪个字段。

准备表格数据

先准备一些测试数据:

<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
const tableData = [
  { date: '2024-01-01', name: '张三', address: '北京市朝阳区' },
  { date: '2024-01-02', name: '李四', address: '上海市浦东新区' },
  { date: '2024-01-03', name: '王五', address: '广州市天河区' },
  { date: '2024-01-04', name: '赵六', address: '深圳市南山区' },
]
</script>

这是一个简单的用户地址列表,包含日期、姓名、地址三个字段。实际项目中数据通常是从接口获取的,这里为了演示方便直接写死。

基础表格

最简单的表格只需要绑定数据和定义列:

<el-card class="demo-card">
  <template #header>基础表格</template>
  <el-table :data="tableData" style="width: 100%">
    <el-table-column prop="date" label="日期" width="120" />
    <el-table-column prop="name" label="姓名" width="100" />
    <el-table-column prop="address" label="地址" />
  </el-table>
</el-card>

:data 绑定数据源,el-table-column 定义列。prop 指定数据字段名,label 是表头显示的文字,width 设置列宽。

前两列设置了固定宽度,最后一列没设置宽度,会自动占满剩余空间。这种写法在列数较少时很实用,不用精确计算每列的宽度。

style="width: 100%" 让表格撑满父容器,默认情况下表格宽度是由内容决定的,可能会出现横向滚动条。

带边框的表格

默认的表格只有横向分割线,如果想要完整的网格线,加上 border 属性:

<el-card class="demo-card">
  <template #header>带边框</template>
  <el-table :data="tableData" border style="width: 100%">
    <el-table-column prop="date" label="日期" width="120" />
    <el-table-column prop="name" label="姓名" width="100" />
    <el-table-column prop="address" label="地址" />
  </el-table>
</el-card>

border 属性会给表格加上完整的边框,包括外边框和单元格边框。这种样式在数据密集的场景下更容易阅读,每个单元格的边界很清晰。

斑马纹表格

数据行数多的时候,纯白背景容易看串行。斑马纹让奇偶行背景色不同,提高可读性:

<el-card class="demo-card">
  <template #header>斑马纹</template>
  <el-table :data="tableData" stripe style="width: 100%">
    <el-table-column prop="date" label="日期" width="120" />
    <el-table-column prop="name" label="姓名" width="100" />
    <el-table-column prop="address" label="地址" />
  </el-table>
</el-card>

stripe 属性开启斑马纹效果,偶数行会有浅灰色背景。这个效果和 border 可以同时使用,根据设计需求组合。

固定表头

数据很多需要滚动时,表头最好固定住,不然滚到下面就不知道每列是什么了:

<el-table :data="tableData" height="250" style="width: 100%">
  <el-table-column prop="date" label="日期" width="120" />
  <el-table-column prop="name" label="姓名" width="100" />
  <el-table-column prop="address" label="地址" />
</el-table>

只要给表格设置 height 属性,表头就会自动固定。高度可以是数字(像素)或者字符串(比如 calc(100vh - 200px))。超出高度的内容会在表格内部滚动,表头始终可见。

固定列

横向内容太多时,可能需要固定某些列,比如操作列:

<el-table :data="tableData" style="width: 100%">
  <el-table-column fixed prop="date" label="日期" width="120" />
  <el-table-column prop="name" label="姓名" width="100" />
  <el-table-column prop="address" label="地址" width="300" />
  <el-table-column prop="remark" label="备注" width="300" />
  <el-table-column fixed="right" label="操作" width="120">
    <template #default>
      <el-button type="primary" size="small">编辑</el-button>
    </template>
  </el-table-column>
</el-table>

fixed 属性可以设置为 true(或 left)固定在左侧,right 固定在右侧。固定列在横向滚动时会保持不动,方便用户对照数据进行操作。

自定义列内容

prop 只能显示简单的文本,复杂的展示需要用插槽:

<script setup lang="ts">
import { ref } from 'vue'

const tableData = ref([
  { date: '2024-01-01', name: '张三', status: 1, address: '北京市朝阳区' },
  { date: '2024-01-02', name: '李四', status: 0, address: '上海市浦东新区' },
  { date: '2024-01-03', name: '王五', status: 1, address: '广州市天河区' },
])

const statusMap: Record<number, string> = {
  0: '禁用',
  1: '正常'
}

const handleEdit = (row: any) => {
  console.log('编辑', row)
}

const handleDelete = (row: any) => {
  console.log('删除', row)
}
</script>

<template>
  <el-table :data="tableData" style="width: 100%">
    <el-table-column prop="date" label="日期" width="120" />
    <el-table-column prop="name" label="姓名" width="100" />
    <el-table-column label="状态" width="100">
      <template #default="{ row }">
        <el-tag :type="row.status === 1 ? 'success' : 'danger'">
          {{ statusMap[row.status] }}
        </el-tag>
      </template>
    </el-table-column>
    <el-table-column prop="address" label="地址" />
    <el-table-column label="操作" width="150">
      <template #default="{ row }">
        <el-button type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
        <el-button type="danger" size="small" @click="handleDelete(row)">删除</el-button>
      </template>
    </el-table-column>
  </el-table>
</template>

#default 插槽可以自定义单元格内容,通过解构获取 row(当前行数据)、column(当前列配置)、$index(行索引)。

状态列用 el-tag 组件展示,根据状态值动态设置颜色。操作列放了编辑和删除两个按钮,点击时把当前行数据传给处理函数。

排序功能

点击表头可以对数据进行排序:

<el-table :data="tableData" style="width: 100%">
  <el-table-column prop="date" label="日期" width="120" sortable />
  <el-table-column prop="name" label="姓名" width="100" sortable />
  <el-table-column prop="address" label="地址" />
</el-table>

给列加上 sortable 属性就能开启排序,表头会显示排序图标。点击可以在升序、降序、默认三种状态间切换。

默认是前端排序,如果数据量大需要后端排序,可以用 sortable="custom" 配合 @sort-change 事件:

<script setup lang="ts">
const handleSortChange = ({ prop, order }: { prop: string; order: string }) => {
  console.log('排序字段:', prop, '排序方式:', order)
  // 调用接口重新获取数据
}
</script>

<template>
  <el-table :data="tableData" @sort-change="handleSortChange" style="width: 100%">
    <el-table-column prop="date" label="日期" width="120" sortable="custom" />
    <el-table-column prop="name" label="姓名" width="100" />
    <el-table-column prop="address" label="地址" />
  </el-table>
</template>

order 的值是 ascending(升序)、descending(降序)或 null(默认)。

多选功能

批量操作场景需要多选功能:

<script setup lang="ts">
import { ref } from 'vue'

const tableData = ref([
  { date: '2024-01-01', name: '张三', address: '北京市朝阳区' },
  { date: '2024-01-02', name: '李四', address: '上海市浦东新区' },
  { date: '2024-01-03', name: '王五', address: '广州市天河区' },
])

const selectedRows = ref([])

const handleSelectionChange = (selection: any[]) => {
  selectedRows.value = selection
  console.log('选中的行:', selection)
}

const handleBatchDelete = () => {
  console.log('批量删除:', selectedRows.value)
}
</script>

<template>
  <div>
    <el-button type="danger" :disabled="selectedRows.length === 0" @click="handleBatchDelete">
      批量删除 ({{ selectedRows.length }})
    </el-button>
    <el-table :data="tableData" @selection-change="handleSelectionChange" style="width: 100%; margin-top: 16px;">
      <el-table-column type="selection" width="55" />
      <el-table-column prop="date" label="日期" width="120" />
      <el-table-column prop="name" label="姓名" width="100" />
      <el-table-column prop="address" label="地址" />
    </el-table>
  </div>
</template>

type="selection" 的列会显示复选框,表头的复选框可以全选/取消全选。@selection-change 事件在选中项变化时触发,参数是所有选中行的数组。

批量删除按钮根据选中数量动态显示,没有选中时禁用。

展开行

有些数据需要展开查看详情:

<el-table :data="tableData" style="width: 100%">
  <el-table-column type="expand">
    <template #default="{ row }">
      <div style="padding: 20px;">
        <p>日期:{{ row.date }}</p>
        <p>姓名:{{ row.name }}</p>
        <p>地址:{{ row.address }}</p>
        <p>这里可以放更多详细信息...</p>
      </div>
    </template>
  </el-table-column>
  <el-table-column prop="date" label="日期" width="120" />
  <el-table-column prop="name" label="姓名" width="100" />
  <el-table-column prop="address" label="地址" />
</el-table>

type="expand" 的列会显示展开按钮,点击展开后显示插槽内容。展开行适合展示不常用但偶尔需要查看的详细信息,避免表格列数过多。

与鸿蒙原生能力结合

在 Electron for OpenHarmony 项目中,表格数据可能来自原生层,操作也可能需要调用原生能力:

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useOhos } from '@/composables/useOhos'
import { ElMessage, ElMessageBox } from 'element-plus'

const { openFile, showNotification } = useOhos()
const tableData = ref([])

onMounted(async () => {
  // 模拟从原生层获取数据
  tableData.value = [
    { id: 1, name: '文档.docx', size: '2.5MB', path: '/storage/documents/文档.docx' },
    { id: 2, name: '图片.png', size: '1.2MB', path: '/storage/pictures/图片.png' },
  ]
})

const handleOpen = async (row: any) => {
  // 调用原生能力打开文件
  await showNotification('提示', `正在打开文件: ${row.name}`)
}

const handleExport = async () => {
  const files = await openFile({
    title: '选择导出位置',
    filters: [{ name: 'Excel', extensions: ['xlsx'] }]
  })
  if (files && files.length > 0) {
    ElMessage.success('导出成功')
  }
}
</script>

<template>
  <div>
    <el-button type="primary" @click="handleExport">导出表格</el-button>
    <el-table :data="tableData" style="width: 100%; margin-top: 16px;">
      <el-table-column prop="name" label="文件名" />
      <el-table-column prop="size" label="大小" width="100" />
      <el-table-column label="操作" width="100">
        <template #default="{ row }">
          <el-button type="primary" size="small" @click="handleOpen(row)">打开</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

这个例子展示了文件列表场景,点击打开按钮会调用原生能力打开文件,导出按钮会调用文件选择器让用户选择保存位置。useOhos 封装了与鸿蒙原生层的通信逻辑。

空数据状态

数据为空时需要友好的提示:

<el-table :data="tableData" style="width: 100%">
  <template #empty>
    <div style="padding: 40px 0; text-align: center;">
      <el-icon :size="48" style="color: #909399;"><Document /></el-icon>
      <p style="color: #909399; margin-top: 16px;">暂无数据</p>
    </div>
  </template>
  <el-table-column prop="date" label="日期" width="120" />
  <el-table-column prop="name" label="姓名" width="100" />
  <el-table-column prop="address" label="地址" />
</el-table>

#empty 插槽自定义空状态的显示内容,比默认的"暂无数据"文字更美观。

加载状态

请求数据时显示 loading:

<script setup lang="ts">
import { ref } from 'vue'

const loading = ref(false)
const tableData = ref([])

const fetchData = async () => {
  loading.value = true
  // 模拟接口请求
  setTimeout(() => {
    tableData.value = [
      { date: '2024-01-01', name: '张三', address: '北京市朝阳区' },
    ]
    loading.value = false
  }, 1000)
}
</script>

<template>
  <el-table v-loading="loading" :data="tableData" style="width: 100%">
    <el-table-column prop="date" label="日期" width="120" />
    <el-table-column prop="name" label="姓名" width="100" />
    <el-table-column prop="address" label="地址" />
  </el-table>
</template>

v-loading 指令在表格上显示加载遮罩,请求完成后自动消失。这是 Element Plus 提供的全局指令,任何元素都可以用。

小结

Table 表格是数据展示的核心组件,这篇文章介绍了它的各种用法:基础表格、边框和斑马纹、固定表头和列、自定义列内容、排序、多选、展开行,以及与鸿蒙原生能力的结合。表格组件的配置项很多,但核心思路就是通过 el-table-column 声明式地定义每一列的行为。掌握了这些基础用法,再复杂的表格需求也能轻松应对。


欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐