Files
blog-press/docs/Web/Vue/ElTable.md
2026-05-27 11:26:16 +08:00

6.2 KiB
Raw Permalink Blame History

title, date
title date
Element Plus 表格组件 2026-05-27

一、加载中

<el-table 
    v-loading="dispatchStore.isLoading" 
    element-loading-text="加载中..."
    :data="dispatchStore.contractList" 
    border 
    stripeempty-text="暂无记录"
    class="common-table">
    
</el-table>

::: tip 要配合pinia存储使用获取数据前将loading置为true完成后置为false。
:::

二、展开行

<template>
  <el-table :data="dispatchStore.contractList"
            :row-key="getRowKey"
            :expand-row-keys="expandedRows"
            @expand-change="handleExpandChange">
      <el-table-column type="expand">
      <template #default="props">
          <el-table :data="props.row.dispatchList" 
              border stripeempty-text="暂无记录"
              class="dispatch-content"
              style="width: 100%">
          <el-table-column fixed type="index" align="center" width="60" />
          <el-table-column prop="customer" label="终端客户" align="center" width="180"/>
          </el-table>
      </template>
      </el-table-column>
  </el-table>
</template>

<script lang="ts" setup>
const expandedRows = ref<number[]>([])

// 获取行的唯一标识
const getRowKey = (row: IDispatch) => {
  return row.id
}

// 处理展开行变化
const handleExpandChange = (row: IDispatch, expandedRowsList: IDispatch[]) => {
  if (expandedRowsList.length > 0) {
    // 只保留最新展开的行
    expandedRows.value = [getRowKey(row)]
  } else {
    expandedRows.value = []
  }
}
</script>

通过类型expand和插槽props实现可以展示任意格式数据。
  增加表格的row-keyexpand-row-key属性可以实现只展开某一行功能。

三、排序和筛选

<template>
  <el-table :data="dispatchStore.contractList">
    <el-table-column prop="contractNum" label="合同编号" align="center" min-width="10%"
                     :filters=contractNumFilters :filter-method="filterColumnMethod" sortable>
    </el-table-column>
  </el-table>
</template>

<script lang="ts" setup>
import { useDispatchStore } from '@/stores/dispatch.ts'
import { computed } from 'vue'
import type { IContract } from '@/types/dispatch.ts'

const dispatchStore = useDispatchStore()

type ContractField = keyof IContract

const filterColumnMethod = (value, row, column) => {
  return row[column.property] === value
}

const getFiltersByKey = (key: ContractField) => {
  const result = [...new Set(dispatchStore.contractList.map((item) => item[key]).filter((value) => value != null && value !==''))]

  return result.map((item) => ({
    text: item,
    value: item
  }))
}

const contractNumFilters = computed(() => getFiltersByKey('contractNum'))
</script>

  在el-table-column中添加sortable即可实现排序功能。
  在el-table-column中添加filtersfilter-method实现筛选功能。
filters为原始数据中该字段下的所有值,并转为指定格式,也可以自定义筛选字段。

四、表头搜索

<template>
  <el-table :data="dispatchStore.contractList" >
    <el-table-column prop="contractNum" label="合同编号" align="center" min-width="10%">
      <template #header>
        <div class="custom-header">
          <span :class="{ 'active': contractNumSearch }">合同编号</span>
          <el-popover ref="contractNumPopoverRef" placement="bottom" width="230" trigger="click">
            <template #default>
              <el-select v-model="contractNumSearch" filterable clearable
                         placeholder="请选择或输入合同编号" @change="applySearch('contractNum')">
                <el-option v-for="(item, index) in contractNumValue"
                           :key="index" :label="item" :value="item"/>
              </el-select>
              <div style="margin-top: 8px;display: flex;justify-content: flex-end;">
                <el-button type="primary" size="small" @click="applySearch('contractNum')">搜索</el-button>
                <el-button size="small" @click="resetSearch('contractNum')">重置</el-button>
              </div>
            </template>
            <template #reference>
              <el-icon class="search-icon"><Search/></el-icon>
            </template>
          </el-popover>
        </div>
      </template>
    </el-table-column>
  </el-table>
</template>

<script lang="ts" setup>
import { useDispatchStore } from '@/stores/dispatch.ts'
import { computed, ref } from 'vue'
import { Search } from '@element-plus/icons-vue'
import type { IContract } from '@/types/dispatch.ts'
import type { PopoverInstance } from 'element-plus'
import type { Ref } from 'vue'

const dispatchStore = useDispatchStore()

type ContractField = keyof IContract

const getFiltersByKey = (key: ContractField) => {
  const result = [...new Set(dispatchStore.contractList.map((item) => item[key]).filter((value) => value != null && value !== ''))]

  return result.map((item) => ({
    text: item,
    value: item
  }))
}

const contractNumFilters = computed(() => getFiltersByKey('contractNum'))

const contractNumValue = computed(() => contractNumFilters.value.map((item) => item.value))

const contractNumPopoverRef = ref<PopoverInstance>()
const contractNumSearch = ref('')

const popoverMap = new Map<string, Ref<PopoverInstance | undefined>>([
  ['contractNum', contractNumPopoverRef]
])
const searchMap = new Map<string, Ref<string>>([
  ['contractNum', contractNumSearch]
])

const applySearch = async (field: ContractField) => {
  const searchRef = searchMap.get(field)

  if (!searchRef) return

  if (!searchRef.value) {
    await dispatchStore.queryContractList()
    return
  }
  if (!searchRef.value.trim()) return

  dispatchStore.contractList = dispatchStore.contractList.filter((item) => {
    const value = item[field]
    return typeof value === 'string' && value.includes(searchRef.value)
  })
  popoverMap.get(field)?.value?.hide()
}

const resetSearch = async (field: string) => {
  const searchRef = searchMap.get(field)

  if (searchRef) {
    searchRef.value = ''
  }
  popoverMap.get(field)?.value?.hide()
  await dispatchStore.queryContractList()
}
</script>

  表头搜索主要利用header插槽和el-popover组件实现。