All checks were successful
Release / release (push) Successful in 1m11s
| Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié Co-authored-by: kevin <kevin@yuno.malio.fr> Co-authored-by: matthieu <matthieu@yuno.malio.fr> Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr> Reviewed-on: #43 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
486 lines
18 KiB
Markdown
486 lines
18 KiB
Markdown
# @malio/layer-ui — Composants
|
|
|
|
Tous les composants sont auto-importés avec le préfixe `Malio`. Utiliser `v-model` pour le binding bidirectionnel sur les composants de formulaire.
|
|
|
|
---
|
|
|
|
## MalioInputText
|
|
|
|
Champ texte avec label, icône optionnelle et support de masque de saisie.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `id` | `string` | auto | Identifiant HTML |
|
|
| `label` | `string` | `''` | Label du champ |
|
|
| `modelValue` | `string \| null` | `undefined` | Valeur (v-model) |
|
|
| `disabled` | `boolean` | `false` | Désactive le champ |
|
|
| `readonly` | `boolean` | `false` | Lecture seule |
|
|
| `required` | `boolean` | `false` | Champ requis |
|
|
| `hint` | `string` | `''` | Message d'aide |
|
|
| `error` | `string` | `''` | Message d'erreur |
|
|
| `success` | `string` | `''` | Message de succès |
|
|
| `iconName` | `string` | `''` | Icône Iconify (ex: `mdi:magnify`) |
|
|
| `iconPosition` | `'left' \| 'right'` | `'right'` | Position de l'icône |
|
|
| `iconSize` | `string \| number` | `24` | Taille icône |
|
|
| `iconColor` | `string` | `'text-m-muted'` | Classe couleur icône |
|
|
| `mask` | `string \| MaskInputOptions` | — | Masque de saisie (maska) |
|
|
| `maxLength` | `number \| string` | — | Longueur max |
|
|
| `minLength` | `number \| string` | — | Longueur min |
|
|
| `inputClass` | `string` | `''` | Classes CSS input |
|
|
| `labelClass` | `string` | `''` | Classes CSS label |
|
|
| `groupClass` | `string` | `''` | Classes CSS conteneur |
|
|
|
|
**Events :** `update:modelValue(value: string)`
|
|
|
|
```vue
|
|
<MalioInputText v-model="nom" label="Nom" />
|
|
<MalioInputText v-model="search" label="Recherche" icon-name="mdi:magnify" />
|
|
<MalioInputText v-model="tel" label="Téléphone" mask="## ## ## ## ##" />
|
|
<MalioInputText v-model="email" label="Email" error="Email invalide" />
|
|
<MalioInputText v-model="info" label="Info" disabled hint="Champ désactivé" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioInputPassword
|
|
|
|
Champ mot de passe avec toggle visibilité.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `string \| null` | `undefined` | Valeur (v-model) |
|
|
| `label` | `string` | `''` | Label |
|
|
| `displayIcon` | `boolean` | `true` | Afficher l'icône toggle |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
| `readonly` | `boolean` | `false` | Lecture seule |
|
|
| `hint` | `string` | `''` | Message d'aide |
|
|
| `error` | `string` | `''` | Message d'erreur |
|
|
| `success` | `string` | `''` | Message de succès |
|
|
|
|
**Events :** `update:modelValue(value: string)`
|
|
|
|
```vue
|
|
<MalioInputPassword v-model="password" label="Mot de passe" />
|
|
<MalioInputPassword v-model="password" label="Sans icône" :display-icon="false" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioInputAmount
|
|
|
|
Champ montant avec icône devise (euro par défaut).
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `string \| null` | `undefined` | Valeur (v-model) |
|
|
| `label` | `string` | `''` | Label |
|
|
| `iconName` | `string` | `'mdi:currency-eur'` | Icône devise |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
| `error` | `string` | `''` | Message d'erreur |
|
|
|
|
**Events :** `update:modelValue(value: string)`
|
|
|
|
```vue
|
|
<MalioInputAmount v-model="montant" label="Montant TTC" />
|
|
<MalioInputAmount v-model="prix" label="Prix" error="Montant invalide" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioInputNumber
|
|
|
|
Champ numérique avec boutons +/-.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `string \| null` | `undefined` | Valeur (v-model) |
|
|
| `label` | `string` | `''` | Label |
|
|
| `min` | `number \| string` | — | Valeur minimum |
|
|
| `max` | `number \| string` | — | Valeur maximum |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
| `error` | `string` | `''` | Message d'erreur |
|
|
|
|
**Events :** `update:modelValue(value: string)`
|
|
|
|
```vue
|
|
<MalioInputNumber v-model="quantite" label="Quantité" min="0" max="100" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioInputTextArea
|
|
|
|
Zone de texte multiligne avec compteur et redimensionnement.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `string \| null` | `undefined` | Valeur (v-model) |
|
|
| `label` | `string` | `''` | Label |
|
|
| `size` | `number \| string` | `2` | Nombre de lignes |
|
|
| `resize` | `'none' \| 'both' \| 'horizontal' \| 'vertical'` | `'both'` | Mode redimensionnement |
|
|
| `maxLength` | `number` | `800` | Longueur max |
|
|
| `showCounter` | `boolean` | `false` | Afficher le compteur |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
| `error` | `string` | `''` | Message d'erreur |
|
|
|
|
**Events :** `update:modelValue(value: string)`
|
|
|
|
```vue
|
|
<MalioInputTextArea v-model="commentaire" label="Commentaire" :show-counter="true" />
|
|
<MalioInputTextArea v-model="note" label="Note" resize="vertical" :size="4" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioInputRichText
|
|
|
|
Éditeur de texte riche basé sur **TipTap v3** + **StarterKit** + **tiptap-markdown** + **TextStyle/Color/Highlight**. Toolbar avec gras, italique, barré, titres H2/H3, listes, citation, code, code-block, lien, **couleur du texte**, **surlignage**, undo/redo. Sortie en HTML (par défaut) ou markdown.
|
|
|
|
> Couleurs et surlignages ne sont **pas persistés en markdown**. Pour les conserver au save/reload, utiliser `output-format="html"`.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `id` | `string` | auto | Identifiant HTML |
|
|
| `label` | `string` | `''` | Label affiché au-dessus de l'éditeur |
|
|
| `modelValue` | `string \| null` | `undefined` | Contenu (v-model) |
|
|
| `placeholder` | `string` | `''` | Texte affiché quand vide |
|
|
| `minHeight` | `string` | `'160px'` | Hauteur min de la zone d'édition |
|
|
| `editable` | `boolean` | `true` | `false` → mode affichage seul (toolbar masquée) |
|
|
| `disabled` | `boolean` | `false` | Désactive l'édition et la toolbar |
|
|
| `readonly` | `boolean` | `false` | Lecture seule (toolbar visible mais désactivée) |
|
|
| `hint` | `string` | `''` | Message d'aide |
|
|
| `error` | `string` | `''` | Message d'erreur |
|
|
| `success` | `string` | `''` | Message de succès |
|
|
| `outputFormat` | `'markdown' \| 'html'` | `'html'` | Format émis dans `update:modelValue` |
|
|
| `groupClass` | `string` | `''` | Classes CSS conteneur (twMerge) |
|
|
| `labelClass` | `string` | `''` | Classes CSS label (twMerge) |
|
|
| `editorClass` | `string` | `''` | Classes CSS wrapper éditeur (twMerge) |
|
|
|
|
**Events :** `update:modelValue(value: string)`
|
|
|
|
```vue
|
|
<MalioInputRichText v-model="note" label="Note" placeholder="Écrire ici…" />
|
|
<MalioInputRichText v-model="cr" label="Compte-rendu" error="Trop court" />
|
|
<MalioInputRichText v-model="article" label="Article" min-height="240px" />
|
|
<MalioInputRichText :model-value="content" :editable="false" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioInputUpload
|
|
|
|
Champ d'upload de fichier.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `string \| null` | `undefined` | Nom du fichier (v-model) |
|
|
| `label` | `string` | `''` | Label |
|
|
| `accept` | `string` | `''` | Types de fichiers acceptés |
|
|
| `displayIcon` | `boolean` | `true` | Afficher l'icône |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
| `error` | `string` | `''` | Message d'erreur |
|
|
|
|
**Events :** `update:modelValue(value: string)`, `file-selected(file: File)`
|
|
|
|
```vue
|
|
<MalioInputUpload v-model="fileName" label="Document" accept=".pdf,.doc" @file-selected="onFile" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioSelect
|
|
|
|
Liste déroulante.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `string \| number \| null` | **requis** | Valeur sélectionnée (v-model) |
|
|
| `options` | `{ value: string \| number, text: string }[]` | `[]` | Options disponibles |
|
|
| `emptyOptionLabel` | `string` | `''` | Placeholder option vide |
|
|
| `label` | `string` | `''` | Label |
|
|
| `hint` | `string` | `''` | Message d'aide |
|
|
| `error` | `string` | `''` | Message d'erreur |
|
|
| `success` | `string` | `''` | Message de succès |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
| `groupClass` | `string` | `''` | Classes CSS conteneur (twMerge) |
|
|
| `rounded` | `string` | `'rounded-md'` | Classe border-radius |
|
|
| `textField` | `string` | `'text-lg'` | Classe taille texte bouton |
|
|
| `textValue` | `string` | `'text-lg'` | Classe taille texte valeur |
|
|
| `textLabel` | `string` | `'text-sm'` | Classe taille texte label |
|
|
|
|
**Events :** `update:modelValue(value: string | number | null)`
|
|
**Slots :** `icon` (icône dropdown custom)
|
|
|
|
```vue
|
|
<MalioSelect v-model="pays" label="Pays" :options="[{ value: 'FR', text: 'France' }, { value: 'BE', text: 'Belgique' }]" />
|
|
<MalioSelect v-model="ville" label="Ville" :options="villes" empty-option-label="Choisir..." />
|
|
<MalioSelect v-model="civilite" label="Civilité" :options="civilites" group-class="mt-0" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioSelectCheckbox
|
|
|
|
Liste déroulante multi-sélection avec checkboxes.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `(string \| number)[]` | **requis** | Valeurs sélectionnées (v-model) |
|
|
| `options` | `{ value: string \| number, text: string }[]` | `[]` | Options |
|
|
| `displayTag` | `boolean` | `false` | Afficher les tags sélectionnés |
|
|
| `displaySelectAll` | `boolean` | `false` | Afficher "Tout sélectionner" |
|
|
| `selectAllLabel` | `string` | `'Tout sélectionner'` | Texte du sélecteur global |
|
|
| `label` | `string` | `''` | Label |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
|
|
**Events :** `update:modelValue(value: (string | number)[])`
|
|
|
|
```vue
|
|
<MalioSelectCheckbox v-model="competences" label="Compétences" :options="skills" :display-tag="true" />
|
|
<MalioSelectCheckbox v-model="sites" label="Sites" :options="sitesList" :display-select-all="true" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioCheckbox
|
|
|
|
Case à cocher.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `boolean \| null` | `undefined` | Valeur (v-model) |
|
|
| `label` | `string` | `''` | Label |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
| `readonly` | `boolean` | `false` | Lecture seule |
|
|
| `error` | `string` | `''` | Message d'erreur |
|
|
|
|
**Events :** `update:modelValue(value: boolean)`
|
|
|
|
```vue
|
|
<MalioCheckbox v-model="accepte" label="J'accepte les conditions" />
|
|
<MalioCheckbox v-model="newsletter" label="Newsletter" disabled />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioRadioButton
|
|
|
|
Bouton radio (à utiliser en groupe avec le même `name`).
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `string \| number \| boolean \| null` | `undefined` | Valeur du groupe (v-model) |
|
|
| `value` | `string \| number \| boolean \| null` | `undefined` | Valeur de cette option |
|
|
| `label` | `string` | `''` | Label |
|
|
| `name` | `string` | `''` | Nom du groupe radio |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
| `readonly` | `boolean` | `false` | Lecture seule |
|
|
|
|
**Events :** `update:modelValue(value: string | number | boolean | null)`
|
|
|
|
```vue
|
|
<MalioRadioButton v-model="civilite" name="civilite" value="M" label="Monsieur" />
|
|
<MalioRadioButton v-model="civilite" name="civilite" value="Mme" label="Madame" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioTime
|
|
|
|
Sélecteur d'heure.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `string \| null` | `undefined` | Heure au format HH:mm (v-model) |
|
|
| `label` | `string` | `''` | Label |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
| `readonly` | `boolean` | `false` | Lecture seule |
|
|
| `error` | `string` | `''` | Message d'erreur |
|
|
|
|
**Events :** `update:modelValue(value: string)`
|
|
|
|
```vue
|
|
<MalioTime v-model="heure" label="Heure de début" />
|
|
<MalioTime v-model="fin" label="Heure de fin" readonly />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioButton
|
|
|
|
Bouton d'action avec 4 variantes visuelles et icône optionnelle.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `label` | `string` | `''` | Texte du bouton (ou slot par défaut) |
|
|
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'danger'` | `'primary'` | Variante visuelle |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
| `buttonClass` | `string` | `''` | Classes CSS additionnelles (twMerge) |
|
|
| `iconName` | `string` | `''` | Icône Iconify |
|
|
| `iconPosition` | `'left' \| 'right'` | `'right'` | Position icône |
|
|
| `iconSize` | `string \| number` | `16` | Taille icône |
|
|
|
|
**Events :** `click(e: MouseEvent)`
|
|
**Slots :** `default` (contenu du bouton, remplace `label`)
|
|
|
|
```vue
|
|
<MalioButton label="Valider" />
|
|
<MalioButton label="Modifier" variant="secondary" icon-name="mdi:pencil" icon-position="left" />
|
|
<MalioButton label="Voir plus" variant="tertiary" />
|
|
<MalioButton label="Supprimer" variant="danger" icon-name="mdi:trash" icon-position="left" />
|
|
<MalioButton label="Pleine largeur" button-class="w-full" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioButtonIcon
|
|
|
|
Bouton icône seul (sans texte).
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `icon` | `string` | **requis** | Icône Iconify |
|
|
| `ariaLabel` | `string` | **requis** | Label accessible |
|
|
| `variant` | `'filled' \| 'ghost'` | `'filled'` | Variante visuelle |
|
|
| `disabled` | `boolean` | `false` | Désactivé |
|
|
| `buttonClass` | `string` | `''` | Classes CSS additionnelles |
|
|
| `iconSize` | `string \| number` | `24` | Taille icône |
|
|
|
|
**Events :** `click(e: MouseEvent)`
|
|
|
|
```vue
|
|
<MalioButtonIcon icon="mdi:pencil" aria-label="Modifier" />
|
|
<MalioButtonIcon icon="mdi:trash" aria-label="Supprimer" variant="ghost" />
|
|
```
|
|
|
|
---
|
|
|
|
## MalioTabList
|
|
|
|
Navigation par onglets avec contenu dynamique.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `string` | `undefined` | Onglet actif (v-model) |
|
|
| `tabs` | `{ key: string, label: string, icon?: string }[]` | **requis** | Liste des onglets |
|
|
|
|
**Events :** `update:modelValue(value: string)`
|
|
**Slots :** Un slot nommé par `tab.key` pour le contenu de chaque onglet
|
|
|
|
```vue
|
|
<MalioTabList v-model="activeTab" :tabs="[{ key: 'infos', label: 'Informations' }, { key: 'docs', label: 'Documents', icon: 'mdi:file' }]">
|
|
<template #infos>Contenu infos</template>
|
|
<template #docs>Contenu docs</template>
|
|
</MalioTabList>
|
|
```
|
|
|
|
---
|
|
|
|
## MalioSidebar
|
|
|
|
Barre latérale de navigation rétractable.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `modelValue` | `boolean` | `undefined` | État ouvert/fermé (v-model) |
|
|
| `sections` | `SidebarSection[]` | **requis** | Sections de navigation |
|
|
| `sidebarClass` | `string` | `''` | Classes CSS sidebar |
|
|
| `toggleClass` | `string` | `''` | Classes CSS bouton toggle |
|
|
|
|
**Type SidebarSection :** `{ title?: string, items: { label: string, icon?: string, to?: string, href?: string, active?: boolean }[] }`
|
|
|
|
**Events :** `update:modelValue(value: boolean)`
|
|
**Slots :** `logo` (sidebar ouverte), `logo-collapsed` (sidebar fermée)
|
|
|
|
```vue
|
|
<MalioSidebar v-model="isOpen" :sections="menuSections">
|
|
<template #logo><img src="/logo.png" /></template>
|
|
<template #logo-collapsed><img src="/logo-small.png" /></template>
|
|
</MalioSidebar>
|
|
```
|
|
|
|
---
|
|
|
|
## MalioDrawer
|
|
|
|
Panneau latéral (drawer) qui s'ouvre depuis la droite avec backdrop semi-transparent.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `id` | `string` | auto | Identifiant HTML |
|
|
| `modelValue` | `boolean` | `undefined` | État ouvert/fermé (v-model) |
|
|
| `title` | `string` | `''` | Titre affiché dans le header |
|
|
| `showClose` | `boolean` | `true` | Afficher le bouton de fermeture (croix) |
|
|
| `drawerClass` | `string` | `''` | Classes CSS panneau (twMerge) |
|
|
|
|
**Events :** `update:modelValue(value: boolean)`
|
|
**Slots :** `default` (contenu du drawer)
|
|
|
|
```vue
|
|
<MalioDrawer v-model="isOpen" title="Détails">
|
|
<p>Contenu du drawer</p>
|
|
</MalioDrawer>
|
|
<MalioDrawer v-model="isOpen" title="Sans croix" :show-close="false">
|
|
<p>Fermeture uniquement via backdrop</p>
|
|
</MalioDrawer>
|
|
<MalioDrawer v-model="isOpen" title="Large" drawer-class="max-w-2xl">
|
|
<p>Drawer plus large</p>
|
|
</MalioDrawer>
|
|
```
|
|
|
|
---
|
|
|
|
## MalioDataTable
|
|
|
|
Tableau de données presentational avec pagination, filtres par slots et lignes cliquables.
|
|
|
|
| Prop | Type | Défaut | Description |
|
|
|------|------|--------|-------------|
|
|
| `id` | `string` | auto | Identifiant HTML |
|
|
| `columns` | `{ key: string, label: string }[]` | **requis** | Définition des colonnes |
|
|
| `items` | `Record<string, unknown>[]` | **requis** | Données à afficher |
|
|
| `totalItems` | `number` | **requis** | Total pour la pagination |
|
|
| `page` | `number` | `1` | Page courante (v-model) |
|
|
| `perPage` | `number` | `10` | Lignes par page (v-model) |
|
|
| `perPageOptions` | `number[]` | `[10, 25, 50]` | Options du sélecteur de lignes |
|
|
| `rowClickable` | `boolean` | `true` | Lignes cliquables (cursor pointer + hover) |
|
|
| `tableClass` | `string` | `''` | Classes CSS sur `<table>` (twMerge) |
|
|
| `emptyMessage` | `string` | `'Aucune donnée'` | Message si items vide |
|
|
|
|
**Events :** `update:page(value: number)`, `update:per-page(value: number)`, `row-click(item: Record<string, unknown>)`
|
|
**Slots :** `#header-{key}` (filtre dans le `<th>`, placeholder = label), `#cell-{key}` (contenu du `<td>`), `#empty` (état vide)
|
|
|
|
```vue
|
|
<!-- Avec filtres et pagination -->
|
|
<MalioDataTable
|
|
:columns="[{ key: 'nom', label: 'Nom' }, { key: 'ville', label: 'Ville' }]"
|
|
:items="data"
|
|
:total-items="total"
|
|
v-model:page="page"
|
|
v-model:per-page="perPage"
|
|
@row-click="router.push(`/contact/${$event.id}`)"
|
|
>
|
|
<template #header-nom>
|
|
<input v-model="filtreNom" placeholder="Nom" class="w-full border-0 border-b border-black bg-transparent px-0 py-1 text-sm outline-none">
|
|
</template>
|
|
<template #header-ville>
|
|
<select v-model="filtreVille" class="w-full appearance-none border-0 border-b border-black bg-transparent px-0 py-1 text-sm outline-none">
|
|
<option value="">Ville</option>
|
|
<option v-for="v in villes" :key="v" :value="v">{{ v }}</option>
|
|
</select>
|
|
</template>
|
|
<template #cell-nom="{ item }">
|
|
<strong>{{ item.nom }}</strong>
|
|
</template>
|
|
</MalioDataTable>
|
|
|
|
<!-- Simple sans filtres -->
|
|
<MalioDataTable
|
|
:columns="columns"
|
|
:items="data"
|
|
:total-items="total"
|
|
v-model:page="page"
|
|
v-model:per-page="perPage"
|
|
/>
|
|
```
|