@jtandrelevicius/utils-js-library 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +260 -194
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,61 +1,102 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class DataQueryService {
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class ServicoDados {
|
|
4
|
+
|
|
6
5
|
/**
|
|
7
|
-
* Define o executor de query padrão para a classe.
|
|
8
|
-
*
|
|
9
|
-
* @param {Function} executorFunction Função que aceita (query, params, onSuccess, onError).
|
|
6
|
+
* Define o executor de query padrão para a classe (Legado).
|
|
7
|
+
* @param {Function} funcaoExecutor Função que aceita (query, params, onSuccess, onError).
|
|
10
8
|
*/
|
|
11
|
-
static
|
|
12
|
-
this.executor =
|
|
9
|
+
static definirExecutor(funcaoExecutor) {
|
|
10
|
+
this.executor = funcaoExecutor;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Realiza requisições do tipo POST (Base para salvar/excluir).
|
|
15
|
+
* @param {string} url URL da requisição.
|
|
16
|
+
* @param {Object} corpo Corpo da requisição.
|
|
17
|
+
* @param {Object} opcoes Opções adicionais (headers, raw).
|
|
18
|
+
* @returns {Promise<Object>} Resposta da requisição.
|
|
19
|
+
*/
|
|
20
|
+
static async post(url, corpo, { headers, raw } = { headers: {}, raw: false }) {
|
|
21
|
+
let isJSON = true;
|
|
22
|
+
|
|
23
|
+
if (headers) {
|
|
24
|
+
const cabecalhoTipoOriginal = headers['Content-Type'] ? String(headers['Content-Type']) : 'application/json; charset=UTF-8';
|
|
25
|
+
isJSON = headers['Content-Type'] ? RegExp(/json/i).exec(headers['Content-Type']) : isJSON;
|
|
26
|
+
|
|
27
|
+
if (headers['Content-Type']) delete headers['Content-Type'];
|
|
28
|
+
headers['Content-Type'] = cabecalhoTipoOriginal;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
let corpoRequisicaoFormatado = corpo;
|
|
33
|
+
|
|
34
|
+
if (corpo && typeof corpo === 'object') {
|
|
35
|
+
corpoRequisicaoFormatado = JSON.stringify(corpo);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (typeof window === 'undefined' || !window.fetch) {
|
|
39
|
+
throw new Error("O método 'post' requer um ambiente de navegador com 'fetch' disponível.");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const resposta = await window.fetch.bind(window)(url, {
|
|
43
|
+
headers,
|
|
44
|
+
method: 'POST',
|
|
45
|
+
redirect: 'follow',
|
|
46
|
+
credentials: 'include',
|
|
47
|
+
body: corpoRequisicaoFormatado
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (raw) {
|
|
51
|
+
return resposta;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return isJSON ? resposta.json() : resposta.text();
|
|
55
|
+
|
|
56
|
+
} catch (erro) {
|
|
57
|
+
console.error("[ServicoDados] Erro no POST:", erro);
|
|
58
|
+
throw erro;
|
|
59
|
+
}
|
|
13
60
|
}
|
|
14
61
|
|
|
15
62
|
/**
|
|
63
|
+
* Normaliza a resposta da API (Método auxiliar).
|
|
16
64
|
* @private
|
|
17
|
-
* Normaliza a resposta da API, lidando com estruturas aninhadas legadas/específicas.
|
|
18
|
-
* @param {unknown} rawValue O valor bruto retornado pela query.
|
|
19
|
-
* @returns {Array<Object>} O array de dados normalizado.
|
|
20
65
|
*/
|
|
21
|
-
static
|
|
66
|
+
static normalizarResposta(valorBruto) {
|
|
22
67
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (typeof
|
|
34
|
-
|
|
35
|
-
} else if (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const body = parsedValue.data?.responseBody || parsedValue.responseBody;
|
|
39
|
-
finalData = typeof body === 'string' ? JSON.parse(body) : body;
|
|
68
|
+
const valorParseado = typeof valorBruto === 'string' ? JSON.parse(valorBruto) : valorBruto;
|
|
69
|
+
|
|
70
|
+
if (!valorParseado) return [];
|
|
71
|
+
if (Array.isArray(valorParseado)) return valorParseado;
|
|
72
|
+
|
|
73
|
+
let dadosFinais = null;
|
|
74
|
+
|
|
75
|
+
if (typeof valorParseado === 'object') {
|
|
76
|
+
if (typeof valorParseado.b === 'string') {
|
|
77
|
+
dadosFinais = JSON.parse(valorParseado.b);
|
|
78
|
+
} else if (valorParseado.c?.b && typeof valorParseado.c.b === 'string') {
|
|
79
|
+
dadosFinais = JSON.parse(valorParseado.c.b);
|
|
80
|
+
} else if (valorParseado.data?.responseBody || valorParseado.responseBody) {
|
|
81
|
+
const corpo = valorParseado.data?.responseBody || valorParseado.responseBody;
|
|
82
|
+
dadosFinais = typeof corpo === 'string' ? JSON.parse(corpo) : corpo;
|
|
40
83
|
}
|
|
41
84
|
}
|
|
42
85
|
|
|
43
|
-
if (Array.isArray(
|
|
44
|
-
|
|
86
|
+
if (Array.isArray(dadosFinais)) return dadosFinais;
|
|
45
87
|
throw new Error("Formato de resposta desconhecido ou inválido.");
|
|
46
|
-
} catch (
|
|
47
|
-
throw new Error(`Falha ao normalizar resposta da query: ${
|
|
88
|
+
} catch (erro) {
|
|
89
|
+
throw new Error(`Falha ao normalizar resposta da query: ${erro.message}`);
|
|
48
90
|
}
|
|
49
91
|
}
|
|
50
92
|
|
|
51
93
|
/**
|
|
52
|
-
* Executa uma consulta de dados.
|
|
53
|
-
* @param {string} query A string da consulta
|
|
54
|
-
* @param {Object} [params=null] Parâmetros opcionais
|
|
94
|
+
* Executa uma consulta de dados (Via executor configurado ou executeQuery global).
|
|
95
|
+
* @param {string} query A string da consulta.
|
|
96
|
+
* @param {Object} [params=null] Parâmetros opcionais.
|
|
55
97
|
* @returns {Promise<Array<Object>>} Promise resolvendo com os dados.
|
|
56
98
|
*/
|
|
57
|
-
static async
|
|
58
|
-
// Tenta usar o executor configurado ou busca o global executeQuery como fallback
|
|
99
|
+
static async consultar(query, params = null) {
|
|
59
100
|
const executor = this.executor || (typeof window !== 'undefined' ? window.executeQuery : null);
|
|
60
101
|
|
|
61
102
|
if (typeof executor !== 'function') {
|
|
@@ -64,10 +105,10 @@ class DataQueryService {
|
|
|
64
105
|
|
|
65
106
|
return new Promise((resolve, reject) => {
|
|
66
107
|
executor(query, params,
|
|
67
|
-
(
|
|
108
|
+
(valorBruto) => {
|
|
68
109
|
try {
|
|
69
|
-
const
|
|
70
|
-
resolve(
|
|
110
|
+
const dados = ServicoDados.normalizarResposta(valorBruto);
|
|
111
|
+
resolve(dados || []);
|
|
71
112
|
} catch (err) {
|
|
72
113
|
reject(err);
|
|
73
114
|
}
|
|
@@ -79,154 +120,189 @@ class DataQueryService {
|
|
|
79
120
|
|
|
80
121
|
/**
|
|
81
122
|
* Executa uma consulta paginada.
|
|
82
|
-
* @param {string} query A query base.
|
|
83
|
-
* @param {Object} params Parâmetros da query.
|
|
84
|
-
* @param {number} limit Registros por página.
|
|
85
|
-
* @param {number} offset Ponto de início (pular x registros).
|
|
86
|
-
* @returns {Promise<Object>} Objeto contendo dados e metadados de paginação.
|
|
87
123
|
*/
|
|
88
|
-
static async
|
|
89
|
-
const
|
|
90
|
-
const
|
|
124
|
+
static async consultarPaginado(query, params = null, limite, offset) {
|
|
125
|
+
const limiteSeguro = Number(limite);
|
|
126
|
+
const offsetSeguro = Number(offset);
|
|
91
127
|
|
|
92
|
-
if (isNaN(
|
|
93
|
-
throw new Error("Parâmetros '
|
|
128
|
+
if (isNaN(limiteSeguro) || isNaN(offsetSeguro) || limiteSeguro < 0 || offsetSeguro < 0) {
|
|
129
|
+
throw new Error("Parâmetros 'limite' e 'offset' devem ser números positivos válidos.");
|
|
94
130
|
}
|
|
95
131
|
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
const currentPage = Math.floor(safeOffset / safeLimit) + 1;
|
|
132
|
+
const queryPaginada = `${query} LIMIT ${limiteSeguro} OFFSET ${offsetSeguro}`;
|
|
133
|
+
const dados = await ServicoDados.consultar(queryPaginada, params);
|
|
134
|
+
const paginaAtual = Math.floor(offsetSeguro / limiteSeguro) + 1;
|
|
101
135
|
|
|
102
136
|
return {
|
|
103
|
-
|
|
104
|
-
meta: {
|
|
105
|
-
currentPage,
|
|
106
|
-
itemsPerPage: safeLimit,
|
|
107
|
-
totalItems: null,
|
|
108
|
-
totalPages: null
|
|
109
|
-
}
|
|
137
|
+
dados,
|
|
138
|
+
meta: { paginaAtual, itensPorPagina: limiteSeguro, totalItens: null, totalPaginas: null }
|
|
110
139
|
};
|
|
111
140
|
}
|
|
112
|
-
}
|
|
113
141
|
|
|
114
|
-
/**
|
|
115
|
-
* FormatUtils (Antigo JFSK)
|
|
116
|
-
* Utilitários para formatação de moeda, números e datas (Locale pt-BR).
|
|
117
|
-
*/
|
|
118
|
-
class FormatUtils {
|
|
119
142
|
/**
|
|
120
|
-
*
|
|
143
|
+
* Salva (Insert) ou Atualiza (Update) um registro usando DatasetSP.
|
|
144
|
+
* * Lógica Automática:
|
|
145
|
+
* 1. Se 'chavesPrimarias' for informado -> Realiza UPDATE no registro específico.
|
|
146
|
+
* 2. Se 'chavesPrimarias' for NULO/UNDEFINED -> Realiza INSERT de um novo registro.
|
|
147
|
+
* * @param {Object} dados Objeto com os dados a serem gravados { CAMPO: valor }.
|
|
148
|
+
* @param {string} entidade Nome da entidade/tabela (ex: 'Parceiro', 'TGFCAB').
|
|
149
|
+
* @param {Object} [chavesPrimarias=null] Chaves primárias { PK: valor } para edição.
|
|
150
|
+
* @returns {Promise<Object>} Resposta da requisição.
|
|
121
151
|
*/
|
|
122
|
-
static
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
152
|
+
static async salvar(dados, entidade, chavesPrimarias = null) {
|
|
153
|
+
const url = `${window.location.origin}/mge/service.sbr?serviceName=DatasetSP.save&outputType=json`;
|
|
154
|
+
|
|
155
|
+
const chavesDados = Object.keys(dados);
|
|
156
|
+
const fields = chavesDados.map(campo => campo.toUpperCase());
|
|
157
|
+
|
|
158
|
+
const values = {};
|
|
159
|
+
chavesDados.forEach((chave, indice) => {
|
|
160
|
+
values[indice.toString()] = String(dados[chave]);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const record = { values: values };
|
|
164
|
+
|
|
165
|
+
if (chavesPrimarias) {
|
|
166
|
+
const pk = {};
|
|
167
|
+
Object.keys(chavesPrimarias).forEach(chave => {
|
|
168
|
+
pk[chave.toUpperCase()] = String(chavesPrimarias[chave]);
|
|
169
|
+
});
|
|
170
|
+
record.pk = pk;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const dadosEnvio = {
|
|
174
|
+
serviceName: 'DatasetSP.save',
|
|
175
|
+
requestBody: {
|
|
176
|
+
entityName: entidade,
|
|
177
|
+
fields: fields,
|
|
178
|
+
records: [record]
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
return await ServicoDados.post(url, dadosEnvio);
|
|
130
183
|
}
|
|
131
184
|
|
|
132
185
|
/**
|
|
133
|
-
*
|
|
186
|
+
* Exclui registros da base de dados.
|
|
187
|
+
* @param {string} entidade Nome da Entidade.
|
|
188
|
+
* @param {Object|Array} chavesPrimarias Objeto PK { COD: 1 } ou Array de PKs.
|
|
189
|
+
* @returns {Promise<Object>} Resposta da exclusão.
|
|
134
190
|
*/
|
|
135
|
-
static
|
|
136
|
-
const
|
|
137
|
-
|
|
191
|
+
static async excluir(entidade, chavesPrimarias) {
|
|
192
|
+
const url = `${window.location.origin}/mge/service.sbr?serviceName=DatasetSP.removeRecord&outputType=json`;
|
|
193
|
+
|
|
194
|
+
const dadosEnvio = {
|
|
195
|
+
serviceName: 'DatasetSP.removeRecord',
|
|
196
|
+
requestBody: {
|
|
197
|
+
entityName: entidade,
|
|
198
|
+
pks: Array.isArray(chavesPrimarias) ? chavesPrimarias : [chavesPrimarias]
|
|
199
|
+
}
|
|
200
|
+
};
|
|
138
201
|
|
|
139
|
-
return
|
|
140
|
-
|
|
141
|
-
|
|
202
|
+
return ServicoDados.post(url, dadosEnvio);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* UtilitariosFormatacao
|
|
208
|
+
*/
|
|
209
|
+
class UtilitariosFormatacao {
|
|
210
|
+
static moeda(valor) {
|
|
211
|
+
const valorNumerico = parseFloat(valor);
|
|
212
|
+
if (isNaN(valorNumerico)) return "R$ 0,00";
|
|
213
|
+
return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(valorNumerico);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static numerico(valor) {
|
|
217
|
+
const valorNumerico = parseFloat(valor);
|
|
218
|
+
if (isNaN(valorNumerico)) return "0";
|
|
219
|
+
return new Intl.NumberFormat('pt-BR', { maximumFractionDigits: 0 }).format(valorNumerico);
|
|
142
220
|
}
|
|
143
221
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const numberVal = parseFloat(value);
|
|
149
|
-
if (isNaN(numberVal)) return "0,00";
|
|
150
|
-
|
|
151
|
-
return new Intl.NumberFormat('pt-BR', {
|
|
152
|
-
minimumFractionDigits: 2,
|
|
153
|
-
maximumFractionDigits: 2
|
|
154
|
-
}).format(numberVal);
|
|
222
|
+
static decimal(valor) {
|
|
223
|
+
const valorNumerico = parseFloat(valor);
|
|
224
|
+
if (isNaN(valorNumerico)) return "0,00";
|
|
225
|
+
return new Intl.NumberFormat('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(valorNumerico);
|
|
155
226
|
}
|
|
156
227
|
|
|
157
|
-
|
|
158
|
-
* Retorna data atual no formato DD/MM/YYYY.
|
|
159
|
-
*/
|
|
160
|
-
static currentDate() {
|
|
228
|
+
static data() {
|
|
161
229
|
return new Intl.DateTimeFormat('pt-BR').format(new Date());
|
|
162
230
|
}
|
|
163
231
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const parts = isoDateString.split('-');
|
|
170
|
-
if (parts.length !== 3) return isoDateString;
|
|
171
|
-
return `${parts[2]}/${parts[1]}/${parts[0]}`;
|
|
232
|
+
static formatarDataParaDMY(stringDataIso) {
|
|
233
|
+
if (!stringDataIso) return '';
|
|
234
|
+
const partes = stringDataIso.split('-');
|
|
235
|
+
if (partes.length !== 3) return stringDataIso;
|
|
236
|
+
return `${partes[2]}/${partes[1]}/${partes[0]}`;
|
|
172
237
|
}
|
|
173
238
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
239
|
+
static normalizarStringData(entradaData) {
|
|
240
|
+
if (!entradaData) return '01/01/2025';
|
|
241
|
+
let str = String(entradaData).trim();
|
|
242
|
+
|
|
243
|
+
if (str.includes('/')) {
|
|
244
|
+
const partes = str.split('/');
|
|
245
|
+
if (partes.length >= 3) {
|
|
246
|
+
const dia = String(parseInt(partes[0], 10)).padStart(2, '0');
|
|
247
|
+
const mes = String(parseInt(partes[1], 10)).padStart(2, '0');
|
|
248
|
+
const ano = partes[2].substring(0, 4);
|
|
249
|
+
return `${dia}/${mes}/${ano}`;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (str.includes('-')) {
|
|
254
|
+
const partes = str.split('-');
|
|
255
|
+
if (partes.length === 3) {
|
|
256
|
+
return `${partes[2].substring(0, 2)}/${partes[1]}/${partes[0]}`;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return str;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
static obterDataAtualISO() {
|
|
178
264
|
return new Date().toISOString().split('T')[0];
|
|
179
265
|
}
|
|
180
266
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const datePart = new Intl.DateTimeFormat('pt-BR').format(now);
|
|
187
|
-
const timePart = now.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
|
|
188
|
-
return `${datePart} ${timePart}`;
|
|
267
|
+
static dataHora() {
|
|
268
|
+
const agora = new Date();
|
|
269
|
+
const parteData = new Intl.DateTimeFormat('pt-BR').format(agora);
|
|
270
|
+
const parteHora = agora.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
|
|
271
|
+
return `${parteData} ${parteHora}`;
|
|
189
272
|
}
|
|
190
273
|
}
|
|
191
274
|
|
|
192
275
|
/**
|
|
193
|
-
*
|
|
194
|
-
* Utilitários para ordenação de listas.
|
|
276
|
+
* UtilitariosOrdenacao
|
|
195
277
|
*/
|
|
196
|
-
class
|
|
197
|
-
static
|
|
198
|
-
if (!Array.isArray(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
const valB = b[key];
|
|
205
|
-
|
|
278
|
+
class UtilitariosOrdenacao {
|
|
279
|
+
static ordenar(dados, chave, direcao = 'asc') {
|
|
280
|
+
if (!Array.isArray(dados) || !chave) return dados;
|
|
281
|
+
const multiplicador = direcao === 'asc' ? 1 : -1;
|
|
282
|
+
|
|
283
|
+
return [...dados].sort((a, b) => {
|
|
284
|
+
const valA = a[chave];
|
|
285
|
+
const valB = b[chave];
|
|
206
286
|
if (valA == null) return 1;
|
|
207
287
|
if (valB == null) return -1;
|
|
208
|
-
|
|
209
288
|
if (typeof valA === 'string' && typeof valB === 'string') {
|
|
210
|
-
return valA.localeCompare(valB) *
|
|
289
|
+
return valA.localeCompare(valB) * multiplicador;
|
|
211
290
|
}
|
|
212
|
-
|
|
213
|
-
if (valA
|
|
214
|
-
if (valA > valB) return 1 * multiplier;
|
|
291
|
+
if (valA < valB) return -1 * multiplicador;
|
|
292
|
+
if (valA > valB) return 1 * multiplicador;
|
|
215
293
|
return 0;
|
|
216
294
|
});
|
|
217
295
|
}
|
|
218
296
|
}
|
|
219
297
|
|
|
220
298
|
/**
|
|
221
|
-
*
|
|
222
|
-
* Exportação para Excel (.xlsx) e CSV.
|
|
299
|
+
* ServicoExportacao
|
|
223
300
|
*/
|
|
224
|
-
class
|
|
301
|
+
class ServicoExportacao {
|
|
225
302
|
static CDN_URL = "https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js";
|
|
226
303
|
|
|
227
|
-
static async
|
|
304
|
+
static async carregarDependencia() {
|
|
228
305
|
if (typeof window !== 'undefined' && window.XLSX) return;
|
|
229
|
-
|
|
230
306
|
return new Promise((resolve, reject) => {
|
|
231
307
|
const script = document.createElement('script');
|
|
232
308
|
script.src = this.CDN_URL;
|
|
@@ -237,38 +313,36 @@ class ExportService {
|
|
|
237
313
|
});
|
|
238
314
|
}
|
|
239
315
|
|
|
240
|
-
static async
|
|
241
|
-
await this.
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
});
|
|
247
|
-
return row;
|
|
316
|
+
static async paraExcel(dados, colunas, nomeArquivo, nomePlanilha = 'Dados') {
|
|
317
|
+
await this.carregarDependencia();
|
|
318
|
+
const dadosFormatados = dados.map(item => {
|
|
319
|
+
const linha = {};
|
|
320
|
+
colunas.forEach(col => { linha[col.display] = item[col.key]; });
|
|
321
|
+
return linha;
|
|
248
322
|
});
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
XLSX.utils.book_append_sheet(
|
|
253
|
-
XLSX.writeFile(
|
|
323
|
+
|
|
324
|
+
const planilha = XLSX.utils.json_to_sheet(dadosFormatados);
|
|
325
|
+
const pastaTrabalho = XLSX.utils.book_new();
|
|
326
|
+
XLSX.utils.book_append_sheet(pastaTrabalho, planilha, nomePlanilha);
|
|
327
|
+
XLSX.writeFile(pastaTrabalho, `${nomeArquivo}.xlsx`);
|
|
254
328
|
}
|
|
255
329
|
|
|
256
|
-
static
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
return
|
|
330
|
+
static paraCSV(dados, colunas, nomeArquivo) {
|
|
331
|
+
const linhaCabecalho = colunas.map(c => `"${c.display}"`).join(';');
|
|
332
|
+
const linhasCorpo = dados.map(item => {
|
|
333
|
+
return colunas.map(col => {
|
|
260
334
|
const val = item[col.key] ?? '';
|
|
261
|
-
const
|
|
262
|
-
return `"${
|
|
335
|
+
const valString = String(val).replace(/"/g, '""').replace(/\r?\n|\r/g, ' ');
|
|
336
|
+
return `"${valString}"`;
|
|
263
337
|
}).join(';');
|
|
264
338
|
});
|
|
265
|
-
const
|
|
266
|
-
const blob = new Blob([
|
|
339
|
+
const conteudoCsv = [linhaCabecalho, ...linhasCorpo].join('\r\n');
|
|
340
|
+
const blob = new Blob([conteudoCsv], { type: 'text/csv;charset=utf-8;' });
|
|
267
341
|
const link = document.createElement('a');
|
|
268
342
|
if (link.download !== undefined) {
|
|
269
343
|
const url = URL.createObjectURL(blob);
|
|
270
344
|
link.setAttribute('href', url);
|
|
271
|
-
link.setAttribute('download', `${
|
|
345
|
+
link.setAttribute('download', `${nomeArquivo}.csv`);
|
|
272
346
|
link.style.visibility = 'hidden';
|
|
273
347
|
document.body.appendChild(link);
|
|
274
348
|
link.click();
|
|
@@ -276,44 +350,36 @@ class ExportService {
|
|
|
276
350
|
}
|
|
277
351
|
}
|
|
278
352
|
|
|
279
|
-
static async
|
|
280
|
-
if (!
|
|
281
|
-
if (!
|
|
353
|
+
static async exportar(dados, colunas, nomeArquivo = 'exportacao', formato = 'xlsx') {
|
|
354
|
+
if (!dados?.length) throw new Error("Sem dados para exportar.");
|
|
355
|
+
if (!colunas?.length) throw new Error("Colunas não definidas.");
|
|
282
356
|
|
|
283
|
-
const fmt =
|
|
284
|
-
if (fmt === 'xlsx') await this.
|
|
285
|
-
else if (fmt === 'csv') this.
|
|
286
|
-
else throw new Error(`Formato '${
|
|
357
|
+
const fmt = formato.toLowerCase();
|
|
358
|
+
if (fmt === 'xlsx') await this.paraExcel(dados, colunas, nomeArquivo);
|
|
359
|
+
else if (fmt === 'csv') this.paraCSV(dados, colunas, nomeArquivo);
|
|
360
|
+
else throw new Error(`Formato '${formato}' não suportado.`);
|
|
287
361
|
}
|
|
288
362
|
}
|
|
289
363
|
|
|
290
|
-
// ==========================================
|
|
291
|
-
// DETECÇÃO DE AMBIENTE (Browser vs Node.js)
|
|
292
|
-
// ==========================================
|
|
293
|
-
|
|
294
364
|
const lib = {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
365
|
+
ServicoDados,
|
|
366
|
+
UtilitariosFormatacao,
|
|
367
|
+
UtilitariosOrdenacao,
|
|
368
|
+
ServicoExportacao,
|
|
369
|
+
|
|
370
|
+
// Aliases
|
|
371
|
+
JSK: ServicoDados,
|
|
372
|
+
JFSK: UtilitariosFormatacao,
|
|
373
|
+
JOSK: UtilitariosOrdenacao,
|
|
374
|
+
JEXSK: ServicoExportacao
|
|
304
375
|
};
|
|
305
376
|
|
|
306
|
-
// Se estiver em ambiente Node.js/CommonJS
|
|
307
377
|
if (typeof module !== 'undefined' && module.exports) {
|
|
308
378
|
module.exports = lib;
|
|
309
|
-
}
|
|
310
|
-
// Se estiver no Browser (janela global)
|
|
311
|
-
else if (typeof window !== 'undefined') {
|
|
379
|
+
} else if (typeof window !== 'undefined') {
|
|
312
380
|
Object.assign(window, lib);
|
|
313
|
-
|
|
314
|
-
// Configuração automática se existir a função legado
|
|
315
381
|
if (typeof window.executeQuery === 'function') {
|
|
316
|
-
|
|
317
|
-
console.log("[
|
|
382
|
+
ServicoDados.definirExecutor(window.executeQuery);
|
|
383
|
+
console.log("[JSK] Executor 'executeQuery' detectado e configurado automaticamente.");
|
|
318
384
|
}
|
|
319
385
|
}
|