Skip to content

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 slot toolbar recebe busca e filtros; #cell-<key> formata cada coluna.
  • BaseSearch: a busca da lista, que emite search depois que a pessoa para de digitar.
  • BaseSegmented ou BaseSelect: filtros por situação ou categoria.
  • BasePagination: páginas e itens por página, quando a API pagina.
  • BaseBadge no slot da situação: selo com ponto e texto, nunca só a cor.
  • BaseLink na coluna principal: leva ao detalhe do registro.

Texto ​

  • O label da 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çaNã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.