Skip to content
API 风格:

Tableau ​

Tableau configuré par colonnes. Prend en charge le tri, les colonnes fixes, les rayures de zèbre, les slots de cellule, ainsi que le défilement virtuel pour les gros volumes de données (fenêtre visible calculée par des fonctions pures du core, implémentation maison sans bibliothèque externe).

Utilisation de base ​

Utilisation de base
Nom
Rôle
Ville
Shen Yan
Architecte
Hangzhou
Su Wan
Frontend
Suzhou
Lu Li
Designer
Pékin
vue
<script setup>
const cols = [
  { prop: 'name', label: 'Nom', width: 140 },
  { prop: 'role', label: 'Rôle' },
  { prop: 'city', label: 'Ville', width: 120 },
]
const rows = [
  { id: 1, name: 'Shen Yan', role: 'Architecte', city: 'Hangzhou' },
  { id: 2, name: 'Su Wan', role: 'Frontend', city: 'Suzhou' },
  { id: 3, name: 'Lu Li', role: 'Designer', city: 'Pékin' },
]
</script>

<template>
  <LhTable :data="rows" :columns="cols" />
</template>
vue
<script>
export default {
  data() {
    return {
      cols: [
        { prop: 'name', label: 'Nom', width: 140 },
        { prop: 'role', label: 'Rôle' },
        { prop: 'city', label: 'Ville', width: 120 },
      ],
      rows: [
        { id: 1, name: 'Shen Yan', role: 'Architecte', city: 'Hangzhou' },
        { id: 2, name: 'Su Wan', role: 'Frontend', city: 'Suzhou' },
        { id: 3, name: 'Lu Li', role: 'Designer', city: 'Pékin' },
      ],
    }
  },
}
</script>

<template>
  <LhTable :data="rows" :columns="cols" />
</template>

Tri et colonnes fixes ​

Tri + colonnes fixes + rayures de zèbre
Nom
Rôle
Ville
Shen Yan
34
Architecte
Hangzhou
Su Wan
28
Frontend
Suzhou
Lu Li
31
Designer
Pékin
vue
<script setup>
const sortCols = [
  { prop: 'name', label: 'Nom', width: 140, fixed: 'left' },
  { prop: 'age', label: 'Âge', width: 100, sortable: true, align: 'center' },
  { prop: 'role', label: 'Rôle' },
  { prop: 'city', label: 'Ville', width: 120 },
]
</script>

<template>
  <LhTable :data="rows" :columns="sortCols" stripe border size="small" />
</template>
vue
<script>
export default {
  data() {
    return {
      sortCols: [
        { prop: 'name', label: 'Nom', width: 140, fixed: 'left' },
        { prop: 'age', label: 'Âge', width: 100, sortable: true, align: 'center' },
        { prop: 'role', label: 'Rôle' },
        { prop: 'city', label: 'Ville', width: 120 },
      ],
    }
  },
}
</script>

<template>
  <LhTable :data="rows" :columns="sortCols" stripe border size="small" />
</template>

Défilement virtuel (gros volumes de données) ​

Définissez height pour l'activer : seules les lignes de la fenêtre visible sont rendues ; rowHeight doit correspondre à la hauteur réelle des lignes.

Défilement virtuel sur 1000 lignes
Nom
Rôle
Ville
Ligne 1
Architecte
Hangzhou
Ligne 2
Frontend
Hangzhou
Ligne 3
Architecte
Hangzhou
Ligne 4
Frontend
Hangzhou
Ligne 5
Architecte
Hangzhou
Ligne 6
Frontend
Hangzhou
vue
<script setup lang="ts">
const cols = [
  { prop: 'name', label: 'Nom', width: 140 },
  { prop: 'role', label: 'Rôle' },
  { prop: 'city', label: 'Ville', width: 120 },
]

const bigRows = Array.from({ length: 1000 }, (_, i) => ({
  id: i + 1,
  name: `Ligne ${i + 1}`,
  role: i % 2 === 0 ? 'Architecte' : 'Frontend',
  city: 'Hangzhou',
}))
</script>

<template>
  <LhTable :data="bigRows" :columns="cols" :height="240" :row-height="48" />
</template>
vue
<script>
export default {
  data() {
    return {
      cols: [
        { prop: 'name', label: 'Nom', width: 140 },
        { prop: 'role', label: 'Rôle' },
        { prop: 'city', label: 'Ville', width: 120 },
      ],
      bigRows: Array.from({ length: 1000 }, (_, i) => ({
        id: i + 1,
        name: `Ligne ${i + 1}`,
        role: i % 2 === 0 ? 'Architecte' : 'Frontend',
        city: 'Hangzhou',
      })),
    }
  },
}
</script>

