Skip to content

Table 表格

gi-table 组件是基于 Element Plus 的 el-tableel-pagination 组件封装的高级表格组件,提供了更便捷的配置方式和增强的功能,包括动态列配置、自定义渲染、嵌套列支持、分页集成等

基础用法

暂无数据
  • 1
共 0 条
查看代码
vue
<template>
  <gi-page-layout bordered style="height: 500px">
    <template #tool>
      <el-row justify="space-between" class="g-tool g-w-full">
        <el-space warp>
          <gi-button type="add"></gi-button>
          <gi-button type="delete"></gi-button>
        </el-space>
        <el-space warp>
          <el-input v-model="queryParams.keyword" placeholder="搜索姓名或地址" clearable style="width: 200px" />
          <ElButton type="primary" @click="search">搜索</ElButton>
        </el-space>
      </el-row>
    </template>
    <gi-table v-loading="loading" :columns="columns" :data="tableData" :pagination="pagination">
      <template #action="scope">
        <el-space>
          <ElButton type="primary" size="small" @click="onEdit(scope.row)">编辑</ElButton>
          <ElButton type="danger" size="small">删除</ElButton>
        </el-space>
      </template>
    </gi-table>
  </gi-page-layout>
</template>

<script lang="ts" setup>
import type { UserItem } from '@docs/_shared/apis/mockTable'
import type { TableColumnItem } from 'gi-component'
import { getUserList } from '@docs/_shared/apis/mockTable'
import { useTable } from '@docs/_shared/hooks'
import { ElButton, ElMessage, ElTag } from 'element-plus'
import { h, reactive } from 'vue'

const columns: TableColumnItem<UserItem>[] = [
  { type: 'selection', width: 55, align: 'center', fixed: 'left' },
  { type: 'index', label: '序号', width: 60, align: 'center' },
  {
    prop: 'name',
    label: '姓名',
    width: 100,
    align: 'center',
    showOverflowTooltip: true
  },
  { prop: 'age', label: '年龄', width: 60, align: 'center' },
  {
    prop: 'sex',
    label: '性别',
    width: 80,
    align: 'center',
    render: ({ row }) => {
      return h(
        ElTag,
        { type: row.sex === '男' ? 'primary' : 'danger', size: 'small' },
        { default: () => row.sex }
      )
    }
  },
  {
    prop: 'address',
    label: '地址'
  },
  { prop: 'remark', label: '描述', width: 150, showOverflowTooltip: true },
  {
    prop: 'action',
    label: '操作',
    width: 140,
    align: 'center',
    slotName: 'action',
    fixed: 'right'
  }
]

const queryParams = reactive({
  keyword: ''
})

const { tableData, pagination, search, loading } = useTable({
  listAPI: (p) => getUserList({ ...p, ...queryParams }),
  onSuccess: () => {
    // ElMessage.success(`页码${pagination.currentPage}, 页数${pagination.pageSize}条--数据成功加载`);
  }
})

function onEdit(row: UserItem) {
  ElMessage.success(`编辑 ${row.name}`)
}
</script>

<style lang="scss" scoped></style>

表头搜索

本示例只提供技巧参考,实际项目请根据自身需求进行封装useTableHeaderSearch函数hooks

暂无数据
  • 1
共 0 条
{}
查看代码
vue
<template>
  <gi-page-layout bordered style="height: 500px">
    <template #tool>
      <el-row justify="space-between" class="g-tool g-w-full">
        <el-space warp>
          <gi-button type="add"></gi-button>
          <gi-button type="delete"></gi-button>
        </el-space>
        <el-space warp>
          <el-input v-model="queryParams.keyword" placeholder="搜索姓名或地址" clearable style="width: 200px" />
          <ElButton type="primary" @click="search">搜索</ElButton>
        </el-space>
      </el-row>
    </template>
    <gi-table v-loading="loading" :columns="columns" :data="tableData" :pagination="pagination" border>
      <template #action="scope">
        <el-space>
          <ElButton type="primary" size="small" @click="onEdit(scope)">编辑</ElButton>
          <ElButton type="danger" size="small">删除</ElButton>
        </el-space>
      </template>
    </gi-table>
    <pre class="doc-pre">{{ headerParams }}</pre>
  </gi-page-layout>
</template>

<script lang="ts" setup>
import type { UserItem } from '@docs/_shared/apis/mockTable'
import type { TableColumnItem } from 'gi-component'
import { getUserList } from '@docs/_shared/apis/mockTable'
import { useTable } from '@docs/_shared/hooks'
import { ElButton, ElMessage, ElTag } from 'element-plus'
import { h, reactive } from 'vue'
import { useTableHeaderSearch } from '../_fragments/useTableHeaderSearch'

const queryParams = reactive({
  keyword: ''
})

const { tableData, pagination, search, loading } = useTable({
  listAPI: (p) => getUserList({ ...p, ...queryParams }),
  onSuccess: () => {
    // ElMessage.success(`页码${pagination.currentPage}, 页数${pagination.pageSize}条--数据成功加载`);
  }
})

const { headerParams, createTableHeader } = useTableHeaderSearch({ search: () => search() })

const columns: TableColumnItem<UserItem>[] = [
  { type: 'selection', width: 55, align: 'center', fixed: 'left' },
  { type: 'index', label: '序号', width: 60, align: 'center' },
  {
    prop: 'name',
    label: '姓名',
    width: 100,
    align: 'center',
    showOverflowTooltip: true,
    renderHeader: () => createTableHeader({ type: 'input', label: '姓名', field: 'name' })
  },
  { prop: 'age', label: '年龄', width: 60, align: 'center' },
  {
    prop: 'sex',
    label: '性别',
    width: 80,
    align: 'center',
    render: ({ row }) => {
      return h(
        ElTag,
        { type: row.sex === '男' ? 'primary' : 'danger', size: 'small' },
        { default: () => row.sex }
      )
    },
    renderHeader: () => createTableHeader({ type: 'checkbox-group', label: '性别', field: 'sex' })
  },
  {
    prop: 'address',
    label: '地址',
    children: [
      { prop: 'city', label: '城市', width: 100 },
      { prop: 'district', label: '区县', width: 100 }
    ]
  },
  { prop: 'remark', label: '描述', width: 150, showOverflowTooltip: true },
  {
    prop: 'action',
    label: '操作',
    width: 140,
    align: 'center',
    slotName: 'action',
    fixed: 'right'
  }
]

// 编辑操作
function onEdit(scope: any) {
  ElMessage.success(`编辑 ${scope.row.name}`)
}
</script>

<style lang="scss" scoped></style>
useTableHeaderSearch.ts
ts
import { h, reactive } from 'vue'
import HeaderCheckboxGroup from './HeaderCheckboxGroup.vue'
import HeaderInput from './HeaderInput.vue'

export function useTableHeaderSearch(options: { search: () => void }) {
  const headerParams = reactive({} as Record<string, any>)

  function createTableHeader(params: { type: 'input' | 'checkbox-group', label: string, field: string }) {
    if (params.type === 'input') {
      return h(HeaderInput, { headerParams, label: params.label, field: params.field, onConfirm: () => options.search() })
    }
    if (params.type === 'checkbox-group') {
      return h(HeaderCheckboxGroup, { headerParams, label: params.label, field: params.field, onConfirm: () => options.search() })
    }
    return h('div', {}, params.label)
  }

  return {
    headerParams,
    createTableHeader
  }
}
HeaderInput.vue
vue
<template>
  <el-popover v-model:visible="visible" placement="bottom" :width="200">
    <template #reference>
      <HeaderLabel :label="props.label" :actived="!!props.headerParams[props.field]" @click="visible = true">
      </HeaderLabel>
    </template>
    <el-input v-model="props.headerParams[props.field]" :placeholder="`输入${props.label}查询`" clearable
      size="small"></el-input>
    <el-row justify="end" style="margin-top: 10px">
      <el-space>
        <el-button size="small" @click="visible = false">取消</el-button>
        <el-button type="primary" size="small" @click="onConfirm">确定</el-button>
      </el-space>
    </el-row>
  </el-popover>
