Skip to content

CrudLayout

Visão geral

O componente LxCrudLayout fornece uma estrutura completa para páginas de listagem e cadastro (CRUD). Ele encapsula o cabeçalho com título e área de ações, uma toolbar com campo de busca e botão de cadastrar, gerenciamento de estado vazio e de carregamento (skeleton), e uma área de extras (modais, sidebars) integrada.

Todos os blocos estruturais possuem slots de override total: ao fornecer o slot correspondente (header, content, toolbar, extra), o conteúdo padrão é completamente substituído pelo fornecido.

Usuários

Gerencie os usuários do sistema.

Clique para visualizar o código
vue
<script setup lang="ts">
import { ref, computed } from 'vue';
import { LxCrudLayout, LxDataTable, LxDataTableColumn, LxInputText, LxComboBox, LxModal } from '@lde/lxcomponents';
import type { User, RoleOption, FormData } from './types';

const loading = ref(false);
const modalLoading = ref(false);
const searchQuery = ref('');

const allUsers = ref<User[]>([
	{ id: 1, name: 'Ana Souza', email: 'ana.souza@exemplo.com', role: 'Administrador' },
	{ id: 2, name: 'Bruno Costa', email: 'bruno.costa@exemplo.com', role: 'Editor' },
	{ id: 3, name: 'Carla Mendes', email: 'carla.mendes@exemplo.com', role: 'Visualizador' }
]);

const filteredUsers = computed(() => {
	if (!searchQuery.value) return allUsers.value;
	const lower = searchQuery.value.toLowerCase();
	return allUsers.value.filter((u) => u.name.toLowerCase().includes(lower) || u.email.toLowerCase().includes(lower) || u.role.toLowerCase().includes(lower));
});

const isEmpty = computed(() => filteredUsers.value.length === 0);

const roleOptions = ref<RoleOption[]>([{ label: 'Administrador' }, { label: 'Editor' }, { label: 'Visualizador' }]);

const form = ref<FormData>({ name: '', email: '', role: { label: 'Visualizador' } });

const simulateLoading = () => {
	loading.value = true;
	setTimeout(() => {
		loading.value = false;
	}, 2000);
};

const handleSearch = (value: string) => {
	searchQuery.value = value;
};

const handleModalConfirm = (closeModal: () => void) => {
	modalLoading.value = true;
	setTimeout(() => {
		if (form.value.name && form.value.email) {
			allUsers.value.push({ id: Date.now(), name: form.value.name, email: form.value.email, role: form.value.role?.label ?? '' });
		}
		form.value = { name: '', email: '', role: { label: 'Visualizador' } };
		modalLoading.value = false;
		closeModal();
	}, 1000);
};
</script>

<template>
	<LxCrudLayout
		id="example-crud"
		title="Usuários"
		description="Gerencie os usuários do sistema."
		add-label="Novo usuário"
		:loading="loading"
		:is-empty="isEmpty"
		:search-config="{ placeholder: 'Buscar por nome, e-mail ou papel...' }"
		:empty-state-config="{
			icon: 'fas fa-users',
			title: 'Nenhum usuário encontrado.',
			description: 'Cadastre o primeiro usuário ou revise o filtro de busca.'
		}"
		@search="handleSearch"
	>
		<template #header-actions>
			<button class="btn btn-secondary btn-sm" @click="simulateLoading">Simular carregamento</button>
		</template>

		<template #table>
			<LxDataTable id="users-table" :value="filteredUsers" data-key="id">
				<LxDataTableColumn field="name" header="Nome" />
				<LxDataTableColumn field="email" header="E-mail" />
				<LxDataTableColumn field="role" header="Papel" />
			</LxDataTable>
		</template>

		<template #modal="{ modalVisible, closeModal }">
			<LxModal
				v-if="modalVisible"
				title="Novo usuário"
				:loading="modalLoading"
				:btn-confirm="{ label: 'Salvar usuário' }"
				@close="closeModal"
				@confirm="handleModalConfirm(closeModal)"
			>
				<template #content>
					<LxInputText id="form-name" v-model="form.name" label="Nome" placeholder="Nome completo" />
					<LxInputText id="form-email" v-model="form.email" label="E-mail" placeholder="email@exemplo.com" />
					<LxComboBox id="form-role" v-model="form.role" label="Papel" :options="roleOptions" options-label="label" open-direction="top" />
				</template>
			</LxModal>
		</template>
	</LxCrudLayout>