<template>
  <LhTable :data="bigRows" :columns="cols" :height="240" :row-height="48" />
</template>

Cellule personnalisée ​

Personnalisation par slot
Nom
Rôle
Shen Yan
Architecte
Su Wan
Frontend
Lu Li
Designer
vue
<script setup lang="ts">
const rows = [
  { id: 1, name: 'Shen Yan', age: 34, role: 'Architecte', city: 'Hangzhou' },
  { id: 2, name: 'Su Wan', age: 28, role: 'Frontend', city: 'Suzhou' },
  { id: 3, name: 'Lu Li', age: 31, role: 'Designer', city: 'Pékin' },
]
const slotCols = [
  { prop: 'name', label: 'Nom', width: 140 },
  { prop: 'role', label: 'Rôle', slot: 'role' },
]
</script>

<template>
  <!-- Slot personnalisé pour la colonne Rôle -->
  <LhTable :data="rows" :columns="slotCols">
    <template #role="{ value }">
      <LhTag :type="value === 'Architecte' ? 'primary' : 'info'">{{ value }}</LhTag>
    </template>
  </LhTable>
</template>
vue
<script>
export default {
  data() {
    return {
      rows: [
        { id: 1, name: 'Shen Yan', age: 34, role: 'Architecte', city: 'Hangzhou' },
        { id: 2, name: 'Su Wan', age: 28, role: 'Frontend', city: 'Suzhou' },
        { id: 3, name: 'Lu Li', age: 31, role: 'Designer', city: 'Pékin' },
      ],
      slotCols: [
        { prop: 'name', label: 'Nom', width: 140 },
        { prop: 'role', label: 'Rôle', slot: 'role' },
      ],
    }
  },
}
</script>

<template>
  <!-- Slot personnalisé pour la colonne Rôle -->
  <LhTable :data="rows" :columns="slotCols">
    <template #role="{ value }">
      <LhTag :type="value === 'Architecte' ? 'primary' : 'info'">{{ value }}</LhTag>
    </template>
  </LhTable>
</template>

En-têtes multi-niveaux ​

Imbriquez children dans la configuration des colonnes pour obtenir des en-têtes multi-niveaux, avec une imbrication illimitée ; le corps du tableau s'aligne sur les colonnes feuilles.

En-têtes à deux niveaux
Nom
Informations du projet
Rôle
Ville
Shen Yan
Architecte
Hangzhou
34
Su Wan
Frontend
Suzhou
28
Lu Li
Designer
Pékin
31
vue
<script setup>
const nestedCols = [
  { prop: 'name', label: 'Nom', width: 140, fixed: 'left' },
  {
    label: 'Informations du projet',
    children: [
      { prop: 'role', label: 'Rôle' },
      { prop: 'city', label: 'Ville', width: 120 },
    ],
  },
  { prop: 'age', label: 'Âge', sortable: true, align: 'center' },
]
</script>

<template>
  <LhTable :data="rows" :columns="nestedCols" border />
</template>
vue
<script>
export default {
  data() {
    return {
      nestedCols: [
        { prop: 'name', label: 'Nom', width: 140, fixed: 'left' },
        {
          label: 'Informations du projet',
          children: [
            { prop: 'role', label: 'Rôle' },
            { prop: 'city', label: 'Ville', width: 120 },
          ],
        },
        { prop: 'age', label: 'Âge', sortable: true, align: 'center' },
      ],
    }
  },
}
</script>

<template>
  <LhTable :data="rows" :columns="nestedCols" border />
</template>

Filtrage ​

Configurez filters sur une colonne pour faire apparaître un déclencheur de filtrage dans l'en-tête ; cochez les options puis cliquez sur « OK » pour appliquer ou « Réinitialiser » pour effacer. filterMultiple: false correspond à une sélection unique et filterMethod permet une logique de filtrage personnalisée ; l'usage contrôlé est illustré ci-dessous.

Filtrage de colonne (contrôlé)
Nom
Rôle
Shen Yan
Architecte
Su Wan
Frontend
Lu Li
Designer

Filtre actuel : {}

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

const filteredValues = ref({})

const filterCols = [
  { prop: 'name', label: 'Nom', width: 140 },
  {
    prop: 'role', label: 'Rôle', filters: [
      { text: 'Architecte', value: 'Architecte' },
      { text: 'Frontend', value: 'Frontend' },
      { text: 'Designer', value: 'Designer' },
    ],
  },
]

function onFilterChange({ prop, values }) {
  console.log('filterChange', prop, values)
}
</script>

<template>
  <!-- Filtrage contrôlé -->
  <LhTable
    :data="rows"
    :columns="filterCols"
    v-model:filtered-values="filteredValues"
    @filter-change="onFilterChange"
  />
</template>
vue
<script>
export default {
  data() {
    return {
      filteredValues: {},
      filterCols: [
        { prop: 'name', label: 'Nom', width: 140 },
        {
          prop: 'role', label: 'Rôle', filters: [
            { text: 'Architecte', value: 'Architecte' },
            { text: 'Frontend', value: 'Frontend' },
            { text: 'Designer', value: 'Designer' },
          ],
        },
      ],
    }
  },
  methods: {
    onFilterChange({ prop, values }) {
      console.log('filterChange', prop, values)
    },
  },
}
</script>

<template>
  <!-- Filtrage contrôlé -->
  <LhTable
    :data="rows"
    :columns="filterCols"
    v-model:filtered-values="filteredValues"
    @filter-change="onFilterChange"
  />
</template>

Redimensionnement des colonnes ​

Activez resizable (commutateur global) et marquez les colonnes avec resizable: true (colonnes feuilles dont le width est un nombre). Une poignée de redimensionnement apparaît sur le bord droit de l'en-tête ; une fois focalisée, les touches ←/→ ajustent la largeur de ±10 px, avec un minimum de 40 px lors du glissement.

Redimensionnement des colonnes par glisser
Nom
Rôle
Ville
Shen Yan
Architecte
Hangzhou
Su Wan
Frontend
Suzhou
Lu Li
Designer
Pékin

Dernier ajustement : (aucun ajustement)

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

const resizeLog = ref('(aucun ajustement)')

const resizeCols = [
  { prop: 'name', label: 'Nom', width: 140, resizable: true, fixed: 'left' },
  { prop: 'role', label: 'Rôle' },
  { prop: 'city', label: 'Ville', width: 120, resizable: true },
]

function onColumnResize({ prop, width }) {
  resizeLog.value = `${prop} → ${width}px`
}
</script>

<template>
  <!-- Redimensionnement des colonnes -->
  <LhTable :data="rows" :columns="resizeCols" resizable border @column-resize="onColumnResize" />
</template>
vue
<script>
export default {
  data() {
    return {
      resizeLog: '(aucun ajustement)',
      resizeCols: [
        { prop: 'name', label: 'Nom', width: 140, resizable: true, fixed: 'left' },
        { prop: 'role', label: 'Rôle' },
        { prop: 'city', label: 'Ville', width: 120, resizable: true },
      ],
    }
  },
  methods: {
    onColumnResize({ prop, width }) {
      this.resizeLog = `${prop} → ${width}px`
    },
  },
}
</script>

<template>
  <!-- Redimensionnement des colonnes -->
  <LhTable :data="rows" :columns="resizeCols" resizable border @column-resize="onColumnResize" />
</template>

Clé de ligne, hauteur maximale et tri par défaut ​

row-key définit la clé unique d'une ligne (recommandé) ; avec max-height, l'en-tête reste fixe et le corps défile ; sorter fournit une fonction de tri personnalisée ; default-sort-prop / default-sort-order définissent le tri initial ; default-filtered-values définit le filtrage initial.

rowKey / maxHeight / tri par défaut
Nom
Rôle
Shen Yan
34
Architecte
Lu Li
31
Designer
Su Wan
28
Frontend
vue
<script setup>
const sorterCols = [
  { prop: 'name', label: 'Nom', width: 140 },
  { prop: 'age', label: 'Âge', sortable: true, sorter: (a, b) => a.age - b.age },
  { prop: 'role', label: 'Rôle' },
]
</script>

<template>
  <LhTable
    :data="rows"
    :columns="sorterCols"
    row-key="name"
    max-height="200"
    default-sort-prop="age"
    default-sort-order="desc"
  />
</template>
vue
<script>
export default {
  data() {
    return {
      sorterCols: [
        { prop: 'name', label: 'Nom', width: 140 },
        { prop: 'age', label: 'Âge', sortable: true, sorter: (a, b) => a.age - b.age },
        { prop: 'role', label: 'Rôle' },
      ],
    }
  },
}
</script>

<template>
  <LhTable
    :data="rows"
    :columns="sorterCols"
    row-key="name"
    max-height="200"
    default-sort-prop="age"
    default-sort-order="desc"
  />