</template>

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

interface Props {
  label: string
  field: string
  headerParams: Record<string, any>
}
const props = withDefaults(defineProps<Props>(), {})

const emit = defineEmits<{
  (e: 'confirm'): void
}>()

const visible = ref(false)
function onConfirm() {
  emit('confirm')
  visible.value = false
}
</script>
HeaderCheckboxGroup.vue
vue
<template>
  <el-popover :visible="visible" placement="bottom" :width="200" :popper-style="{ width: '140px', minWidth: '0px' }">
    <template #reference>
      <HeaderLabel :label="props.label" :actived="!!props.headerParams[props.field]?.length" @click="visible = true">
      </HeaderLabel>
    </template>
    <el-checkbox-group v-model="props.headerParams[props.field]" :options="options" />
    <el-row justify="end" style="margin-top: 10px">
      <el-space>
        <el-button size="small" @click="visible = false">取消</el-button>
        <el-button type="primary" size="small" @click="onConfirm">确定</el-button>
      </el-space>
    </el-row>
  </el-popover>
</template>

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

interface Props {
  label: string
  field: string
  headerParams?: Record<string, any>
}
const props = withDefaults(defineProps<Props>(), {
  headerParams: () => ({})
})

const emit = defineEmits<{
  (e: 'confirm'): void
}>()

const visible = ref(false)
const options = [
  { label: '男', value: '1' },
  { label: '女', value: '2' }
]
function onConfirm() {
  emit('confirm')
  visible.value = false
}
</script>

<style lang="scss" scoped>
:deep(.el-checkbox) {
  display: flex;
  margin-right: 0;
}
</style>

TableSetting 表格工具栏(参考)

文档示例组件 TableSetting 提供斑马纹、刷新、全屏、边框、表格尺寸与列设置(拖拽排序、显示隐藏、左/右固定),默认插槽向表格注入 settingColumnsisFullscreentablePropsstripebordersize),可与 gi-table 组合使用。

表格工具栏
暂无数据
  • 1
共 0 条
查看代码
vue
<template>
  <gi-page-layout bordered style="height: 520px">
    <template #tool>
      <el-row justify="space-between" class="g-tool g-w-full">
        <el-space wrap>
          <gi-button type="add"></gi-button>
          <gi-button type="delete"></gi-button>
        </el-space>
        <el-space wrap>
          <el-input v-model="queryParams.keyword" placeholder="搜索姓名或地址" clearable style="width: 200px" />
          <ElButton type="primary" @click="search">搜索</ElButton>
        </el-space>
      </el-row>
    </template>
    <TableSetting title="表格工具栏" :columns="columns" :disabled-column-keys="disabledColumnKeys" @refresh="refresh">
      <template #default="{ settingColumns, tableProps }">
        <gi-table v-loading="loading" v-bind="tableProps" :columns="settingColumns" :data="tableData"
          :pagination="pagination">
          <template #action="scope">
            <el-space>
              <ElButton type="primary" size="small" @click="onEdit(scope.row)">编辑</ElButton>
              <ElButton type="danger" size="small">删除</ElButton>
            </el-space>
          </template>
        </gi-table>
      </template>
    </TableSetting>
  </gi-page-layout>
</template>

<script lang="ts" setup>
import type { UserItem } from '@docs/_shared/apis/mockTable'
import type { TableColumnItem } from 'gi-component'
import { getUserList } from '@docs/_shared/apis/mockTable'
import { useTable } from '@docs/_shared/hooks'
import { ElButton, ElMessage, ElTag } from 'element-plus'
import { h, reactive } from 'vue'
import TableSetting from '../_fragments/TableSetting.vue'

