reorganização de arquivos
This commit is contained in:
parent
317b0b3b3e
commit
fa1f93aedc
23 changed files with 3 additions and 3 deletions
673
src/componentes/EliTabela/EliTabela.vue
Normal file
673
src/componentes/EliTabela/EliTabela.vue
Normal file
|
|
@ -0,0 +1,673 @@
|
|||
<template>
|
||||
<div class="eli-tabela">
|
||||
<EliTabelaDebug :isDev="isDev" :menuAberto="menuAberto" :menuPopupPos="menuPopupPos" />
|
||||
|
||||
<EliTabelaEstados
|
||||
v-if="carregando || Boolean(erro) || !linhas.length"
|
||||
:carregando="carregando"
|
||||
:erro="erro"
|
||||
:mensagemVazio="tabela.mensagemVazio"
|
||||
/>
|
||||
|
||||
<template v-else>
|
||||
<EliTabelaCabecalho
|
||||
v-if="exibirBusca || temAcoesCabecalho"
|
||||
:exibirBusca="exibirBusca"
|
||||
:valorBusca="valorBusca"
|
||||
:acoesCabecalho="acoesCabecalho"
|
||||
@buscar="atualizarBusca"
|
||||
@colunas="abrirModalColunas"
|
||||
/>
|
||||
|
||||
<EliTabelaModalColunas
|
||||
:aberto="modalColunasAberto"
|
||||
:rotulosColunas="rotulosColunas"
|
||||
:configInicial="configColunas"
|
||||
:colunas="tabela.colunas"
|
||||
@fechar="fecharModalColunas"
|
||||
@salvar="salvarModalColunas"
|
||||
/>
|
||||
|
||||
<table class="eli-tabela__table">
|
||||
<EliTabelaHead
|
||||
:colunas="colunasEfetivas"
|
||||
:temAcoes="temAcoes"
|
||||
:temColunasInvisiveis="temColunasInvisiveis"
|
||||
:colunaOrdenacao="colunaOrdenacao"
|
||||
:direcaoOrdenacao="direcaoOrdenacao"
|
||||
@alternar-ordenacao="alternarOrdenacao"
|
||||
/>
|
||||
|
||||
<EliTabelaBody
|
||||
:colunas="colunasEfetivas"
|
||||
:colunasInvisiveis="colunasInvisiveisEfetivas"
|
||||
:temColunasInvisiveis="temColunasInvisiveis"
|
||||
:linhasExpandidas="linhasExpandidas"
|
||||
:linhas="linhas"
|
||||
:temAcoes="temAcoes"
|
||||
:menuAberto="menuAberto"
|
||||
:possuiAcoes="possuiAcoes"
|
||||
:toggleMenu="toggleMenu"
|
||||
:alternarLinhaExpandida="alternarLinhaExpandida"
|
||||
/>
|
||||
</table>
|
||||
|
||||
<EliTabelaMenuAcoes
|
||||
ref="menuPopup"
|
||||
:menuAberto="menuAberto"
|
||||
:posicao="menuPopupPos"
|
||||
:acoes="menuAberto === null ? [] : acoesDisponiveisPorLinha(menuAberto)"
|
||||
:linha="menuAberto === null ? null : linhas[menuAberto]"
|
||||
@executar="({ acao, linha }) => { menuAberto = null; acao.acao(linha as never); }"
|
||||
/>
|
||||
|
||||
<EliTabelaPaginacao
|
||||
v-if="totalPaginas > 1 && quantidade > 0"
|
||||
:pagina="paginaAtual"
|
||||
:totalPaginas="totalPaginas"
|
||||
:maximoBotoes="tabela.maximo_botoes_paginacao"
|
||||
@alterar="irParaPagina"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
/**
|
||||
* EliTabela
|
||||
* Componente de tabela consultável com busca, paginação, ordenação e ações por linha.
|
||||
*/
|
||||
|
||||
/** Dependências do Vue (Composition API) */
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
PropType,
|
||||
ref,
|
||||
watch,
|
||||
} from "vue";
|
||||
/** Enum de códigos de resposta utilizado na consulta */
|
||||
import { codigosResposta } from "p-respostas";
|
||||
/** Componentes auxiliares */
|
||||
import EliTabelaCabecalho from "./EliTabelaCabecalho.vue";
|
||||
import EliTabelaEstados from "./EliTabelaEstados.vue";
|
||||
import EliTabelaDebug from "./EliTabelaDebug.vue";
|
||||
import EliTabelaHead from "./EliTabelaHead.vue";
|
||||
import EliTabelaBody from "./EliTabelaBody.vue";
|
||||
import EliTabelaMenuAcoes from "./EliTabelaMenuAcoes.vue";
|
||||
import EliTabelaPaginacao from "./EliTabelaPaginacao.vue";
|
||||
import EliTabelaModalColunas from "./EliTabelaModalColunas.vue";
|
||||
import type { EliColuna } from "./types-eli-tabela";
|
||||
/** Tipos da configuração/contrato da tabela */
|
||||
import type { EliTabelaConsulta } from "./types-eli-tabela";
|
||||
import {
|
||||
carregarConfigColunas,
|
||||
salvarConfigColunas,
|
||||
type EliTabelaColunasConfig,
|
||||
} from "./colunasStorage";
|
||||
|
||||
export default defineComponent({
|
||||
name: "EliTabela",
|
||||
inheritAttrs: false,
|
||||
components: {
|
||||
EliTabelaCabecalho,
|
||||
EliTabelaEstados,
|
||||
EliTabelaDebug,
|
||||
EliTabelaHead,
|
||||
EliTabelaBody,
|
||||
EliTabelaMenuAcoes,
|
||||
EliTabelaPaginacao,
|
||||
EliTabelaModalColunas,
|
||||
},
|
||||
props: {
|
||||
/** Configuração principal da tabela (colunas, consulta e ações) */
|
||||
tabela: {
|
||||
type: Object as PropType<EliTabelaConsulta<any>>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
/** Flag para habilitar elementos de debug */
|
||||
const isDev = import.meta.env.DEV;
|
||||
/** Estados de carregamento/erro e dados retornados */
|
||||
const carregando = ref(false);
|
||||
const erro = ref<string | null>(null);
|
||||
const linhas = ref<unknown[]>([]);
|
||||
const quantidade = ref<number>(0);
|
||||
|
||||
/** Controle de visibilidade das ações por linha */
|
||||
const acoesVisiveis = ref<boolean[][]>([]);
|
||||
/** Estado do menu de ações (aberto, elemento e posição) */
|
||||
const menuAberto = ref<number | null>(null);
|
||||
// O componente EliTabelaMenuAcoes expõe `menuEl` (ref do elemento <ul>)
|
||||
const menuPopup = ref<{ menuEl: { value: HTMLElement | null } } | null>(null);
|
||||
const menuPopupPos = ref({ top: 0, left: 0 });
|
||||
|
||||
/** Filtros e ordenação */
|
||||
const valorBusca = ref<string>("");
|
||||
const paginaAtual = ref(1);
|
||||
const colunaOrdenacao = ref<string | null>(null);
|
||||
const direcaoOrdenacao = ref<"asc" | "desc">("asc");
|
||||
|
||||
/** Alias reativo da prop tabela */
|
||||
const tabela = computed(() => props.tabela);
|
||||
|
||||
/** Exibição da busca e ações do cabeçalho */
|
||||
const exibirBusca = computed(() => Boolean(props.tabela.mostrarCaixaDeBusca));
|
||||
const acoesCabecalho = computed(() => props.tabela.acoesTabela ?? []);
|
||||
const temAcoesCabecalho = computed(() => acoesCabecalho.value.length > 0);
|
||||
|
||||
/** Colunas: visibilidade/ordem com persistência */
|
||||
const modalColunasAberto = ref(false);
|
||||
const configColunas = ref<EliTabelaColunasConfig>(
|
||||
carregarConfigColunas(props.tabela.nome)
|
||||
);
|
||||
|
||||
/** Linhas expandidas (para exibir colunas invisíveis) */
|
||||
const linhasExpandidas = ref<Record<number, boolean>>({});
|
||||
|
||||
const rotulosColunas = computed(() => props.tabela.colunas.map((c) => c.rotulo));
|
||||
|
||||
const colunasInvisiveisEfetivas = computed(() => {
|
||||
const colunas = props.tabela.colunas as Array<EliColuna<any>>;
|
||||
|
||||
const configTemDados =
|
||||
(configColunas.value.visiveis?.length ?? 0) > 0 ||
|
||||
(configColunas.value.invisiveis?.length ?? 0) > 0;
|
||||
|
||||
const invisiveisBaseRotulos = configTemDados
|
||||
? configColunas.value.invisiveis ?? []
|
||||
: colunas.filter((c) => c.visivel === false).map((c) => c.rotulo);
|
||||
|
||||
const invisiveisSet = new Set(invisiveisBaseRotulos);
|
||||
const base = colunas.filter((c) => invisiveisSet.has(c.rotulo));
|
||||
|
||||
// ordenação: usa a lista (salva ou derivada do default) e adiciona novas ao final
|
||||
const ordemSalva = invisiveisBaseRotulos;
|
||||
const mapa = new Map<string, EliColuna<any>>();
|
||||
for (const c of base) {
|
||||
if (!mapa.has(c.rotulo)) mapa.set(c.rotulo, c);
|
||||
}
|
||||
|
||||
const ordenadas: Array<EliColuna<any>> = [];
|
||||
for (const r of ordemSalva) {
|
||||
const c = mapa.get(r);
|
||||
if (c) ordenadas.push(c);
|
||||
}
|
||||
for (const c of base) {
|
||||
if (!ordenadas.includes(c)) ordenadas.push(c);
|
||||
}
|
||||
|
||||
return ordenadas;
|
||||
});
|
||||
|
||||
const temColunasInvisiveis = computed(() => colunasInvisiveisEfetivas.value.length > 0);
|
||||
|
||||
const colunasEfetivas = computed(() => {
|
||||
const colunas = props.tabela.colunas;
|
||||
const todosRotulos = rotulosColunas.value;
|
||||
|
||||
const configTemDados =
|
||||
(configColunas.value.visiveis?.length ?? 0) > 0 ||
|
||||
(configColunas.value.invisiveis?.length ?? 0) > 0;
|
||||
|
||||
const invisiveisBaseRotulos = configTemDados
|
||||
? configColunas.value.invisiveis ?? []
|
||||
: (props.tabela.colunas as Array<EliColuna<any>>)
|
||||
.filter((c) => c.visivel === false)
|
||||
.map((c) => c.rotulo);
|
||||
|
||||
const invisiveisSet = new Set(invisiveisBaseRotulos);
|
||||
|
||||
// base visiveis: remove invisiveis
|
||||
const visiveisBaseRotulos = todosRotulos.filter((r) => !invisiveisSet.has(r));
|
||||
const visiveisSet = new Set(visiveisBaseRotulos);
|
||||
|
||||
// aplica ordem salva; novas (sem definicao) entram no fim, respeitando ordem original
|
||||
const ordemSalva = configTemDados ? configColunas.value.visiveis ?? [] : [];
|
||||
const ordemFinal: string[] = [];
|
||||
|
||||
for (const r of ordemSalva) {
|
||||
if (visiveisSet.has(r)) ordemFinal.push(r);
|
||||
}
|
||||
for (const r of visiveisBaseRotulos) {
|
||||
if (!ordemFinal.includes(r)) ordemFinal.push(r);
|
||||
}
|
||||
|
||||
// mapeia rótulo -> coluna, preservando duplicatas (se existirem) pelo primeiro match.
|
||||
// OBS: pressupoe rotulo unico; se repetir, comportamento fica indefinido.
|
||||
const mapa = new Map<string, any>();
|
||||
for (const c of colunas) {
|
||||
if (!mapa.has(c.rotulo)) mapa.set(c.rotulo, c);
|
||||
}
|
||||
|
||||
return ordemFinal.map((r) => mapa.get(r)).filter(Boolean);
|
||||
});
|
||||
|
||||
function abrirModalColunas() {
|
||||
modalColunasAberto.value = true;
|
||||
}
|
||||
|
||||
function fecharModalColunas() {
|
||||
modalColunasAberto.value = false;
|
||||
}
|
||||
|
||||
function salvarModalColunas(cfg: EliTabelaColunasConfig) {
|
||||
configColunas.value = cfg;
|
||||
salvarConfigColunas(props.tabela.nome, cfg);
|
||||
modalColunasAberto.value = false;
|
||||
|
||||
// ao mudar colunas, fecha detalhes expandidos
|
||||
linhasExpandidas.value = {};
|
||||
}
|
||||
|
||||
function alternarLinhaExpandida(indice: number) {
|
||||
const atual = Boolean(linhasExpandidas.value[indice]);
|
||||
linhasExpandidas.value = {
|
||||
...linhasExpandidas.value,
|
||||
[indice]: !atual,
|
||||
};
|
||||
}
|
||||
|
||||
/** Registros por consulta (normaliza para inteiro positivo) */
|
||||
const registrosPorConsulta = computed(() => {
|
||||
const valor = props.tabela.registros_por_consulta;
|
||||
if (typeof valor === "number" && valor > 0) {
|
||||
return Math.floor(valor);
|
||||
}
|
||||
return 10;
|
||||
});
|
||||
|
||||
/** Total de páginas calculado com base na quantidade */
|
||||
const totalPaginas = computed(() => {
|
||||
const limite = registrosPorConsulta.value;
|
||||
if (!limite || limite <= 0) return 1;
|
||||
|
||||
const total = quantidade.value;
|
||||
if (!total) return 1;
|
||||
|
||||
return Math.max(1, Math.ceil(total / limite));
|
||||
});
|
||||
|
||||
/** Indica se existem ações por linha */
|
||||
const temAcoes = computed(() => (props.tabela.acoesLinha ?? []).length > 0);
|
||||
|
||||
/** Sequencial para evitar race conditions entre consultas */
|
||||
let carregamentoSequencial = 0;
|
||||
|
||||
/** Calcula a posição do menu de ações na viewport */
|
||||
function atualizarPosicaoMenu(anchor: HTMLElement) {
|
||||
const rect = anchor.getBoundingClientRect();
|
||||
const gap = 8;
|
||||
|
||||
// Alinha no canto inferior direito do botão.
|
||||
// Se estourar a tela para baixo, abre para cima.
|
||||
const alturaMenu = menuPopup.value?.menuEl?.value?.offsetHeight ?? 0;
|
||||
const larguraMenu = menuPopup.value?.menuEl?.value?.offsetWidth ?? 180;
|
||||
|
||||
let top = rect.bottom + gap;
|
||||
const left = rect.right - larguraMenu;
|
||||
|
||||
if (alturaMenu && top + alturaMenu > window.innerHeight - gap) {
|
||||
top = rect.top - gap - alturaMenu;
|
||||
}
|
||||
|
||||
menuPopupPos.value = {
|
||||
top: Math.max(gap, Math.round(top)),
|
||||
left: Math.max(gap, Math.round(left)),
|
||||
};
|
||||
}
|
||||
|
||||
/** Fecha o menu quando ocorre clique fora */
|
||||
function handleClickFora(evento: MouseEvent) {
|
||||
if (menuAberto.value === null) return;
|
||||
|
||||
const alvo = evento.target as Node;
|
||||
if (menuPopup.value?.menuEl?.value && menuPopup.value.menuEl.value.contains(alvo)) return;
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("[EliTabela] click fora => fechar menu", { menuAberto: menuAberto.value });
|
||||
}
|
||||
|
||||
menuAberto.value = null;
|
||||
}
|
||||
|
||||
/** Alterna ordenação e recarrega os dados */
|
||||
function alternarOrdenacao(chave?: string) {
|
||||
if (!chave) return;
|
||||
|
||||
if (colunaOrdenacao.value === chave) {
|
||||
direcaoOrdenacao.value = direcaoOrdenacao.value === "asc" ? "desc" : "asc";
|
||||
void carregar();
|
||||
return;
|
||||
}
|
||||
|
||||
colunaOrdenacao.value = chave;
|
||||
direcaoOrdenacao.value = "asc";
|
||||
if (paginaAtual.value !== 1) {
|
||||
paginaAtual.value = 1;
|
||||
} else {
|
||||
void carregar();
|
||||
}
|
||||
}
|
||||
|
||||
/** Atualiza a busca e reinicia paginação, se necessário */
|
||||
function atualizarBusca(texto: string) {
|
||||
if (valorBusca.value === texto) return;
|
||||
|
||||
valorBusca.value = texto;
|
||||
if (paginaAtual.value !== 1) {
|
||||
paginaAtual.value = 1;
|
||||
} else {
|
||||
void carregar();
|
||||
}
|
||||
}
|
||||
|
||||
/** Navega para a página solicitada com limites */
|
||||
function irParaPagina(pagina: number) {
|
||||
const alvo = Math.min(Math.max(1, pagina), totalPaginas.value);
|
||||
if (alvo !== paginaAtual.value) {
|
||||
paginaAtual.value = alvo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista ações visíveis por linha, respeitando regras sync/async de `exibir`.
|
||||
*/
|
||||
function acoesDisponiveisPorLinha(i: number) {
|
||||
const acoesLinha = props.tabela.acoesLinha ?? [];
|
||||
const visibilidade = acoesVisiveis.value[i] ?? [];
|
||||
|
||||
return acoesLinha
|
||||
.map((acao, indice) => {
|
||||
const fallbackVisivel =
|
||||
acao.exibir === undefined
|
||||
? true
|
||||
: typeof acao.exibir === "boolean"
|
||||
? acao.exibir
|
||||
: false;
|
||||
|
||||
return {
|
||||
acao,
|
||||
indice,
|
||||
visivel: visibilidade[indice] ?? fallbackVisivel,
|
||||
};
|
||||
})
|
||||
.filter((item) => item.visivel);
|
||||
}
|
||||
|
||||
/** Informa se a linha possui ações disponíveis */
|
||||
function possuiAcoes(i: number) {
|
||||
return acoesDisponiveisPorLinha(i).length > 0;
|
||||
}
|
||||
|
||||
/** Abre/fecha o menu de ações da linha e posiciona o popup */
|
||||
function toggleMenu(i: number, evento?: MouseEvent) {
|
||||
if (!possuiAcoes(i)) return;
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("[EliTabela] toggleMenu (antes)", { i, atual: menuAberto.value });
|
||||
}
|
||||
|
||||
if (menuAberto.value === i) {
|
||||
menuAberto.value = null;
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("[EliTabela] toggleMenu => fechou", { i });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
menuAberto.value = i;
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("[EliTabela] toggleMenu => abriu", { i });
|
||||
}
|
||||
|
||||
// posiciona assim que abrir
|
||||
const anchor = (evento?.currentTarget as HTMLElement | null) ?? null;
|
||||
if (anchor) {
|
||||
// primeiro posicionamento (antes do menu medir)
|
||||
atualizarPosicaoMenu(anchor);
|
||||
// reposiciona no próximo frame para pegar altura real
|
||||
requestAnimationFrame(() => atualizarPosicaoMenu(anchor));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executa consulta, aplica filtros e resolve visibilidade de ações.
|
||||
* Usa o contador sequencial para ignorar respostas antigas.
|
||||
*/
|
||||
async function carregar() {
|
||||
const idCarregamento = ++carregamentoSequencial;
|
||||
carregando.value = true;
|
||||
erro.value = null;
|
||||
acoesVisiveis.value = [];
|
||||
menuAberto.value = null;
|
||||
linhasExpandidas.value = {};
|
||||
|
||||
const limite = Math.max(1, registrosPorConsulta.value);
|
||||
const offset = (paginaAtual.value - 1) * limite;
|
||||
|
||||
const parametrosConsulta: {
|
||||
coluna_ordem?: never;
|
||||
direcao_ordem?: "asc" | "desc";
|
||||
offSet: number;
|
||||
limit: number;
|
||||
texto_busca?: string;
|
||||
} = {
|
||||
offSet: offset,
|
||||
limit: limite,
|
||||
};
|
||||
|
||||
if (valorBusca.value) {
|
||||
parametrosConsulta.texto_busca = valorBusca.value;
|
||||
}
|
||||
|
||||
if (colunaOrdenacao.value) {
|
||||
parametrosConsulta.coluna_ordem = colunaOrdenacao.value as never;
|
||||
parametrosConsulta.direcao_ordem = direcaoOrdenacao.value;
|
||||
}
|
||||
|
||||
try {
|
||||
const tabelaConfig = props.tabela;
|
||||
const res = await tabelaConfig.consulta(parametrosConsulta);
|
||||
|
||||
if (idCarregamento !== carregamentoSequencial) return;
|
||||
|
||||
if (res.cod !== codigosResposta.sucesso) {
|
||||
linhas.value = [];
|
||||
quantidade.value = 0;
|
||||
erro.value = res.mensagem;
|
||||
return;
|
||||
}
|
||||
|
||||
const valores = res.valor?.valores ?? [];
|
||||
const total = res.valor?.quantidade ?? valores.length;
|
||||
|
||||
linhas.value = valores;
|
||||
quantidade.value = total;
|
||||
|
||||
const totalPaginasRecalculado = Math.max(1, Math.ceil((total || 0) / limite));
|
||||
if (paginaAtual.value > totalPaginasRecalculado) {
|
||||
paginaAtual.value = totalPaginasRecalculado;
|
||||
return;
|
||||
}
|
||||
|
||||
const acoesLinhaConfiguradas = tabelaConfig.acoesLinha ?? [];
|
||||
if (!acoesLinhaConfiguradas.length) {
|
||||
acoesVisiveis.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const preResultado = valores.map(() =>
|
||||
acoesLinhaConfiguradas.map((acao) => {
|
||||
if (acao.exibir === undefined) return true;
|
||||
if (typeof acao.exibir === "boolean") return acao.exibir;
|
||||
return false;
|
||||
})
|
||||
);
|
||||
|
||||
acoesVisiveis.value = preResultado;
|
||||
|
||||
const visibilidade = await Promise.all(
|
||||
valores.map(async (linha) =>
|
||||
Promise.all(
|
||||
acoesLinhaConfiguradas.map(async (acao) => {
|
||||
if (acao.exibir === undefined) return true;
|
||||
if (typeof acao.exibir === "boolean") return acao.exibir;
|
||||
|
||||
try {
|
||||
const resultado = acao.exibir(linha as never);
|
||||
return Boolean(await Promise.resolve(resultado));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (idCarregamento === carregamentoSequencial) {
|
||||
acoesVisiveis.value = visibilidade;
|
||||
}
|
||||
} catch (e) {
|
||||
if (idCarregamento !== carregamentoSequencial) return;
|
||||
|
||||
linhas.value = [];
|
||||
quantidade.value = 0;
|
||||
erro.value = e instanceof Error ? e.message : "Erro ao carregar dados.";
|
||||
} finally {
|
||||
if (idCarregamento === carregamentoSequencial) {
|
||||
carregando.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Ciclo de vida: registra listener global e carrega dados iniciais */
|
||||
onMounted(() => {
|
||||
document.addEventListener("click", handleClickFora);
|
||||
void carregar();
|
||||
});
|
||||
|
||||
/** Ciclo de vida: remove listener global */
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", handleClickFora);
|
||||
});
|
||||
|
||||
/** Watch: ao desabilitar busca, limpa termo e recarrega */
|
||||
watch(
|
||||
() => props.tabela.mostrarCaixaDeBusca,
|
||||
(mostrar) => {
|
||||
if (!mostrar && valorBusca.value) {
|
||||
valorBusca.value = "";
|
||||
if (paginaAtual.value !== 1) {
|
||||
paginaAtual.value = 1;
|
||||
} else {
|
||||
void carregar();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/** Watch: mudança de página dispara nova consulta */
|
||||
watch(paginaAtual, (nova, antiga) => {
|
||||
if (nova !== antiga) void carregar();
|
||||
});
|
||||
|
||||
/** Watch: troca de configuração reseta estados e recarrega */
|
||||
watch(
|
||||
() => props.tabela,
|
||||
() => {
|
||||
menuAberto.value = null;
|
||||
colunaOrdenacao.value = null;
|
||||
direcaoOrdenacao.value = "asc";
|
||||
valorBusca.value = "";
|
||||
modalColunasAberto.value = false;
|
||||
configColunas.value = carregarConfigColunas(props.tabela.nome);
|
||||
linhasExpandidas.value = {};
|
||||
if (paginaAtual.value !== 1) {
|
||||
paginaAtual.value = 1;
|
||||
} else {
|
||||
void carregar();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/** Watch: alteração do limite de registros reinicia paginação */
|
||||
watch(
|
||||
() => props.tabela.registros_por_consulta,
|
||||
() => {
|
||||
if (paginaAtual.value !== 1) {
|
||||
paginaAtual.value = 1;
|
||||
} else {
|
||||
void carregar();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/** Watch: mudança nas linhas fecha o menu aberto */
|
||||
watch(linhas, () => {
|
||||
menuAberto.value = null;
|
||||
linhasExpandidas.value = {};
|
||||
});
|
||||
|
||||
/** Exposição para o template (state, computed, helpers e actions) */
|
||||
return {
|
||||
// state
|
||||
isDev,
|
||||
tabela,
|
||||
carregando,
|
||||
erro,
|
||||
linhas,
|
||||
quantidade,
|
||||
menuAberto,
|
||||
valorBusca,
|
||||
paginaAtual,
|
||||
colunaOrdenacao,
|
||||
direcaoOrdenacao,
|
||||
totalPaginas,
|
||||
|
||||
// computed
|
||||
exibirBusca,
|
||||
acoesCabecalho,
|
||||
temAcoesCabecalho,
|
||||
temAcoes,
|
||||
colunasEfetivas,
|
||||
rotulosColunas,
|
||||
modalColunasAberto,
|
||||
configColunas,
|
||||
|
||||
temColunasInvisiveis,
|
||||
colunasInvisiveisEfetivas,
|
||||
linhasExpandidas,
|
||||
|
||||
abrirModalColunas,
|
||||
fecharModalColunas,
|
||||
salvarModalColunas,
|
||||
|
||||
alternarLinhaExpandida,
|
||||
|
||||
// actions
|
||||
alternarOrdenacao,
|
||||
atualizarBusca,
|
||||
irParaPagina,
|
||||
acoesDisponiveisPorLinha,
|
||||
possuiAcoes,
|
||||
toggleMenu,
|
||||
|
||||
// popup
|
||||
menuPopup,
|
||||
menuPopupPos,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style src="./EliTabela.css"></style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue