@jtandrelevicius/utils-js-library 1.0.4 → 1.0.6
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 +414 -182
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,58 +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
|
-
const
|
|
68
|
+
const valorParseado = typeof valorBruto === 'string' ? JSON.parse(valorBruto) : valorBruto;
|
|
24
69
|
|
|
25
|
-
if (!
|
|
26
|
-
if (Array.isArray(
|
|
70
|
+
if (!valorParseado) return [];
|
|
71
|
+
if (Array.isArray(valorParseado)) return valorParseado;
|
|
27
72
|
|
|
28
|
-
let
|
|
73
|
+
let dadosFinais = null;
|
|
29
74
|
|
|
30
|
-
if (typeof
|
|
31
|
-
if (typeof
|
|
32
|
-
|
|
33
|
-
} else if (
|
|
34
|
-
|
|
35
|
-
} else if (
|
|
36
|
-
const
|
|
37
|
-
|
|
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;
|
|
38
83
|
}
|
|
39
84
|
}
|
|
40
85
|
|
|
41
|
-
if (Array.isArray(
|
|
42
|
-
|
|
86
|
+
if (Array.isArray(dadosFinais)) return dadosFinais;
|
|
43
87
|
throw new Error("Formato de resposta desconhecido ou inválido.");
|
|
44
|
-
} catch (
|
|
45
|
-
throw new Error(`Falha ao normalizar resposta da query: ${
|
|
88
|
+
} catch (erro) {
|
|
89
|
+
throw new Error(`Falha ao normalizar resposta da query: ${erro.message}`);
|
|
46
90
|
}
|
|
47
91
|
}
|
|
48
92
|
|
|
49
93
|
/**
|
|
50
|
-
* Executa uma consulta de dados.
|
|
51
|
-
* @param {string} query A string da consulta
|
|
52
|
-
* @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.
|
|
53
97
|
* @returns {Promise<Array<Object>>} Promise resolvendo com os dados.
|
|
54
98
|
*/
|
|
55
|
-
static async
|
|
99
|
+
static async consultar(query, params = null) {
|
|
56
100
|
const executor = this.executor || (typeof window !== 'undefined' ? window.executeQuery : null);
|
|
57
101
|
|
|
58
102
|
if (typeof executor !== 'function') {
|
|
@@ -61,10 +105,10 @@ class DataQueryService {
|
|
|
61
105
|
|
|
62
106
|
return new Promise((resolve, reject) => {
|
|
63
107
|
executor(query, params,
|
|
64
|
-
(
|
|
108
|
+
(valorBruto) => {
|
|
65
109
|
try {
|
|
66
|
-
const
|
|
67
|
-
resolve(
|
|
110
|
+
const dados = ServicoDados.normalizarResposta(valorBruto);
|
|
111
|
+
resolve(dados || []);
|
|
68
112
|
} catch (err) {
|
|
69
113
|
reject(err);
|
|
70
114
|
}
|
|
@@ -76,154 +120,189 @@ class DataQueryService {
|
|
|
76
120
|
|
|
77
121
|
/**
|
|
78
122
|
* Executa uma consulta paginada.
|
|
79
|
-
* @param {string} query A query base.
|
|
80
|
-
* @param {Object} params Parâmetros da query.
|
|
81
|
-
* @param {number} limit Registros por página.
|
|
82
|
-
* @param {number} offset Ponto de início (pular x registros).
|
|
83
|
-
* @returns {Promise<Object>} Objeto contendo dados e metadados de paginação.
|
|
84
123
|
*/
|
|
85
|
-
static async
|
|
86
|
-
const
|
|
87
|
-
const
|
|
124
|
+
static async consultarPaginado(query, params = null, limite, offset) {
|
|
125
|
+
const limiteSeguro = Number(limite);
|
|
126
|
+
const offsetSeguro = Number(offset);
|
|
88
127
|
|
|
89
|
-
if (isNaN(
|
|
90
|
-
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.");
|
|
91
130
|
}
|
|
92
131
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
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;
|
|
98
135
|
|
|
99
136
|
return {
|
|
100
|
-
|
|
101
|
-
meta: {
|
|
102
|
-
currentPage,
|
|
103
|
-
itemsPerPage: safeLimit,
|
|
104
|
-
totalItems: null,
|
|
105
|
-
totalPages: null
|
|
106
|
-
}
|
|
137
|
+
dados,
|
|
138
|
+
meta: { paginaAtual, itensPorPagina: limiteSeguro, totalItens: null, totalPaginas: null }
|
|
107
139
|
};
|
|
108
140
|
}
|
|
109
|
-
}
|
|
110
141
|
|
|
111
|
-
/**
|
|
112
|
-
* FormatUtils
|
|
113
|
-
* Utilitários para formatação de moeda, números e datas (Locale pt-BR).
|
|
114
|
-
*/
|
|
115
|
-
class FormatUtils {
|
|
116
142
|
/**
|
|
117
|
-
*
|
|
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.
|
|
118
151
|
*/
|
|
119
|
-
static
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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);
|
|
127
183
|
}
|
|
128
184
|
|
|
129
185
|
/**
|
|
130
|
-
*
|
|
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.
|
|
131
190
|
*/
|
|
132
|
-
static
|
|
133
|
-
const
|
|
134
|
-
|
|
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
|
+
};
|
|
201
|
+
|
|
202
|
+
return ServicoDados.post(url, dadosEnvio);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
135
205
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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);
|
|
139
220
|
}
|
|
140
221
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const numberVal = parseFloat(value);
|
|
146
|
-
if (isNaN(numberVal)) return "0,00";
|
|
147
|
-
|
|
148
|
-
return new Intl.NumberFormat('pt-BR', {
|
|
149
|
-
minimumFractionDigits: 2,
|
|
150
|
-
maximumFractionDigits: 2
|
|
151
|
-
}).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);
|
|
152
226
|
}
|
|
153
227
|
|
|
154
|
-
|
|
155
|
-
* Retorna data atual no formato DD/MM/YYYY.
|
|
156
|
-
*/
|
|
157
|
-
static currentDate() {
|
|
228
|
+
static data() {
|
|
158
229
|
return new Intl.DateTimeFormat('pt-BR').format(new Date());
|
|
159
230
|
}
|
|
160
231
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const parts = isoDateString.split('-');
|
|
167
|
-
if (parts.length !== 3) return isoDateString;
|
|
168
|
-
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]}`;
|
|
169
237
|
}
|
|
170
238
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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() {
|
|
175
264
|
return new Date().toISOString().split('T')[0];
|
|
176
265
|
}
|
|
177
266
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const datePart = new Intl.DateTimeFormat('pt-BR').format(now);
|
|
184
|
-
const timePart = now.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
|
|
185
|
-
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}`;
|
|
186
272
|
}
|
|
187
273
|
}
|
|
188
274
|
|
|
189
275
|
/**
|
|
190
|
-
*
|
|
191
|
-
* Utilitários para ordenação de listas.
|
|
276
|
+
* UtilitariosOrdenacao
|
|
192
277
|
*/
|
|
193
|
-
class
|
|
194
|
-
static
|
|
195
|
-
if (!Array.isArray(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
const valB = b[key];
|
|
202
|
-
|
|
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];
|
|
203
286
|
if (valA == null) return 1;
|
|
204
287
|
if (valB == null) return -1;
|
|
205
|
-
|
|
206
288
|
if (typeof valA === 'string' && typeof valB === 'string') {
|
|
207
|
-
return valA.localeCompare(valB) *
|
|
289
|
+
return valA.localeCompare(valB) * multiplicador;
|
|
208
290
|
}
|
|
209
|
-
|
|
210
|
-
if (valA
|
|
211
|
-
if (valA > valB) return 1 * multiplier;
|
|
291
|
+
if (valA < valB) return -1 * multiplicador;
|
|
292
|
+
if (valA > valB) return 1 * multiplicador;
|
|
212
293
|
return 0;
|
|
213
294
|
});
|
|
214
295
|
}
|
|
215
296
|
}
|
|
216
297
|
|
|
217
298
|
/**
|
|
218
|
-
*
|
|
219
|
-
* Exportação para Excel (.xlsx) e CSV.
|
|
299
|
+
* ServicoExportacao
|
|
220
300
|
*/
|
|
221
|
-
class
|
|
301
|
+
class ServicoExportacao {
|
|
222
302
|
static CDN_URL = "https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js";
|
|
223
303
|
|
|
224
|
-
static async
|
|
304
|
+
static async carregarDependencia() {
|
|
225
305
|
if (typeof window !== 'undefined' && window.XLSX) return;
|
|
226
|
-
|
|
227
306
|
return new Promise((resolve, reject) => {
|
|
228
307
|
const script = document.createElement('script');
|
|
229
308
|
script.src = this.CDN_URL;
|
|
@@ -234,38 +313,36 @@ class ExportService {
|
|
|
234
313
|
});
|
|
235
314
|
}
|
|
236
315
|
|
|
237
|
-
static async
|
|
238
|
-
await this.
|
|
239
|
-
const
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
});
|
|
244
|
-
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;
|
|
245
322
|
});
|
|
246
323
|
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
XLSX.utils.book_append_sheet(
|
|
250
|
-
XLSX.writeFile(
|
|
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`);
|
|
251
328
|
}
|
|
252
329
|
|
|
253
|
-
static
|
|
254
|
-
const
|
|
255
|
-
const
|
|
256
|
-
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 => {
|
|
257
334
|
const val = item[col.key] ?? '';
|
|
258
|
-
const
|
|
259
|
-
return `"${
|
|
335
|
+
const valString = String(val).replace(/"/g, '""').replace(/\r?\n|\r/g, ' ');
|
|
336
|
+
return `"${valString}"`;
|
|
260
337
|
}).join(';');
|
|
261
338
|
});
|
|
262
|
-
const
|
|
263
|
-
const blob = new Blob([
|
|
339
|
+
const conteudoCsv = [linhaCabecalho, ...linhasCorpo].join('\r\n');
|
|
340
|
+
const blob = new Blob([conteudoCsv], { type: 'text/csv;charset=utf-8;' });
|
|
264
341
|
const link = document.createElement('a');
|
|
265
342
|
if (link.download !== undefined) {
|
|
266
343
|
const url = URL.createObjectURL(blob);
|
|
267
344
|
link.setAttribute('href', url);
|
|
268
|
-
link.setAttribute('download', `${
|
|
345
|
+
link.setAttribute('download', `${nomeArquivo}.csv`);
|
|
269
346
|
link.style.visibility = 'hidden';
|
|
270
347
|
document.body.appendChild(link);
|
|
271
348
|
link.click();
|
|
@@ -273,40 +350,195 @@ class ExportService {
|
|
|
273
350
|
}
|
|
274
351
|
}
|
|
275
352
|
|
|
276
|
-
static async
|
|
277
|
-
if (!
|
|
278
|
-
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.");
|
|
279
356
|
|
|
280
|
-
const fmt =
|
|
281
|
-
if (fmt === 'xlsx') await this.
|
|
282
|
-
else if (fmt === 'csv') this.
|
|
283
|
-
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.`);
|
|
284
361
|
}
|
|
285
362
|
}
|
|
286
363
|
|
|
364
|
+
/**
|
|
365
|
+
* ServicoPagina
|
|
366
|
+
* Manipuladores de Página e Navegação Sankhya-W
|
|
367
|
+
*/
|
|
368
|
+
class ServicoPagina {
|
|
369
|
+
/**
|
|
370
|
+
* Retorna a URL atual da pagina com o caminho opcional.
|
|
371
|
+
* @param { String } path Caminho a ser adicionado a URL atual
|
|
372
|
+
* @returns { String } A URL com o protocolo HTTPS ou HTTP
|
|
373
|
+
*/
|
|
374
|
+
static getUrl(path) {
|
|
375
|
+
return `${window.location.origin}${path ? '/' + path.replace('/', '') : ''}`;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Remove o frame da página de BI
|
|
380
|
+
* * @param { { instancia: String, paginaInicial: String, opcoes: any } } configuracoes Configuracoes gerais da pagina
|
|
381
|
+
* * **instancia**: Nome exato do componente de BI
|
|
382
|
+
* * **paginaInicial**: URL (a partir da pasta raiz) e nome do arquivo da pagina inicial
|
|
383
|
+
* * **opcoes**: [opcional] Campos com valores a serem recebidos pela pagina
|
|
384
|
+
* * _Padrao_: `{ instancia: '', paginaInicial: 'app.jsp' }`
|
|
385
|
+
* * @example JPSK.removerFrame ({ instancia: 'TELA_HTML5', paginaInicial: 'index.jsp'});
|
|
386
|
+
*/
|
|
387
|
+
static removerFrame({ instancia, paginaInicial, ...opcoes } = { instancia: '', paginaInicial: 'app.jsp' }) {
|
|
388
|
+
|
|
389
|
+
new Promise(resolve => {
|
|
390
|
+
|
|
391
|
+
if (window.parent.document.getElementsByTagName('body').length) {
|
|
392
|
+
|
|
393
|
+
if (window.parent.document.querySelector('div.gwt-PopupPanel.alert-box.box-shadow'))
|
|
394
|
+
window.parent.document.querySelector('div.gwt-PopupPanel.alert-box.box-shadow')
|
|
395
|
+
.style.display = 'none';
|
|
396
|
+
|
|
397
|
+
window.parent.document.getElementsByTagName('body')[0].style.overflow = 'hidden';
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (window.parent.parent.document.getElementsByTagName('body').length) {
|
|
401
|
+
|
|
402
|
+
if (window.parent.parent.document.querySelector('div.gwt-PopupPanel.alert-box.box-shadow'))
|
|
403
|
+
window.parent.parent.document.querySelector('div.gwt-PopupPanel.alert-box.box-shadow')
|
|
404
|
+
.style.display = 'none';
|
|
405
|
+
|
|
406
|
+
window.parent.parent.document.getElementsByTagName('body')[0].style.overflow = 'hidden';
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (
|
|
410
|
+
window.parent.document
|
|
411
|
+
.querySelector('div.GI-BUHVBPVC > div > div > div > div > div > table > tbody > tr > td > div')
|
|
412
|
+
) {
|
|
413
|
+
instancia = window.parent.document
|
|
414
|
+
.querySelector('div.GI-BUHVBPVC > div > div > div > div > div > table > tbody > tr > td > div')
|
|
415
|
+
.textContent;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (instancia && instancia.length > 0) {
|
|
419
|
+
ServicoDados.consultar(`SELECT NUGDG FROM TSIGDG WHERE TITULO = '${instancia}'`).
|
|
420
|
+
then(e => resolve({ gadGetID: 'html5_z6dld', nuGdt: e[0].NUGDG, ...opcoes }));
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
resolve({ gadGetID: 'html5_z6dld', nuGdt: 0, ...opcoes });
|
|
424
|
+
}
|
|
425
|
+
}).
|
|
426
|
+
then(o =>
|
|
427
|
+
setTimeout(() => {
|
|
428
|
+
if (typeof window.parent.document.getElementsByClassName('DashWindow')[0] != 'undefined') {
|
|
429
|
+
|
|
430
|
+
const opcoesUrl =
|
|
431
|
+
Object.
|
|
432
|
+
keys(o).
|
|
433
|
+
filter(item => !['params', 'UID', 'instance', 'nuGdg', 'gadGetID'].includes(item)).
|
|
434
|
+
map(item => `&${item}=${o[item]}`).
|
|
435
|
+
join('');
|
|
436
|
+
|
|
437
|
+
const url = `/mge/html5component.mge?entryPoint=${paginaInicial}&nuGdg=${o.nuGdt}${opcoesUrl}`
|
|
438
|
+
|
|
439
|
+
setTimeout(() =>
|
|
440
|
+
window.parent.document.getElementsByClassName('dyna-gadget')[0].innerHTML =
|
|
441
|
+
`<iframe src="${url}" class="gwt-Frame" style="width: 100%; height: 100%;"></iframe>`
|
|
442
|
+
, 500);
|
|
443
|
+
|
|
444
|
+
setTimeout(() => document.getElementsByClassName('popupContent').length
|
|
445
|
+
? document.getElementsByClassName('popupContent')[0].parentElement.remove()
|
|
446
|
+
: (() => { /**/ })()
|
|
447
|
+
, 20000);
|
|
448
|
+
|
|
449
|
+
setTimeout(() => (document.getElementById('stndz-style').parentElement.parentElement)
|
|
450
|
+
.getElementsByTagName('body')[0].style.overflow = 'hidden'
|
|
451
|
+
, 20000);
|
|
452
|
+
}
|
|
453
|
+
})
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Abre uma nova guia com a pagina atual
|
|
459
|
+
*
|
|
460
|
+
* @param { boolean } forcado - [Opcional] Indica se a abertura da nova guia deve ser forcada
|
|
461
|
+
* * @example JPSK.novaGuia ();
|
|
462
|
+
*/
|
|
463
|
+
static novaGuia(forcado = false) {
|
|
464
|
+
|
|
465
|
+
if ((window.parent.parent.document.querySelector('.Taskbar-container') && !forcado) || forcado) {
|
|
466
|
+
Object.assign(document.createElement('a'), { target: '_blank', href: window.location.href }).click();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Abre uma pagina dentro do Sankhya-W.
|
|
473
|
+
* * - Se o resourceID nao existir, o sistema informara que a tela nao existe.
|
|
474
|
+
* - Se as chaves primarias nao forem informadas, a tela sera aberta na pagina inicial.
|
|
475
|
+
* - Se existirem chaves primarias, mas nao forem encontradas, a tela ssera aberta como visualizacao de um registro vazio (para inclusao)
|
|
476
|
+
* - Se existirem chaves primarias e forem encontradas, a tela sera aberta no registro encontrado.
|
|
477
|
+
* * @param { String } resourceID ID do recurso a ser aberto
|
|
478
|
+
* @param { Object } chavesPrimarias Chaves de identificacao do registro
|
|
479
|
+
* * @example JPSK.abrirPagina ('br.com.sankhya.core.cad.marcas', { CODIGO: 999 });
|
|
480
|
+
*/
|
|
481
|
+
static abrirPagina(resourceID, chavesPrimarias) {
|
|
482
|
+
|
|
483
|
+
let url = ServicoPagina.getUrl(`/mge/system.jsp#app/%resID`);
|
|
484
|
+
url = url.replace('%resID', btoa(resourceID));
|
|
485
|
+
|
|
486
|
+
if (chavesPrimarias) {
|
|
487
|
+
|
|
488
|
+
let body = {};
|
|
489
|
+
|
|
490
|
+
Object.keys(chavesPrimarias).forEach(function (chave) {
|
|
491
|
+
body[chave] = isNaN(chavesPrimarias[chave])
|
|
492
|
+
? String(chavesPrimarias[chave])
|
|
493
|
+
: Number(chavesPrimarias[chave])
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
url = url.concat(`/${btoa(JSON.stringify(body))}`);
|
|
497
|
+
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
Object.assign(document.createElement('a'), {
|
|
501
|
+
target: '_top',
|
|
502
|
+
href: url
|
|
503
|
+
}).click();
|
|
504
|
+
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Fecha a pagina atual.
|
|
509
|
+
* * Ele verifica se a pagina atual esta dentro do Sankhya-W para fechar, senao ele fecha a aba do navegador.
|
|
510
|
+
*/
|
|
511
|
+
static fecharPagina() {
|
|
512
|
+
if (window.parent.parent.document.querySelector('.Taskbar-container')) {
|
|
513
|
+
window.parent.parent.document.querySelector(
|
|
514
|
+
'li.ListItem.AppItem.AppItem-selected div.Taskbar-icon.icon-close').click();
|
|
515
|
+
} else {
|
|
516
|
+
window.close();
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
287
520
|
|
|
288
521
|
const lib = {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
522
|
+
ServicoDados,
|
|
523
|
+
UtilitariosFormatacao,
|
|
524
|
+
UtilitariosOrdenacao,
|
|
525
|
+
ServicoExportacao,
|
|
526
|
+
ServicoPagina,
|
|
527
|
+
|
|
528
|
+
// Aliases
|
|
529
|
+
JSK: ServicoDados,
|
|
530
|
+
JFSK: UtilitariosFormatacao,
|
|
531
|
+
JOSK: UtilitariosOrdenacao,
|
|
532
|
+
JEXSK: ServicoExportacao,
|
|
533
|
+
JPSK: ServicoPagina
|
|
298
534
|
};
|
|
299
535
|
|
|
300
|
-
|
|
301
536
|
if (typeof module !== 'undefined' && module.exports) {
|
|
302
537
|
module.exports = lib;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
else if (typeof window !== 'undefined') {
|
|
538
|
+
} else if (typeof window !== 'undefined') {
|
|
306
539
|
Object.assign(window, lib);
|
|
307
|
-
|
|
308
540
|
if (typeof window.executeQuery === 'function') {
|
|
309
|
-
|
|
310
|
-
console.log("[
|
|
541
|
+
ServicoDados.definirExecutor(window.executeQuery);
|
|
542
|
+
console.log("[JSK] Executor 'executeQuery' detectado e configurado automaticamente.");
|
|
311
543
|
}
|
|
312
544
|
}
|