const columns: TableColumnItem[] = [
  { type: 'selection', width: 55, align: 'center', fixed: 'left' },
  { type: 'index', label: '序号', width: 60, align: 'center' },
  {
    prop: 'name',
    label: '姓名',
    width: 100,
    align: 'center',
    showOverflowTooltip: true
  },
  { prop: 'age', label: '年龄', width: 60, align: 'center' },
  {
    prop: 'sex',
    label: '性别',
    width: 80,
    align: 'center',
    render: ({ row }) => {
      return h(
        ElTag,
        { type: row.sex === '男' ? 'primary' : 'danger', size: 'small' },
        { default: () => row.sex }
      )
    }
  },
  {
    prop: 'address',
    label: '地址'
  },
  { prop: 'remark', label: '描述', width: 150, showOverflowTooltip: true },
  {
    prop: 'action',
    label: '操作',
    width: 140,
    align: 'center',
    slotName: 'action',
    fixed: 'right'
  }
]

/** 与 TableSetting 内 getColumnKey 规则一致:无 prop 的列用 __type_{type}_{index}__(多选列不在列设置中) */
const disabledColumnKeys = ['__type_index_1__', 'name']

const queryParams = reactive({
  keyword: ''
})

const { tableData, pagination, search, refresh, loading } = useTable({
  listAPI: (p) => getUserList({ ...p, ...queryParams }),
  onSuccess: () => { }
})

function onEdit(row: UserItem) {
  ElMessage.success(`编辑 ${row.name}`)
}
</script>

<style lang="scss" scoped>
.demo-table-setting__title {
  font-size: 14px;
  font-weight: 500;
  line-height: 32px;
  color: var(--el-text-color-primary);
}
</style>
TableSetting.vue
vue
<template>
  <div class="table-setting" :class="{ 'table-setting--fullscreen': isFullscreen }">
    <div class="table-setting__toolbar">
      <div class="table-setting__toolbar-left">
        <slot name="toolbar-left">
          <span class="table-setting__title">{{ title }}</span>
        </slot>
      </div>
      <el-space wrap :size="8">
        <el-tooltip content="斑马纹" placement="top">
          <el-switch v-model="stripe" size="small" />
        </el-tooltip>
        <el-tooltip content="刷新" placement="top">
          <el-button class="table-setting__icon-btn" bg text circle @click="emit('refresh')">
            <el-icon :size="14">
              <RefreshRight />
            </el-icon>
          </el-button>
        </el-tooltip>
        <el-tooltip :content="isFullscreen ? '退出全屏' : '全屏'" placement="top">
          <el-button class="table-setting__icon-btn" bg text circle @click="toggleFullscreen">
            <el-icon :size="14">
              <ScaleToOriginal v-if="isFullscreen" />
              <FullScreen v-else />
            </el-icon>
          </el-button>
        </el-tooltip>
        <el-tooltip content="显示边框" placement="top">
          <el-button class="table-setting__icon-btn" bg text circle @click="toggleBorder">
            <el-icon :size="14">
              <Grid />
            </el-icon>
          </el-button>
        </el-tooltip>
        <el-dropdown trigger="click" @command="handleSizeCommand">
          <span class="el-dropdown-link">
            <el-tooltip content="表格尺寸" placement="top">
              <el-button class="table-setting__icon-btn" bg text circle>
                <el-icon :size="14">
                  <Switch />
                </el-icon>
              </el-button>
            </el-tooltip>
          </span>
          <template #dropdown>
            <el-dropdown-menu>
              <el-dropdown-item v-for="item in TABLE_SIZE_OPTIONS" :key="item.label" :command="item.value"
                :class="{ 'is-active': item.value === size }">
                {{ item.label }}
              </el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
        <el-popover v-if="showColumnSetting" placement="bottom-end" :width="180" trigger="click"
          transition="el-zoom-in-top">
          <template #reference>
            <el-button type="primary" bg text circle>
              <el-icon :size="14">
                <Setting />
              </el-icon>
            </el-button>
          </template>
          <div class="table-setting__popover">
            <el-scrollbar class="table-setting__draggable" height="200px" :wrap-style="{ overflowX: 'hidden' }">
              <VueDraggable v-model="settingColumnList" :animation="150" handle=".table-setting__drag-handle">
                <div v-for="item in settingColumnList" :key="item.key" class="table-setting__draggable-item">
                  <span class="table-setting__drag-handle">
                    <el-icon :size="14">
                      <Rank />
                    </el-icon>
                  </span>
                  <el-checkbox v-model="item.show" :disabled="item.disabled" class="table-setting__checkbox">
                    {{ item.title }}
                  </el-checkbox>
                  <div class="table-setting__pins">
                    <span class="table-setting__pin-btn" :class="{ 'is-active': item.fixedLeft }"
                      @click.stop="toggleFixedLeft(item.key)">
                      <el-icon :size="14">
                        <LocationFilled />
                      </el-icon>
                    </span>
                    <span class="table-setting__pin-btn table-setting__pin-btn--right"
                      :class="{ 'is-active': item.fixedRight }" @click.stop="toggleFixedRight(item.key)">
                      <el-icon :size="14">
                        <LocationFilled />
                      </el-icon>
                    </span>
                  </div>
                </div>
              </VueDraggable>
            </el-scrollbar>
            <el-divider style="margin: 8px 0" />
            <el-button type="primary" size="small" style="width: 100%" @click="resetSettingColumns">
              <el-icon class="el-icon--left">
                <RefreshRight />
              </el-icon>
              重置
            </el-button>
          </div>
        </el-popover>
      </el-space>
    </div>
    <div class="table-setting__body">
      <slot :setting-columns="settingColumns" :is-fullscreen="isFullscreen" :table-props="tableProps" />
    </div>
  </div>
