Tema
Listas e tabelas
Uma lista mostra muitos registros do mesmo tipo e deixa a pessoa achar o que procura: buscar, filtrar, ordenar e passar de página.
Quando usar
- A tela principal de um módulo: obras, contratos, medições.
- Uma parte de um detalhe com vários itens: as planilhas de um contrato, a equipe.
Para poucos pares de rótulo e valor de um registro só, use o padrão Detalhe de registro.
Componentes
BaseDataTable: colunas, ordenação, seleção, estado vazio e carregando. No celular vira cartões. O slottoolbarrecebe busca e filtros;#cell-<key>formata cada coluna.BaseSearch: a busca da lista, que emitesearchdepois que a pessoa para de digitar.BaseSegmentedouBaseSelect: filtros por situação ou categoria.BasePagination: páginas e itens por página, quando a API pagina.BaseBadgeno slot da situação: selo com ponto e texto, nunca só a cor.BaseLinkna coluna principal: leva ao detalhe do registro.
Texto
- O
labelda tabela diz o que ela lista: "Obras", "Medições do contrato 012/2026". - O rótulo da busca diz o que se busca; o placeholder, por onde: "Buscar obras" e "Buscar por nome ou comarca".
- Cabeçalhos curtos, sem abreviação que só a equipe entende: "Comarca", "Unidade responsável".
- Estado vazio e busca sem resultado como no padrão Vazio.
Faça e não faça
| Faça | Não faça |
|---|---|
| Fazer do nome do registro um link para o detalhe. | Um botão "Ver" em cada linha, além do nome. |
| Voltar para a página 1 quando a busca ou o filtro muda. | Manter a pessoa na página 5 de uma busca que tem 2 páginas. |
Formatar dinheiro e data no #cell-<key>. | Mandar a data da API (2026-10-28) direto para a célula. |
Código
vue
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { BaseBadge, BaseDataTable, BaseLink, BasePagination, BaseSearch, type DataTableColumn } from '@lab-inovacao-tjes/design-system'
const columns: DataTableColumn[] = [
{ key: 'name', label: 'Obra', sortable: true },
{ key: 'district', label: 'Comarca', width: '200px' },
{ key: 'status', label: 'Situação', width: '140px' },
]
const works = ref<Work[]>([])
const total = ref(0)
const page = ref(1)
const pageSize = ref(20)
const query = ref('')
const loading = ref(false)
async function load(): Promise<void> {
loading.value = true
try {
const result = await workService.search(query.value, page.value, pageSize.value)
works.value = result.items
total.value = result.total
} finally {
loading.value = false
}
}
function search(text: string): void {
query.value = text
page.value = 1
void load()
}
onMounted(load)
</script>
<template>
<BaseDataTable :columns="columns" :rows="works" row-key="id" label="Obras" :loading="loading" empty-title="Nenhuma obra cadastrada">
<template #toolbar>
<BaseSearch label="Buscar obras" placeholder="Buscar por nome ou comarca" @search="search" />
</template>
<template #cell-name="{ row }">
<BaseLink :href="`/works/${row.id}`">{{ row.name }}</BaseLink>
</template>
<template #cell-status="{ row }">
<BaseBadge :tone="row.status === 'in_progress' ? 'success' : 'neutral'">{{ STATUS_LABELS[row.status] }}</BaseBadge>
</template>
</BaseDataTable>
<BasePagination
v-if="total > 0"
v-model:page="page"
v-model:page-size="pageSize"
:total="total"
label="Páginas de obras"
@update:page="load"
@update:page-size="() => { page = 1; load() }"
/>
</template>Na tela de referência
- P19, lista de obras:
WorkListFeature.vue, com busca, paginação pela API, estado vazio e o nome como link para o detalhe. - P20: em construção.