Tema
Detalhe de registro
A tela de um registro só: o que ele é, em que situação está e o que a pessoa pode fazer com ele.
Quando usar
- Ao abrir um item de uma lista: uma obra, um contrato, uma medição.
- Quando o registro tem partes (dados, planilhas, equipe), cada uma ganha uma aba ou um cartão.
- Quando há uma decisão a tomar (aprovar, devolver), ela vai numa coluna lateral, com o resumo.
Componentes
BasePage: o título é o nome do registro (o únicoh1), osubtitledá o contexto e o slotactionsrecebe a situação e as ações.BaseSummaryList: os pares de rótulo e valor. Valor vazio aparece como traço e é lido como "Não informado".BaseCard: cada bloco do detalhe, com título.BaseTabs: as partes do registro, quando são muitas para uma tela só.BasePageAside: a coluna lateral de 360 px para resumo, histórico e decisão.BaseBadge: a situação do registro.BaseBreadcrumb: o caminho de volta à lista, quando o registro fica dois ou mais níveis abaixo do menu.BasePageState: carregando e erro enquanto o registro não chega.
Texto
- Título com o tipo e a identificação: "Contrato 012/2026", "Obra Fórum de Alegre".
- Subtítulo com o contexto que falta: "Obra: Reforma do Fórum de Alegre".
- Rótulos iguais aos do formulário do mesmo registro.
- Ações com verbo e objeto: "Alterar contrato", "Enviar para aprovação".
Faça e não faça
| Faça | Não faça |
|---|---|
| Mostrar a situação com um selo ao lado do título. | Esconder a situação no meio dos dados. |
| Usar os mesmos rótulos do formulário. | Chamar de "Valor global" no detalhe o que o formulário chama de "Valor inicial com BDI". |
| Pôr a decisão no topo da coluna lateral. | Deixar o botão de aprovar no fim de uma página longa. |
Código
vue
<script setup lang="ts">
import { computed, ref } from 'vue'
import { BaseBadge, BaseButton, BaseCard, BasePage, BasePageState, BaseSummaryList, BaseTabs, type SummaryItem } from '@lab-inovacao-tjes/design-system'
const props = defineProps<{ contract: Contract | null; failed: boolean; canEdit: boolean }>()
const emit = defineEmits<{ retry: []; edit: [] }>()
const tab = ref('data')
const tabs = [
{ id: 'data', label: 'Dados' },
{ id: 'sheets', label: 'Planilhas' },
]
const summary = computed<SummaryItem[]>(() => [
{ label: 'Empresa contratada', value: props.contract?.company },
{ label: 'Valor inicial com BDI', value: props.contract?.amountText },
{ label: 'Data do contrato', value: props.contract?.signedOnText },
])
</script>
<template>
<BasePageState v-if="!props.contract" :status="props.failed ? 'error' : 'loading'" loading-label="Carregando o contrato" @retry="emit('retry')" />
<BasePage v-else :title="`Contrato ${props.contract.number}`" :subtitle="`Obra: ${props.contract.workName}`">
<template #actions>
<BaseBadge tone="success">Em execução</BaseBadge>
</template>
<BaseTabs v-model="tab" :tabs="tabs" label="Partes do contrato">
<template #data>
<BaseCard title="Dados do contrato">
<BaseSummaryList :items="summary" layout="inline" />
<BaseButton v-if="props.canEdit" kind="secondary" icon="pencil" @click="emit('edit')">Alterar contrato</BaseButton>
</BaseCard>
</template>
<template #sheets>
<BaseCard title="Planilhas do contrato">…</BaseCard>
</template>
</BaseTabs>
</BasePage>
</template>Na tela de referência
- P19, detalhe do contrato:
ContractDetailFeature.vue, com abas, resumo, selo de situação e o carregamento peloBasePageState. - P19, detalhe da obra:
WorkDetailFeature.vue. - P20: em construção.