</template>

<script lang="ts" setup>
import type { TableColumnItem } from 'gi-component'
import {
  FullScreen,
  Grid,
  LocationFilled,
  Rank,
  RefreshRight,
  ScaleToOriginal,
  Setting,
  Switch
} from '@element-plus/icons-vue'
import { computed, ref, watch } from 'vue'
import { VueDraggable } from 'vue-draggable-plus'

export interface TableSettingColumnItem {
  key: string
  title: string
  show: boolean
  disabled: boolean
  fixedLeft: boolean
  fixedRight: boolean
}

/** Element Plus 表格 size */
export type TableSettingSize = 'small' | 'default' | 'large'

interface TableSettingProps {
  title?: string
  columns?: TableColumnItem[]
  /** 不允许在列设置中切换显示/隐藏的列 key(仍可拖拽、固定) */
  disabledColumnKeys?: string[]
}

const props = withDefaults(defineProps<TableSettingProps>(), {
  title: '',
  columns: () => [],
  disabledColumnKeys: () => []
})

const emit = defineEmits<{
  refresh: []
}>()

const TABLE_SIZE_OPTIONS: { label: string, value: TableSettingSize }[] = [
  { label: '迷你', value: 'small' },
  { label: '中等', value: 'default' },
  { label: '大型', value: 'large' }
]

const stripe = ref(false)
const size = ref<TableSettingSize>('default')
const border = ref(true)
const isFullscreen = ref(false)

const tableProps = computed(() => ({
  stripe: stripe.value,
  border: border.value,
  size: size.value
}))

function toggleFullscreen() {
  isFullscreen.value = !isFullscreen.value
}

function toggleBorder() {
  border.value = !border.value
}

function handleSizeCommand(value: string | number | object) {
  size.value = value as TableSettingSize
}

/** 参与列设置(可拖拽 / 显隐 / 固定)的列;多选列不参与 */
function isColumnInSettingList(column: TableColumnItem): boolean {
  return column.type !== 'selection'
}

