vue-componentes/src/playground/tabela.playground.vue

434 lines
12 KiB
Vue

<template>
<section class="stack">
<h2>EliTabela</h2>
<!-- Exemplo: botão de ação "Novo" no cabeçalho que adiciona uma linha -->
<div style="margin-bottom: 12px">
<strong>Selecionados:</strong> {{ selecionados.map(s => s.empreendedor).join(', ') || 'Nenhum' }}
</div>
<EliTabela :tabela="tabelaOk" :key="versaoTabelaOk" v-model:selecionados="selecionados" />
<EliTabela :tabela="tabelaVazia" />
<EliTabela :tabela="tabelaErro" />
<!-- Modal de simulação de cadastro -->
<v-dialog v-model="mostrarModalCadastro" max-width="500px">
<v-card>
<v-card-title>Novo Cadastro</v-card-title>
<v-card-text>
<v-alert
v-if="erroCadastro"
type="error"
variant="tonal"
density="compact"
class="mb-4"
>
{{ erroCadastro }}
</v-alert>
<v-text-field
v-model="novoCadastro.empreendedor"
label="Empreendedor"
variant="outlined"
density="compact"
class="mb-2"
></v-text-field>
<v-text-field
v-model="novoCadastro.empreendimento"
label="Empreendimento"
variant="outlined"
density="compact"
></v-text-field>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="grey-darken-1"
variant="text"
@click="mostrarModalCadastro = false"
:disabled="salvando"
>
Cancelar
</v-btn>
<v-btn
color="primary"
variant="elevated"
@click="salvarNovoCadastro"
:loading="salvando"
>
Salvar
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</section>
</template>
<script lang="ts">
import { BadgeCheck, Eye, Pencil, Plus, Trash2 } from "@lucide/vue"
import { criarFiltro26, type tipoFiltro26 } from "p-comuns"
import { codigosResposta } from "p-respostas"
import { defineComponent, ref } from "vue"
import type { ComponenteEntrada } from "@/componentes/EliEntrada/tiposEntradas"
import type { tipoEliTabelaConsulta } from "@/componentes/EliTabela"
import { celulaTabela, EliTabela } from "@/componentes/EliTabela"
import { api_criar, api_deletar, api_ler, type Linha } from "./api_simulador"
export default defineComponent({
name: "TabelaPlayground",
components: { EliTabela },
setup() {
const acoesLinha: tipoEliTabelaConsulta<Linha>["acoesLinha"] = [
{
icone: Eye,
cor: "#2563eb",
rotulo: "Detalhes",
acao: (linha) => {
console.log("Visualizar detalhes de", linha.empreendedor)
},
},
{
icone: Trash2,
cor: "#dc2626",
rotulo: "Remover",
acao: (linha) => {
removerCadastro(linha)
},
exibir: (linha) => !linha.empreendimento.toLowerCase().includes("café"),
},
]
const versaoTabelaOk = ref(0)
const selecionados = ref<Linha[]>([])
const mostrarModalCadastro = ref(false)
const salvando = ref(false)
const novoCadastro = ref({
empreendedor: "",
empreendimento: "",
documento: "",
email: "",
telefone: "",
faturamento: 0,
peso_kg: 0,
})
const erroCadastro = ref<string | undefined>(undefined)
// Armazena a função de atualização da tabela atual (vinda da ação do cabeçalho)
let atualizarTabelaCallback: (() => Promise<void>) | undefined
// A ação do cabeçalho recebe { atualizarConsulta, editarLista, ... }
// biome-ignore lint/suspicious/noExplicitAny: playground
function abrirModalCadastro(params?: any) {
if (params?.atualizarConsulta) {
atualizarTabelaCallback = params.atualizarConsulta
}
novoCadastro.value = {
empreendedor: "",
empreendimento: "",
documento: "",
email: "",
telefone: "",
faturamento: 0,
peso_kg: 0,
}
erroCadastro.value = undefined
mostrarModalCadastro.value = true
}
async function salvarNovoCadastro() {
salvando.value = true
erroCadastro.value = undefined
try {
const resposta = await api_criar(novoCadastro.value)
if (resposta.eErro) {
erroCadastro.value = resposta.mensagem
return
}
// PADRÃO RECOMENDADO:
// Se temos o callback de atualização, usamos ele para recarregar os dados na tabela
// sem precisar remontar o componente (sem alterar key).
if (atualizarTabelaCallback) {
await atualizarTabelaCallback()
} else {
// Fallback (caso não tenha vindo pela ação da tabela): força re-render
versaoTabelaOk.value++
}
mostrarModalCadastro.value = false
} catch (e) {
erroCadastro.value = String(e)
} finally {
salvando.value = false
}
}
function removerCadastro(linha: Linha) {
// Exemplo de uso do api_deletar
// Não temos um ID único confiável além das propriedades, vamos usar empreendedor como chave neste exemplo simples
if (confirm(`Tem certeza que deseja remover ${linha.empreendedor}?`)) {
api_deletar("empreendedor", linha.empreendedor).then(() => {
versaoTabelaOk.value++
})
}
}
const tabelaOk: tipoEliTabelaConsulta<Linha> = {
nome: "ExemploPlaygroundData",
registros_por_consulta: 10,
mostrarCaixaDeBusca: true,
mostrar_selecao: true,
chave_selecao: "empreendedor",
acoesTabela: [
{
posicao: "superior",
icone: Plus,
cor: "#16a34a",
rotulo: "Novo (Superior)",
acao: abrirModalCadastro,
},
{
posicao: "inferior",
icone: Plus,
cor: "#2563eb",
rotulo: "Novo (Inferior)",
acao: abrirModalCadastro,
},
],
colunas: [
{
rotulo: "Empreendedor",
celula: (l) =>
celulaTabela("textoSimples", { texto: l.empreendedor }),
coluna_ordem: "empreendedor",
visivel: true,
},
{
rotulo: "Empreendimento",
celula: (l) =>
celulaTabela("textoSimples", { texto: l.empreendimento }),
coluna_ordem: "empreendimento",
visivel: true,
},
{
rotulo: "Documento",
celula: (l) => celulaTabela("textoSimples", { texto: l.documento }),
coluna_ordem: "documento",
visivel: false,
},
{
rotulo: "E-mail",
celula: (l) =>
celulaTabela("textoTruncado", {
texto: l.email,
acao: () => {
// Exemplo de ação: poderia abrir detalhes
alert(`Clicou em ${l.email}`)
},
}),
coluna_ordem: "email",
visivel: true,
},
{
rotulo: "Telefone",
celula: (l) => celulaTabela("textoSimples", { texto: l.telefone }),
coluna_ordem: "telefone",
visivel: true,
},
{
rotulo: "Faturamento",
celula: (l) =>
celulaTabela("numero", {
numero: l.faturamento,
prefixo: "R$",
// Exemplo de ação (clicável)
acao: () =>
alert(`Faturamento de ${l.empreendedor}: R$ ${l.faturamento}`),
}),
visivel: true,
},
{
rotulo: "Peso",
celula: (l) =>
celulaTabela("numero", {
numero: l.peso_kg,
sufixo: "kg",
}),
visivel: false,
},
{
rotulo: "Status",
celula: (l) =>
celulaTabela("tags", {
opcoes: [
{
rotulo: "Ativo",
cor: "success",
icone: BadgeCheck,
},
{
rotulo: "Editar",
cor: "primary",
icone: Pencil,
acao: () => {
alert(`Editar ${l.empreendedor}`)
},
},
],
}),
visivel: true,
},
{
rotulo: "Criado em",
celula: (l) =>
celulaTabela("data", {
valor: l.criado_em,
formato: "data",
}),
visivel: false,
},
{
rotulo: "Atualizado",
celula: (l) =>
celulaTabela("data", {
valor: l.atualizado_em,
formato: "relativo",
acao: () => alert(`Atualizado em: ${l.atualizado_em}`),
}),
visivel: true,
},
],
acoesLinha: acoesLinha,
filtroAvancado: [
{
chave: "empreendedor",
rotulo: "Empreendedor",
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
filtro: (valor: unknown) =>
criarFiltro26({
// biome-ignore lint/suspicious/noExplicitAny: playground
empreendedor: { like: valor as string } as any,
}) as tipoFiltro26<Linha>,
entrada: ["texto", { rotulo: "Empreendedor" }] as ComponenteEntrada,
},
{
chave: "documento",
rotulo: "Documento",
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
filtro: (valor: string) =>
criarFiltro26({
// biome-ignore lint/suspicious/noExplicitAny: playground
documento: { like: valor as string } as any,
}) as tipoFiltro26<Linha>,
entrada: [
"texto",
{ rotulo: "Documento", formato: "cpfCnpj" },
] as ComponenteEntrada,
},
{
chave: "email",
rotulo: "E-mail",
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
filtro: (valor: string) =>
criarFiltro26({
// biome-ignore lint/suspicious/noExplicitAny: playground
email: { like: valor as string } as any,
}) as tipoFiltro26<Linha>,
entrada: [
"texto",
{ rotulo: "E-mail", formato: "email" },
] as ComponenteEntrada,
},
],
consulta: async (parametrosConsulta) => {
try {
const resposta = await api_ler(parametrosConsulta)
return {
cod: codigosResposta.sucesso,
eCerto: true,
eErro: false,
mensagem: undefined,
valor: {
quantidade: resposta.total,
valores: resposta.dados,
},
}
} catch (error) {
return {
cod: codigosResposta.erroConhecido,
eCerto: false,
eErro: true,
mensagem: String(error),
valor: undefined,
}
}
},
}
const tabelaVazia: tipoEliTabelaConsulta<Linha> = {
nome: "Exemplo Vazio",
registros_por_consulta: 10,
mostrarCaixaDeBusca: true,
colunas: tabelaOk.colunas,
consulta: async (_parametrosConsulta) => {
return {
cod: codigosResposta.sucesso,
eCerto: true,
eErro: false,
mensagem: undefined,
valor: {
quantidade: 0,
valores: [],
},
}
},
mensagemVazio: "Nada para mostrar aqui.",
acoesLinha: acoesLinha,
}
const tabelaErro: tipoEliTabelaConsulta<Linha> = {
nome: "Exemplo",
registros_por_consulta: tabelaOk.registros_por_consulta,
mostrarCaixaDeBusca: tabelaOk.mostrarCaixaDeBusca,
colunas: tabelaOk.colunas,
acoesLinha: acoesLinha,
consulta: async (_parametrosConsulta) => {
return {
cod: codigosResposta.erroConhecido,
eCerto: false,
eErro: true,
mensagem: "Falha ao buscar dados",
valor: undefined,
}
},
}
return {
tabelaOk,
tabelaVazia,
tabelaErro,
versaoTabelaOk,
mostrarModalCadastro,
salvando,
novoCadastro,
salvarNovoCadastro,
erroCadastro,
selecionados,
}
},
})
</script>
<style scoped>
.stack {
display: grid;
gap: 16px;
}
.stack :deep(.eli-tabela) {
max-width: 900px;
}
</style>