</template>

Propriedades

NomeDescriçãoTipoPadrão
idID raiz do componenteString'lx-crud-layout'
titleTítulo exibido no cabeçalho. Obrigatório.String
descriptionDescrição exibida abaixo do títuloStringundefined
docsUrlURL da documentação — quando informado, exibe um botão de atalho para a documentaçãoStringundefined
loadingIndica estado de carregamento; exibe o skeleton LxGridLoadingBooleanfalse
addLabelRótulo do botão de adicionar na toolbar e no estado vazioString'Cadastrar'
addIconÍcone FontAwesome do botão de adicionarString'fas fa-plus'
showAddButtonExibe ou oculta o botão de adicionar na toolbar e no estado vazioBooleantrue
isEmptyQuando true, exibe o estado vazio em lugar da tabelaBooleanfalse
searchConfigConfiguração do campo de busca. Ver SearchConfigSearchConfig{}
emptyStateConfigConfiguração do estado vazio. Ver EmptyStateConfigEmptyStateConfig{}

SearchConfig

NomeDescriçãoTipoPadrão
placeholderPlaceholder do input de buscaString'Buscar...'
labelRótulo (label) do input de buscaString''
showExibe ou oculta o campo de buscaBooleantrue

EmptyStateConfig

NomeDescriçãoTipoPadrão
iconClasse do ícone FontAwesomeString'fas fa-inbox'
titleTítulo exibido no estado vazioString'Nenhum registro encontrado.'
descriptionDescrição exibida abaixo do título no estado vazioStringundefined
buttonLabelRótulo do botão de CTA no estado vazio. Padrão: igual ao addLabelStringundefined
showButtonExibe ou oculta o botão de CTA no estado vazioBooleantrue

Eventos

NomeDescriçãoPayload
add:clickEmitido quando o botão de adicionar é clicado
searchEmitido quando o usuário realiza uma buscastring

Slots

Slots de override total

Quando fornecidos, substituem completamente o bloco padrão correspondente do LxBaseLayout.

NomeEscopoDescrição
headerSubstitui todo o cabeçalho padrão (título + ações)
contentSubstitui toda a área de conteúdo (toolbar + body)
footerSubstitui o rodapé padrão. Só é renderizado quando o slot é fornecido
extraSubstitui a área de extras (modais, sidebars). Fallback: slot modal

Slots de composição interna

Funcionam quando os slots de override acima não são fornecidos, permitindo customização pontual dentro do layout padrão.

NomeEscopoDescrição
header-actionsAções extras à direita do cabeçalho, ao lado do botão de docs
toolbarOverride total da toolbar; substitui busca + botão adicionar quando fornecido
toolbar-startConteúdo inserido no início da toolbar, antes do campo de busca
toolbar-actionsAções extras no final da toolbar, após o botão de adicionar
table{ loading }Slot principal para a tabela ou lista de registros
empty-stateSubstitui completamente o estado vazio padrão
modal{ modalVisible: boolean, openModal: () => void, closeModal: () => void }Área de extras para modais. Usa v-if="modalVisible" para exibir a modal e chama closeModal() para fechá-la. O estado é controlado internamente — aberto ao clicar em "Cadastrar" e fechado via closeModal.

Classes Padrão

ClasseDescrição
lx-crud-layoutClasse raiz do componente
lx-crud-layout__headerContêiner do cabeçalho (título + ações)
lx-crud-layout__header-infoÁrea de título e descrição
lx-crud-layout__header-actionsÁrea de ações do cabeçalho
lx-crud-layout__titleElemento <h1> do título
lx-crud-layout__descriptionParágrafo de descrição
lx-crud-layout__toolbarContêiner da toolbar (busca + botão adicionar)
lx-crud-layout__searchElemento do campo de busca
lx-crud-layout__contentÁrea de conteúdo principal (tabela / skeleton / estado vazio)
lx-crud-layout__content--emptyModificador aplicado à área de conteúdo quando isEmpty é true
lx-crud-layout__empty-stateContêiner do estado vazio padrão
lx-crud-layout__empty-iconÍcone do estado vazio
lx-crud-layout__empty-titleTítulo do estado vazio
lx-crud-layout__empty-descriptionDescrição do estado vazio
lx-crud-layout__footerContêiner do rodapé

Desenvolvido pelo time Linx Microvix