const showColumnSetting = computed(() => {
  const cols = props.columns ?? []
  return cols.some((c) => isColumnInSettingList(c))
})

function getColumnKey(column: TableColumnItem, index: number): string {
  if (column.prop != null && column.prop !== '')
    return String(column.prop)
  if (column.type)
    return `__type_${String(column.type)}_${index}__`
  if (typeof column.label === 'string' && column.label)
    return column.label
  return `__column_${index}__`
}

function columnTitle(column: TableColumnItem): string {
  const lab = column.label
  return typeof lab === 'string' ? lab : ''
}

const initialSettingColumns = computed<TableSettingColumnItem[]>(() => {
  const list = props.columns ?? []
  const out: TableSettingColumnItem[] = []
  list.forEach((column, index) => {
    if (!isColumnInSettingList(column))
      return
    const key = getColumnKey(column, index)
    const fixed = column.fixed
    out.push({
      key,
      title: columnTitle(column),
      show: true,
      disabled: props.disabledColumnKeys.includes(key),
      fixedLeft: fixed === 'left',
      fixedRight: fixed === 'right'
    })
  })
  return out
})

const settingColumnList = ref<TableSettingColumnItem[]>([])

function isColumnStructureMatch(
  user: TableSettingColumnItem[],
  initial: TableSettingColumnItem[]
): boolean {
  if (user.length === 0 || user.length !== initial.length)
    return false
  const initialKeys = new Set(initial.map((i) => i.key))
  const userKeys = new Set(user.map((i) => i.key))
  return initialKeys.size === userKeys.size && [...initialKeys].every((k) => userKeys.has(k))
}

const columnMap = computed(() => {
  const list = props.columns ?? []
  return new Map(list.map((col, index) => [getColumnKey(col, index), col]))
})

function resetSettingColumns() {
  settingColumnList.value = initialSettingColumns.value.map((i) => ({ ...i }))
}

function ensureSettingColumnList() {
  if (settingColumnList.value.length === 0 && initialSettingColumns.value.length > 0)
    settingColumnList.value = initialSettingColumns.value.map((i) => ({ ...i }))
}

function toggleFixedLeft(key: string) {
  ensureSettingColumnList()
  settingColumnList.value = settingColumnList.value.map((item) =>
    item.key === key ? { ...item, fixedLeft: !item.fixedLeft, fixedRight: false } : item
  )
}

function toggleFixedRight(key: string) {
  ensureSettingColumnList()
  settingColumnList.value = settingColumnList.value.map((item) =>
    item.key === key ? { ...item, fixedRight: !item.fixedRight, fixedLeft: false } : item
  )
}

watch(
  initialSettingColumns,
  (next) => {
    if (next.length === 0) {
      settingColumnList.value = []
      return
    }
    if (!isColumnStructureMatch(settingColumnList.value, next))
      settingColumnList.value = next.map((i) => ({ ...i }))
  },
  { immediate: true }
)

/** 多选列始终保留在表格中,且不参与列设置列表;按原始 columns 顺序排在最前 */
const selectionColumnsPrefix = computed(() => {
  const cols = props.columns ?? []
  return cols.filter((c) => c.type === 'selection') as TableColumnItem[]
})

const settingColumns = computed<TableColumnItem[]>(() => {
  const cols = props.columns ?? []
  if (!cols.length)
    return []

  const prefix = selectionColumnsPrefix.value

  if (!settingColumnList.value.length)
    return prefix.length ? [...prefix] : []

  const shown = settingColumnList.value.filter((item) => item.show)
  const leftFixed: typeof shown = []
  const noFixed: typeof shown = []
  const rightFixed: typeof shown = []
  for (const item of shown) {
    if (item.fixedLeft)
      leftFixed.push(item)
    else if (item.fixedRight)
      rightFixed.push(item)
    else
      noFixed.push(item)
  }
  const ordered = [...leftFixed, ...noFixed, ...rightFixed]

  const body = ordered
    .map((item) => {
      const col = columnMap.value.get(item.key)
      if (!col)
        return null
      const fixed = item.fixedRight ? 'right' : item.fixedLeft ? 'left' : undefined
      return { ...col, fixed } as TableColumnItem
    })
    .filter(Boolean) as TableColumnItem[]

  return [...prefix, ...body]
})
</script>