</template>

API ​

LhTable Props ​

PropDescriptionTypeValeur par défaut
dataDonnées des lignes[LhTableRow](#type-LhTableRow)[]—
columnsConfiguration des colonnes[LhTableColumn](#type-LhTableColumn)[]—
heightHauteur du corps du tableau (le défilement virtuel est activé une fois défini ; un nombre correspond à des px)string | number—
maxHeightHauteur maximale hors mode virtuelstring | number—
rowHeightHauteur de ligne du défilement virtuel (doit correspondre à la hauteur réelle)number[LH_TABLE_ROW_HEIGHT](#const-LH_TABLE_ROW_HEIGHT)
stripeRayures de zèbrebooleanfalse
borderBordures des cellulesbooleanfalse
sizeTaille"small" | "default" | "large"'default'
rowKeyNom du champ servant de clé de lignestring'id'
defaultSortPropColonne triée par défautstring—
defaultSortOrderDirection du tri par défaut"asc" | "desc"—
sorterFonction de tri personnalisée(a: LhTableRow, b: LhTableRow) => number—
resizableActiver le redimensionnement des colonnes (requiert également resizable au niveau des colonnes)booleanfalse
defaultFilteredValuesÉtat de filtrage par défaut (non contrôlé, clés = prop des colonnes){ [x: string]: (string | number)[]; }() => ({})
filteredValuesÉtat de filtrage contrôlé (clés = prop des colonnes){ [x: string]: (string | number)[]; }—
sxProps de mise en page sxLhSxProps—

Events ​

ÉvénementDescriptionParamètre
sortChangeDéclenché lors d'un changement de tri (une nouvelle colonne recommence à asc ; une même colonne boucle asc → desc → annulation)(payload: { prop: string, order: 'asc' | 'desc' | undefined })
rowClickDéclenché lors du clic sur une ligne(row: LhTableRow, index: number)
filterChangeDéclenché après la soumission du filtrage (« OK » ou « Réinitialiser »)(payload: { prop: string, values: (string | number)[] })
update:filteredValuesDéclenché lors d'un changement de l'état de filtrage (v-model:filtered-values)(values: Record<string, (string | number)[]>)
columnResizeDéclenché à la fin d'un redimensionnement de colonne (fin du glisser ou touches ←/→)(payload: { prop: string, width: number })

Slots : empty, <column.slot> (paramètres déportés { row, column, index, value }).

LhTableColumn ​

ChampDescriptionType
propNom du champ de donnéesstring
labelTitre de la colonnestring
widthLargeur de la colonne (un nombre correspond à des px ; en l'absence de valeur, la largeur est répartie)number | string
alignAlignement'left' | 'center' | 'right'
fixedColonne fixe (requiert width)'left' | 'right'
sortableTriableboolean
slotNom du slot de cellule personnaliséestring
formatterFormatage de la valeur(row, value) => string
childrenColonnes enfants (produit des en-têtes multi-niveaux)LhTableColumn[]
filtersOptions de filtrage de la colonne (colonnes feuilles uniquement)LhTableFilterOption[]
filterMethodLogique de filtrage personnalisée (par défaut, comparaison d'égalité stricte sur prop)(value, row) => boolean
filterMultipleSélection multiple dans le panneau de filtrageboolean
resizableLa colonne est redimensionnable (doit être une colonne feuille avec un width numérique)boolean

Accessibilité ​

  • role="grid" + aria-rowcount / aria-colcount ; lignes row, en-têtes de colonne columnheader, cellules gridcell
  • Les en-têtes de colonne triables exposent aria-sort (none / ascending / descending), et le déclencheur de tri est un véritable <button>
  • Les colonnes fixes utilisent position: sticky en CSS (pas de calcul d'adhésion maison)
  • Le déclencheur de filtrage porte aria-haspopup + aria-expanded, et le panneau a role="group"
  • La poignée de redimensionnement a role="separator", est focalisable et s'ajuste par ±10 px avec les touches ←/→

Définitions de types ​

Ce composant exporte les structures de types TypeScript suivantes (extraites automatiquement de packages/components/table/src/types.ts et des sources @lh-ui/share/core/* ; cliquez sur chaque entrée pour déplier la définition complète ; la description des props se trouve dans le tableau Props ci-dessus) :

LhTableProps
ts
export interface LhTableProps {
  /** Données des lignes */
  data: LhTableRow[]
  /** Configuration des colonnes */
  columns: LhTableColumn[]
  /** Hauteur du corps du tableau (le défilement virtuel est activé une fois défini ; un nombre correspond à des px) */
  height?: number | string
  /** Hauteur maximale hors mode virtuel */
  maxHeight?: number | string
  /** Hauteur de ligne du défilement virtuel (doit correspondre à la hauteur réelle) */
  rowHeight?: number
  /** Rayures de zèbre */
  stripe?: boolean
  /** Bordures des cellules */
  border?: boolean
  /** Taille */
  size?: 'small' | 'default' | 'large'
  /** Nom du champ servant de clé de ligne */
  rowKey?: string
  /** Colonne triée par défaut */
  defaultSortProp?: string
  /** Direction du tri par défaut */
  defaultSortOrder?: LhTableSortOrder
  /** Fonction de tri personnalisée */
  sorter?: (a: LhTableRow, b: LhTableRow) => number
  /** Active le redimensionnement des colonnes (requiert également `resizable` au niveau des colonnes, et un `width` numérique) */
  resizable?: boolean
  /** État de filtrage par défaut (non contrôlé, clés = prop des colonnes) */
  defaultFilteredValues?: Record<string, (string | number)[]>
  /** État de filtrage contrôlé (clés = prop des colonnes) */
  filteredValues?: Record<string, (string | number)[]>
  /** Props de mise en page sx */
  sx?: LhSxProps
}
LhTableSortOrder (provenant de @lh-ui/share/core/table)
ts
/** Direction du tri */
export type LhTableSortOrder = 'asc' | 'desc'
LhTableRow (provenant de @lh-ui/share/core/table)
ts
/** Type de données d'une ligne */
export type LhTableRow = Record<string, any>
LhTableColumn (provenant de @lh-ui/share/core/table)
ts
/** Configuration d'une colonne */
export interface LhTableColumn {
  /** Nom du champ de données */
  prop: string
  /** Titre de la colonne */
  label: string
  /** Largeur de la colonne (un nombre correspond à des px) */
  width?: number | string
  /** Alignement */
  align?: LhTableAlign
  /** Colonne fixe (requiert width) */
  fixed?: 'left' | 'right'
  /** Triable */
  sortable?: boolean
  /** Nom du slot de cellule personnalisée */
  slot?: string
  /** Formatage de la valeur */
  formatter?: (row: LhTableRow, value: any) => string
  /** Colonnes enfants (produit des en-têtes multi-niveaux, imbrication illimitée) */
  children?: LhTableColumn[]
  /** Options de filtrage de la colonne (colonnes feuilles uniquement) */
  filters?: LhTableFilterOption[]
  /** Logique de filtrage personnalisée (par défaut, comparaison d'égalité stricte sur prop) */
  filterMethod?: (value: string | number, row: LhTableRow) => boolean
  /** Sélection multiple dans le panneau de filtrage (true par défaut) */
  filterMultiple?: boolean
  /** La colonne est redimensionnable (doit être une colonne feuille avec un width numérique) */
  resizable?: boolean
}
LhTableFilterOption (provenant de @lh-ui/share/core/table)
ts
/** Option de filtrage */
export interface LhTableFilterOption {
  /** Libellé de l'option */
  text: string
  /** Valeur de l'option (prise en compte par filterMethod) */
  value: string | number
}
LhTableAlign (provenant de @lh-ui/share/core/table)
ts
/** Alignement des cellules */
export type LhTableAlign = 'left' | 'center' | 'right'

Constantes ​

Les constantes publiques utilisées par ce composant sont les suivantes (vous pouvez cliquer sur une entrée de même nom dans les colonnes « Type / Valeur par défaut » du tableau Props pour sauter jusqu'ici) :

LH_TABLE_ROW_HEIGHT (provenant de @lh-ui/share/core/table)
ts
/** Hauteur de ligne par défaut du défilement virtuel */
export const LH_TABLE_ROW_HEIGHT = 48
LH_TABLE_OVERSCAN (provenant de @lh-ui/share/core/table)
ts
/** Nombre de lignes supplémentaires rendues au-dessus et au-dessous de la fenêtre (tampon anti-scintillement) */
export const LH_TABLE_OVERSCAN = 3
LH_TABLE_MIN_COL_WIDTH (provenant de @lh-ui/share/core/table)
ts
// ============================================================================
// Redimensionnement des colonnes
// ============================================================================

/** Largeur minimale lors du redimensionnement (px) ; en deçà, le contenu des cellules n'est plus tenable */
export const LH_TABLE_MIN_COL_WIDTH = 40

La beauté de la tradition, la fondation du code