<style lang="scss" scoped>
.table-setting {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  background: var(--el-bg-color);

  &__title {
    font-size: 15px;
    font-weight: 600;
    line-height: 32px;
    color: var(--el-text-color-primary);
  }

  &--fullscreen {
    position: fixed;
    inset: 0;
    z-index: 2000;
    padding: 12px;
    box-sizing: border-box;
  }

  &__toolbar {
    display: flex;
    flex-shrink: 0;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
  }

  &__toolbar-left {
    flex: 1;
    min-width: 0;
  }

  &__body {
    flex: 1;
    min-height: 0;
    overflow: hidden;
  }

  &__draggable {
    box-sizing: border-box;
    padding: 2px 0;
    box-sizing: border-box;

    :deep(.el-scrollbar__wrap) {
      overflow-x: hidden !important;
    }

    :deep(.el-scrollbar__view) {
      box-sizing: border-box;
      min-width: 0;
      overflow-x: hidden;
    }
  }

  &__draggable-item {
    display: flex;
    align-items: center;
    padding: 2px 4px;
    cursor: pointer;
    border-radius: var(--el-border-radius-small);
    box-sizing: border-box;

    &:hover {
      background-color: var(--el-fill-color-light);
    }
  }

  &__drag-handle {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    padding: 0 4px;
    box-sizing: border-box;
    color: var(--el-text-color-secondary);
    cursor: move;
  }

  &__checkbox {
    flex: 1;
    min-width: 0;
    margin-right: 4px;
    font-size: 12px;

    :deep(.el-checkbox__label) {
      font-size: 12px;
      color: var(--el-text-color-regular);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }

  :deep(.el-checkbox.is-disabled .el-checkbox__label) {
    color: var(--el-text-color-placeholder);
  }

  &__pins {
    display: flex;
    flex-shrink: 0;
    gap: 2px;
    align-items: center;
    margin-left: auto;
    padding-right: 10px;
  }

  &__pin-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 2px;
    color: var(--el-text-color-placeholder);
    cursor: pointer;
    transition: color 0.2s;

    &:hover {
      color: var(--el-text-color-secondary);
    }

    &.is-active {
      color: var(--el-color-primary);
    }

    &--right {
      transform: scaleX(-1);
    }
  }
}

:deep(.el-dropdown-menu__item.is-active) {
  font-weight: 600;
  color: var(--el-color-primary);
}
</style>

API 使用说明

组件属性

属性名类型默认值说明
columnsTableColumnItem[][]表格列配置数组
dataany[]-表格数据
paginationPartial<PaginationProps> | boolean{}分页配置
其他属性--支持 Element Plus Table 组件的所有属性

TableColumnItem 接口

TableColumnItem 接口继承自 Element Plus 的 TableColumnInstance['$props'],并扩展了以下属性:

属性名类型说明
slotNamestring用于自定义列内容的插槽名称
childrenTableColumnItem[]子列配置,用于创建嵌套表头
render(scope: TableColumnCtx<any>) => VNode | VNode[] | string自定义渲染函数,用于动态生成单元格内容
其他属性-支持 Element Plus TableColumn 的所有属性

Pagination 配置

pagination 属性支持 Element Plus Pagination 组件的所有配置项,常用配置如下:

事件

支持 Element Plus Table 组件的所有事件。

插槽

除了通过 slotName 属性定义的自定义列插槽外,还支持 Element Plus Table 组件的所有作用域插槽。