vue-intergrall-plugins 1.1.90 → 1.2.18

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.
Files changed (46) hide show
  1. package/README.md +220 -220
  2. package/dist/dist/vue-intergrall-plugins.css +1 -1
  3. package/dist/vue-intergrall-plugins.esm.js +223 -89
  4. package/dist/vue-intergrall-plugins.min.js +11 -11
  5. package/dist/vue-intergrall-plugins.ssr.js +276 -147
  6. package/package.json +1 -1
  7. package/src/lib-components/Buttons/IconButton.vue +27 -27
  8. package/src/lib-components/Buttons/SimpleButton.vue +140 -140
  9. package/src/lib-components/Cards/Card.vue +490 -490
  10. package/src/lib-components/Cards/CardCheck.vue +35 -35
  11. package/src/lib-components/Cards/CardFile.vue +163 -163
  12. package/src/lib-components/Chat/BtnDownloadAllFiles.vue +36 -36
  13. package/src/lib-components/Chat/BtnEmojis.vue +118 -118
  14. package/src/lib-components/Chat/BtnExpand.vue +17 -17
  15. package/src/lib-components/Chat/BtnFiles.vue +486 -486
  16. package/src/lib-components/Chat/BtnMic.vue +60 -60
  17. package/src/lib-components/Chat/BtnScreenShare.vue +31 -31
  18. package/src/lib-components/Chat/BtnStandardMessages.vue +17 -17
  19. package/src/lib-components/Chat/ExpandTextarea.vue +427 -427
  20. package/src/lib-components/Chat/MultipleFilePreview.vue +291 -291
  21. package/src/lib-components/Chat/Picker.vue +525 -525
  22. package/src/lib-components/Chat/RemainingCharacters.vue +28 -28
  23. package/src/lib-components/Chat/SingleFilePreview.vue +94 -94
  24. package/src/lib-components/Chat/SkeletonPicker.vue +110 -110
  25. package/src/lib-components/Chat/StandardMessages.vue +252 -252
  26. package/src/lib-components/Chat/TextFooter.vue +1007 -1007
  27. package/src/lib-components/Email/EmailExpanded.vue +270 -270
  28. package/src/lib-components/Email/EmailFile.vue +192 -192
  29. package/src/lib-components/Email/EmailFrom.vue +66 -66
  30. package/src/lib-components/Email/EmailItem.vue +867 -850
  31. package/src/lib-components/Email/EmailTo.vue +64 -64
  32. package/src/lib-components/Loader/Loader.vue +78 -78
  33. package/src/lib-components/Messages/AnexoMensagem.vue +625 -497
  34. package/src/lib-components/Messages/CardAttachment.vue +61 -61
  35. package/src/lib-components/Messages/CardMessages.vue +687 -687
  36. package/src/lib-components/Messages/ChatMessages.vue +259 -78
  37. package/src/lib-components/Messages/InteratividadeBotoes.vue +197 -197
  38. package/src/lib-components/Messages/InteratividadeContato.vue +32 -32
  39. package/src/lib-components/Messages/InteratividadeContatoItem.vue +235 -235
  40. package/src/lib-components/Messages/InteratividadeFormulario.vue +334 -334
  41. package/src/lib-components/Messages/InteratividadePopup.vue +95 -95
  42. package/src/lib-components/Messages/LinkPreview.vue +176 -176
  43. package/src/lib-components/Scroll/ScrollContent.vue +166 -166
  44. package/src/lib-components/Templates/TemplateGenerator.vue +640 -640
  45. package/src/lib-components/Templates/TemplateMessage.vue +83 -83
  46. package/src/lib-components/Templates/TemplateSingle.vue +478 -478
@@ -1,851 +1,868 @@
1
- <template>
2
- <div
3
- :class="['email-item box-shadow-active', isOpen ? isOpenClass : `${isClosedClass} cursor-pointer`, showInfos ? 'info-open' : '', origem, customClass]"
4
- @click="openByItem($event)">
5
- <div v-if="showInfos" class="email-overlay"></div>
6
- <div class="email-author" v-if="autor" :class="[origem, isMainEmail ? 'main' : '']" @click.stop="toggleIsOpen"
7
- :key="autor">
8
- <span :title="statusTitle"><fa-icon :icon="['fas', status !== 'C' ? 'reply' : 'times']" :class="[origem]" /></span>
9
- <p class="email-author-name" v-text="returnAuthorName" :title="returnAuthorName"></p>
10
- <span :title="`E-mail ${origem === 'outros' ? 'recebido' : 'enviado'}`">
11
- <fa-icon :icon="['fas', origem === 'outros' ? 'user' : 'headset']" class="email-to-svg" />
12
- </span>
13
- </div>
14
- <div class="email-custom-buttons" @click.stop>
15
- <span
16
- v-if="status && status !== 'C'"
17
- class="email-status-badge email-status-enviado"
18
- v-tippy
19
- :content="`${formataDataHora(dataHora, true, false)}`"
20
- >
21
- <fa-icon :icon="['fas', 'check']" />
22
- </span>
23
- <span
24
- v-else-if="status === 'C'"
25
- class="email-status-badge email-status-nao-enviado"
26
- v-tippy
27
- :content="`${formataDataHora(dataHora, true, false)}`"
28
- >
29
- Não enviado
30
- </span>
31
- <template v-if="customButtons && customButtons.length">
32
- <span v-for="(button, index) in customButtons" :key="button.id || index" v-show="button.use"
33
- @click="button.callback ? button.callback(returnParams(button.params)) : $emit(button.emitName, returnParams(button.params))" :class="`${button.customClass || 'menu-mensagem'}`"
34
- v-tippy :content="button.tippyContent">
35
- <fa-icon :icon="['fas', button.icon || 'question-circle']" v-if="!button.svgIcon" />
36
- <span v-else v-html="button.svgIcon" :class="button.svgClass ? button.svgClass : ''"></span>
37
- </span>
38
- </template>
39
- <span
40
- v-if="status !== 'C'"
41
- class="email-pdf-download cursor-pointer"
42
- @click.stop="generatePdf"
43
- title="Baixar e-mail em PDF"
44
- >
45
- <fa-icon :icon="['fas', 'file-pdf']" />
46
- </span>
47
- <span
48
- v-if="isOpen && filteredFiles && filteredFiles.length"
49
- class="email-clip-anchor cursor-pointer"
50
- @click.stop="scrollToAttachments"
51
- :title="`${filteredFiles.length} anexos`"
52
- >
53
- <fa-icon :icon="['fas', 'paperclip']" />
54
- </span>
55
- </div>
56
- <div
57
- v-if="customActionButtons && customActionButtons.length && customActionButtons[0].type === 'ja-spam'"
58
- :key="customActionButtons[0].id"
59
- class="ja-marcado-spam"
60
- >
61
- <span class="icon-tittle-ja-spam">
62
- <fa-icon :icon="['fas', 'exclamation-triangle']" style="color:#FF0000; margin-right:8px; font-size:16px;" />
63
- {{ customActionButtons[0].label }}
64
- </span>
65
- <div @click.stop class="ja-marcado-spam-buttons">
66
- <button
67
- :disabled="customActionButtons[0].btn1.disabled"
68
- class="btn-nao-spam"
69
- @click="customActionButtons[0].btn1.callback"
70
- style="position: relative;"
71
- >
72
- <VueLoader
73
- v-if="customActionButtons[0].btn1.loading"
74
- :hasBg="false"
75
- class="btn-loader"
76
- />
77
- <span :style="{ opacity: customActionButtons[0].btn1.loading ? 0 : 1 }">
78
- {{ customActionButtons[0].btn1.label }}
79
- </span>
80
- </button>
81
- <button
82
- :disabled="customActionButtons[0].btn2.disabled"
83
- class="btn-e-spam"
84
- @click="customActionButtons[0].btn2.callback"
85
- style="position: relative;"
86
- >
87
- <VueLoader
88
- v-if="customActionButtons[0].btn2.loading"
89
- :hasBg="false"
90
- class="btn-loader"
91
- />
92
- <span :style="{ opacity: customActionButtons[0].btn2.loading ? 0 : 1 }">
93
- <fa-icon v-if="customActionButtons[0].btn2.icon"
94
- class="icon-btn-encerrar-spam"
95
- :icon="['fas', customActionButtons[0].btn2.icon === 'exit' ? 'sign-out-alt' : customActionButtons[0].btn2.icon]"
96
- style="margin-right: 6px;"
97
- />
98
- {{ customActionButtons[0].btn2.label }}
99
- </span>
100
- </button>
101
- </div>
102
- </div>
103
- <div class="email-custom-action-buttons" v-if="customActionButtons && customActionButtons.length" @click.stop>
104
- <template v-for="(button, index) in customActionButtons">
105
- <button
106
- v-if="button.type !== 'ja-spam'"
107
- :key="button.id || index"
108
- v-show="button.use"
109
- @click="button.callback(returnParams(button.params))"
110
- :class="`${button.customClass || 'email-custom-action-button'}`"
111
- v-tippy
112
- :content="button.tippyContent"
113
- >
114
- <fa-icon :icon="['fas', button.icon || 'question-circle']" v-if="!button.svgIcon" />
115
- <span v-else v-html="button.svgIcon" :class="button.svgClass ? button.svgClass : ''"></span>
116
- <span v-if="button.label" class="email-custom-action-label" v-text="button.label"></span>
117
- </button>
118
- </template>
119
- </div>
120
- <div class="email-header" @click.stop="toggleIsOpen">
121
- <p class="email-subject" :title="returnMainValue()">
122
- {{ returnMainValue() }}
123
- </p>
124
- <div
125
- class="divBtnReplyEmail" :class="[isOpen ? 'reply-email-open' : '']" v-if="status === 'C'">
126
- <strong class="strongMsg">{{ status_msg }} </strong>
127
- <button
128
- v-if="hasReplyEmail"
129
- class="btnReplyEmail"
130
- @click.stop="emitReplyEmail"
131
- ><strong>Reenviar</strong>
132
- </button>
133
- </div>
134
- <div class="header-container">
135
- <div :class="`email-header-content${isOpen ? ' open' : ''}`">
136
- <div class="email-header-infos">
137
- <template>
138
- <div v-if="para && para.length" class="email-to-from-container" ref="emailToContainer">
139
- <EmailTo v-for="(recipient, index) in para" :key="`to-${recipient.email}`"
140
- :currentName="formattedToName(recipient.name, index, para.length)" :email="recipient.email"
141
- :showMail="isOpen" />
142
- </div>
143
-
144
- <div v-if="copia && copia.length" class="email-cc-container">
145
- <EmailTo v-for="(recipient, index) in copia" :key="`cc-${recipient.email}`"
146
- :currentName="formattedCcName(recipient.name, index, copia.length)" :email="recipient.email"
147
- :showMail="isOpen" />
148
- </div>
149
- <div class="email-to-btn" v-show="isOpen" @click.stop="toggleShowInfos()">
150
- <fa-icon :icon="['fas', 'caret-down']" />
151
- </div>
152
- </template>
153
- <div v-if="!isMainEmail && isOpen" class="email-subject-secondary">
154
- <span class="email-subject-span"
155
- v-text="`Assunto: ${htmlEntityToEmoji(replaceUnicodeWithEmoji(assunto)) || '(Sem assunto)'}`"></span>
156
- </div>
157
- </div>
158
- </div>
159
- <span :class="`email-date ${isOpen ? isOpenClass : isClosedClass}`" :title="formattedDate" ref="emailDate"
160
- v-show="formattedDate">
161
- <fa-icon :icon="['fas', 'calendar']" />
162
- {{ formattedDate }}
163
- </span>
164
- </div>
165
- <ul v-if="showInfos" ref="emailToInfos" class="email-to-infos box-shadow" v-clickaway="() => showInfos = false"
166
- @click.stop>
167
- <li v-for="(info, index) in mailInfos" :key="`info-${index}`" class="email-info">
168
- <span v-text="info.label"></span>
169
- {{ info.value }}
170
- </li>
171
- </ul>
172
- <span v-if="isOpen" :class="['email-actions box-shadow', { active: actionsOpen }, { main: isMainEmail }]"
173
- @click.stop="toggleActions">
174
- <span></span>
175
- <span></span>
176
- <span></span>
177
- </span>
178
- <ul class="email-actions-list box-shadow" v-if="actionsOpen && isOpen" v-clickaway="closeActions" @click.stop>
179
- <li class="email-action" @click.stop="toggleOpenMessage">
180
- {{ !openMessage ? 'Visualizar' : 'Esconder' }} texto do e-mail (html)
181
- </li>
182
- </ul>
183
- </div>
184
- <div class="email-content" v-show="isOpen" ref="emailIframeParent">
185
- <VueLoader v-if="isIframeLoading" />
186
- <div v-if="hasError" class="email-error-content">
187
- <p>Erro ao carregar o e-mail</p>
188
- <button class="box-shadow" @click="handleTryAgain()">Tentar novamente</button>
189
- </div>
190
- <iframe v-show="!hasError" ref="emailIframe"
191
- :class="`email-html ${isIframeLoading ? 'visibility-hidden' : ''}`"></iframe>
192
- <span class="email-raw" v-if="openMessage">{{ mensagem }}</span>
193
- </div>
194
- <div v-if="filteredFiles && filteredFiles.length" class="email-files" v-show="isOpen">
195
- <EmailFile v-for="(anexo, index) in filteredFiles" :key="index" :anexo="anexo" :dominio="dominio" />
196
- </div>
197
- <div
198
- v-if="filteredFiles && filteredFiles.length && !isOpen"
199
- class="email-attachments-clip cursor-pointer"
200
- @click.stop="openAndScrollToAttachments"
201
- >
202
- <span class="clip-icon">
203
- <fa-icon :icon="['fas', 'paperclip']" />
204
- <span class="clip-badge">{{ filteredFiles.length }}</span>
205
- </span>
206
- </div>
207
- <!-- Status da mensagem -->
208
- <vue-html2pdf
209
- :show-layout="false"
210
- :enable-download="true"
211
- :preview-modal="false"
212
- :paginate-elements-by-height="1400"
213
- :filename="pdfFilename"
214
- :pdf-quality="2"
215
- ref="html2pdf"
216
- :manual-pagination="true"
217
- :html-to-pdf-options="htmlToPdfOptions"
218
- >
219
- <section slot="pdf-content" id="conteudo-pdf">
220
- <div class="pdf-content">
221
- <h1 class="pdf-title">{{ htmlEntityToEmoji(replaceUnicodeWithEmoji(assunto)) || '(Sem assunto)' }}</h1>
222
-
223
- <table class="pdf-info-table">
224
- <tbody>
225
- <tr>
226
- <th>De:</th>
227
- <td>{{ formatPdfRecipients(from) }}</td>
228
- </tr>
229
- <tr>
230
- <th>Para:</th>
231
- <td>{{ formatPdfRecipients(para) }}</td>
232
- </tr>
233
- <tr v-if="copia && copia.length">
234
- <th>Cópia:</th>
235
- <td>{{ formatPdfRecipients(copia) }}</td>
236
- </tr>
237
- <tr>
238
- <th>Data:</th>
239
- <td>{{ formataDataHora(dataHora, true, false) }} - {{ formattedDate }}</td>
240
- </tr>
241
- </tbody>
242
- </table>
243
-
244
- <hr class="pdf-divisor" />
245
-
246
- <div class="pdf-email-content">
247
- <div v-html="formatContentForPdf()"></div>
248
- </div>
249
-
250
- <div v-if="filteredFiles && filteredFiles.length" class="pdf-attachments">
251
- <h3>Anexos ({{ filteredFiles.length }}):</h3>
252
- <ul>
253
- <li v-for="(anexo, index) in filteredFiles" :key="index">
254
- {{ anexo.name || `Anexo ${index + 1}` }}
255
- </li>
256
- </ul>
257
- </div>
258
- </div>
259
- </section>
260
- </vue-html2pdf>
261
- </div>
262
- </template>
263
-
264
- <script>
265
- import EmailFile from "./EmailFile.vue";
266
- import EmailTo from "./EmailTo.vue";
267
- import { textoLongo } from "@/mixins/formatarTexto";
268
- import Clickaway from '@/directives/clickaway';
269
- import VueHtml2pdf from 'vue-html2pdf';
270
- import '../../styles/style.css';
271
-
272
- export default {
273
- mixins: [textoLongo],
274
- directives: {
275
- clickaway: Clickaway
276
- },
277
- components: { EmailFile, EmailTo, VueHtml2pdf },
278
- props: {
279
- dominio: {
280
- type: String,
281
- required: true
282
- },
283
- dicionario: {
284
- type: Object,
285
- required: true
286
- },
287
- customClass: {
288
- type: String,
289
- default: ''
290
- },
291
- from: {
292
- type: Array,
293
- default: () => []
294
- },
295
- para: {
296
- type: Array,
297
- default: () => []
298
- },
299
- copia: {
300
- type: Array,
301
- default: () => []
302
- },
303
- html: {
304
- type: String,
305
- default: ""
306
- },
307
- assunto: {
308
- type: String,
309
- default: ""
310
- },
311
- dataHora: {
312
- type: String,
313
- default: ""
314
- },
315
- mensagem: {
316
- type: String,
317
- default: ""
318
- },
319
- anexos: {
320
- type: [Array, String],
321
- default: () => []
322
- },
323
- iniciarAberto: {
324
- type: Boolean,
325
- default: false
326
- },
327
- dataServer: {
328
- type: String,
329
- default: ""
330
- },
331
- isOpenClass: {
332
- type: String,
333
- default: 'bg-dark-white-2'
334
- },
335
- isClosedClass: {
336
- type: String,
337
- default: 'bg-white'
338
- },
339
- refKey: {
340
- type: String,
341
- },
342
- showRawMessageInsideEmail: {
343
- type: Boolean,
344
- default: false
345
- },
346
- origem: {
347
- type: String,
348
- default: 'outros'
349
- },
350
- autor: {
351
- type: String
352
- },
353
- isMainEmail: {
354
- type: Boolean,
355
- default: true
356
- },
357
- customButtons: {
358
- type: Array
359
- },
360
- customActionButtons: {
361
- type: Array
362
- },
363
- status: {
364
- type: String,
365
- default: ''
366
- },
367
- status_msg: {
368
- type: String,
369
- default: ''
370
- },
371
- hasReplyEmail: {
372
- type: Boolean,
373
- default: false
374
- },
375
- id_lig: {
376
- type: String,
377
- },
378
- token_cliente: {
379
- type: String,
380
- },
381
- infos: {
382
- type: Object,
383
- default: () => ({}),
384
- },
385
- jaMarcadoSpam: {
386
- type: Boolean,
387
- default: false
388
- },
389
- msgTooltip: {
390
- type: String,
391
- default: ''
392
- }
393
- },
394
- data() {
395
- return {
396
- isOpen: this.iniciarAberto,
397
- openMessage: false,
398
- actionsOpen: false,
399
- showInfos: false,
400
- isIframeLoading: true,
401
- hasError: false,
402
- errorCount: 0,
403
- scrollToFile: false,
404
- htmlToPdfOptions: {
405
- margin: [15, 0, 15, 0],
406
- filename: `email.pdf`,
407
- image: {
408
- type: 'jpeg',
409
- quality: 0.98
410
- },
411
- enableLinks: false,
412
- html2canvas: {
413
- scale: 2,
414
- useCORS: true,
415
- letterRendering: true,
416
- logging: false,
417
- scrollY: 0,
418
- scrollX: 0
419
- },
420
- jsPDF: {
421
- unit: 'mm',
422
- format: 'a4',
423
- orientation: 'portrait',
424
- compress: true
425
- }
426
- }
427
- };
428
- },
429
- computed: {
430
- statusTitle() {
431
- if(this.status !== 'C') {
432
- return `Enviado por: ${this.autor}`
433
- }
434
-
435
- if(!this.status_msg) {
436
- return `Enviado com erro por: ${this.autor}`
437
- }
438
-
439
- return `Enviado com erro por: ${this.autor} - ${this.status_msg}`
440
- },
441
- paraCopia() {
442
- if (!this.copia || !this.copia.length) return this.para && this.para.length ? this.para : []
443
- return this.para && this.para.length ? this.para.concat(this.copia) : this.copia
444
- },
445
- formattedDate() {
446
- return this.formataDateHoraDateFns(this.dataHora, this.dataServer, this.dicionario);
447
- },
448
- contentTooltip: {
449
- get() {
450
- if(!this.status || !this.msgTooltip) return ''
451
- if (this.status && !this.msgTooltip) {
452
- const msgStatus = "msg_status_" + this.status;
453
- return (this.strTooltipAux = this.dicionario[msgStatus]);
454
- } else if (this.status && this.msgTooltip) {
455
- return (this.strTooltipAux = this.msgTooltip);
456
- }
457
- },
458
- set(msg) {
459
- return (this.contentTooltip = msg);
460
- },
461
- },
462
- mailInfos() {
463
- const formatAsString = (arr) => {
464
- let str = ''
465
- arr.forEach(({ name, email }, index) => {
466
- if (!name && !email) str += ''
467
- else str += `${name || ''} ${email ? `<${email}>` : ''}${((name || email) && index !== arr.length - 1) ? ', ' : ''}`
468
- })
469
- return str
470
- }
471
-
472
- const infos = [
473
- {
474
- label: 'De:',
475
- value: this.from && this.from.length ? formatAsString(this.from) : '--'
476
- },
477
- {
478
- label: 'Para:',
479
- value: this.para && this.para.length ? formatAsString(this.para) : '--'
480
- },
481
- {
482
- label: 'Assunto:',
483
- value: this.htmlEntityToEmoji(this.replaceUnicodeWithEmoji(this.assunto)) || '(Sem assunto)'
484
- },
485
- {
486
- label: 'Data:',
487
- value: this.formataDataHora(this.dataHora) || '--'
488
- },
489
- ]
490
-
491
- if (this.copia && this.copia.length) {
492
- infos.splice(2, 0, {
493
- label: `${this.dicionario.tit_copia}:` || 'Copia:',
494
- value: formatAsString(this.copia)
495
- })
496
- }
497
-
498
- return infos
499
- },
500
- filteredFiles() {
501
- if (!this.anexos || !this.anexos.length) return
502
-
503
- return this.anexos.filter(anexo => {
504
- return !this.html.includes(anexo.name);
505
- })
506
- },
507
- returnAuthorName() {
508
- if (!this.from || !this.from.length) return this.autor || ''
509
- const { email, name } = this.from[0]
510
- if (!name) return email
511
- if (!email) return name
512
- if (this.isOpen) return `${name} (${email})`
513
- return name
514
- },
515
- formattedToName() {
516
- return (name, index, length) => {
517
- let addVirgulaDps = index < length - 1
518
- const text = index === 0 ? `Para: ${name || ''}` : name ? `${name}${addVirgulaDps ? ', ' : ''}` : '';
519
- return text
520
- }
521
- },
522
- formattedCcName() {
523
- return (name, index, length) => {
524
- let addVirgulaDps = index < length - 1
525
- const text = index === 0 ? `${this.dicionario.tit_copia || 'Cópia'}: ${name || ''}` : name ? `${name}${addVirgulaDps ? ', ' : ''}` : '';
526
- return text
527
- }
528
- },
529
- pdfFilename() {
530
- const subject = this.assunto || 'email';
531
- const sanitized = subject.replace(/[^a-z0-9]/gi, '_').substring(0, 50);
532
- const date = this.dataHora ? new Date(this.dataHora).toISOString().split('T')[0] : '';
533
- const str = `email_${sanitized}_${date}`
534
- if(str != this.htmlToPdfOptions.filename) this.htmlToPdfOptions.filename = `${str}.pdf`;
535
- return str;
536
- }
537
- },
538
- watch: {
539
- isOpen: 'updateIframeContent'
540
- },
541
- mounted() {
542
- this.updateIframeContent();
543
- this.adjustMaxWidth()
544
-
545
- if (this.refKey) this.$root.$refs[`${refKey}`] = this
546
-
547
- setTimeout(() => {
548
- if (this.isIframeLoading) this.adjustIframeHeight()
549
- }, 15000);
550
- },
551
- methods: {
552
- openAndScrollToAttachments() {
553
- if (!this.isOpen) {
554
- this.scrollToFile = true;
555
- this.isIframeLoading = true;
556
- this.isOpen = true;
557
- } else if (!this.isIframeLoading) {
558
- this.scrollToAttachments();
559
- } else {
560
- this.scrollToFile = true;
561
- }
562
- },
563
- scrollToAttachments() {
564
- this.$nextTick(() => {
565
- const files = this.$el.querySelector('.email-files');
566
- if (files) {
567
- files.scrollIntoView({ behavior: 'smooth', block: 'center' });
568
- }
569
- });
570
- },
571
- handleTryAgain() {
572
- this.isOpen = true
573
- this.isIframeLoading = true
574
- this.hasError = false
575
-
576
- this.$nextTick(this.updateIframeContent())
577
- },
578
-
579
- emitReplyEmail() {
580
- if(!this.hasReplyEmail) return
581
-
582
- this.$emit('reply-email', {
583
- html: this.html,
584
- mensagem: this.mensagem,
585
- assunto: this.assunto,
586
- from: this.from,
587
- para: this.para,
588
- dataHora: this.dataHora,
589
- anexos: this.anexos,
590
- status: this.status,
591
- status_msg: this.status_msg,
592
- copia: this.copia,
593
- origem: this.origem,
594
- autor: this.autor,
595
- id_lig: this.id_lig,
596
- token_cliente: this.token_cliente,
597
- infos: this.infos,
598
- });
599
- },
600
-
601
- gerarMensagemEstilizada(msg) {
602
- try {
603
- const currentStyles = [
604
- { tagAbertura: "<i>", tagFechamento: "</i>", find: /_(.*?)_/g },
605
- { tagAbertura: "<b>", tagFechamento: "</b>", find: /\*(.*?)\*/g },
606
- { tagAbertura: "<del>", tagFechamento: "</del>", find: /~(.*?)~/g }
607
- ]
608
-
609
- const isLetterOrUnderscore = (char) => {
610
- return /[a-zA-Z_]/.test(char)
611
- }
612
-
613
- const isValidMatch = (message, fullMatch, startIndex) => {
614
- const beforeChar = message[startIndex - 1]
615
- const afterChar = message[startIndex + fullMatch.length]
616
- return (
617
- (beforeChar === undefined || !isLetterOrUnderscore(beforeChar)) &&
618
- (afterChar === undefined || !isLetterOrUnderscore(afterChar))
619
- )
620
- }
621
-
622
- let message = msg
623
-
624
- for (const { tagAbertura, tagFechamento, find } of currentStyles) {
625
- let match
626
- while ((match = find.exec(message)) !== null) {
627
- const fullMatch = match[0]
628
- const content = match[1]
629
- const startIndex = match.index
630
-
631
- if (isValidMatch(message, fullMatch, startIndex)) {
632
- const endIndex = startIndex + fullMatch.length
633
- const replacedContent = `${tagAbertura}${content}${tagFechamento}`
634
- message = message.slice(0, startIndex) + replacedContent + message.slice(endIndex)
635
- }
636
- }
637
- }
638
-
639
- return message
640
- } catch (err) {
641
- console.error('Erro ao gerar mensagem estilizada: ', err)
642
- return msg
643
- }
644
- },
645
- updateIframeContent() {
646
- this.$nextTick(() => {
647
- if (this.isOpen && this.isIframeLoading) {
648
- const iframe = this.$refs.emailIframe;
649
- if (!iframe) {
650
- console.warn('Iframe not found');
651
- this.isIframeLoading = false
652
- return
653
- }
654
- const doc = iframe.contentDocument || iframe.contentWindow.document;
655
- if (!doc) {
656
- console.warn('Document not found in iframe');
657
- this.isIframeLoading = false
658
- return
659
- }
660
- let content = this.html || ''
661
- if(!content || !content.length) {
662
- console.warn('No content to display in iframe');
663
- this.isIframeLoading = false
664
- return
665
- }
666
- if (!/<[a-z][\s\S]*>/i.test(content)) {
667
- content = content.replace(/\n/g, '<br>');
668
- content = this.gerarMensagemEstilizada(content)
669
- content = `<p>${content}</p>`;
670
- }
671
-
672
- doc.open();
673
- doc.write(content);
674
- doc.close();
675
-
676
- const images = doc.getElementsByTagName('img');
677
- let imagesLoaded = 0;
678
-
679
- const imageLoadHandler = () => {
680
- imagesLoaded++;
681
- if (imagesLoaded === images.length) {
682
- this.adjustIframeHeight();
683
- }
684
- };
685
-
686
- if (images.length === 0) {
687
- this.adjustIframeHeight();
688
- } else {
689
- for (let img of images) {
690
- img.addEventListener('load', imageLoadHandler);
691
- img.addEventListener('error', imageLoadHandler);
692
- }
693
- }
694
- }
695
- });
696
- },
697
- adjustIframeHeight() {
698
- this.$nextTick(() => {
699
- const iframe = this.$refs.emailIframe;
700
- const emailParent = this.$refs.emailIframeParent
701
- if (iframe && emailParent) {
702
- setTimeout(() => {
703
- const doc = iframe.contentDocument || iframe.contentWindow.document;
704
- const margin = 20
705
- const value = doc.documentElement.offsetHeight + margin
706
- if (value === 0 || doc.documentElement.querySelector('body').innerHTML === '') {
707
- this.errorCount++
708
- if (this.errorCount > 3) {
709
- this.hasError = true
710
- if (this.isIframeLoading) this.isIframeLoading = false
711
- return
712
- }
713
- this.isIframeLoading = true
714
- this.updateIframeContent()
715
- return
716
- }
717
- if (this.hasError || this.errorCount > 0) {
718
- this.hasError = false
719
- this.errorCount = 0
720
- }
721
- iframe.style.height = `${value}px`;
722
- emailParent.style.minHeight = `${value}px`
723
- if (this.scrollToFile) {
724
- this.scrollToFile = false;
725
- this.scrollToAttachments();
726
- }
727
- }, 100)
728
- }
729
-
730
- if (this.isIframeLoading) this.isIframeLoading = false
731
- });
732
- },
733
- toggleShowInfos() {
734
- this.showInfos = !this.showInfos;
735
- if (this.showInfos) {
736
- this.$nextTick(() => {
737
- this.adjustTopPosition();
738
- });
739
- }
740
- },
741
- adjustTopPosition() {
742
- const ulElement = this.$refs.emailToInfos;
743
- if (!ulElement) return
744
- const ulHeight = ulElement.offsetHeight;
745
- const maxHeight = 300
746
- const finalHeight = ulHeight < maxHeight ? ulHeight : maxHeight
747
- ulElement.style.bottom = `-${finalHeight}px`;
748
- },
749
- toggleIsOpen() {
750
- this.isOpen = !this.isOpen;
751
- if (this.isOpen) {
752
- this.isIframeLoading = true;
753
- } else {
754
- this.actionsOpen = false;
755
- this.openMessage = false;
756
- this.showInfos = false;
757
- this.scrollToFile = false;
758
- }
759
- },
760
- adjustMaxWidth() {
761
- this.$nextTick(() => {
762
- const toContainer = this.$refs.emailToContainer
763
- const dateContainer = this.$refs.emailDate
764
- if (!toContainer || !dateContainer) return
765
- toContainer.style.maxWidth = `calc(100% - ${dateContainer.offsetWidth}px)`
766
- })
767
- },
768
- toggleActions() {
769
- this.actionsOpen = !this.actionsOpen;
770
- },
771
- toggleOpenMessage() {
772
- if (this.showRawMessageInsideEmail) {
773
- this.openMessage = !this.openMessage;
774
- }
775
-
776
- this.$emit('open-message', {
777
- dicionario: this.dicionario,
778
- from: this.from,
779
- para: this.para,
780
- html: this.html,
781
- assunto: this.assunto,
782
- dataHora: this.dataHora,
783
- mensagem: this.mensagem,
784
- anexos: this.filteredFiles,
785
- dataServer: this.dataServer,
786
- dominio: this.dominio
787
- })
788
- },
789
- closeActions() {
790
- this.actionsOpen = false;
791
- },
792
- returnMainValue() {
793
- if (this.isMainEmail) return this.htmlEntityToEmoji(this.replaceUnicodeWithEmoji(this.assunto)) || '(Sem assunto)'
794
- return ''
795
- },
796
- openByItem(event) {
797
- if (this.isOpen) return;
798
-
799
- event.preventDefault();
800
- event.stopPropagation();
801
-
802
- this.toggleIsOpen();
803
- },
804
- returnParams(params) {
805
- const defaultParams = {
806
- message: this.mensagem,
807
- };
808
-
809
- if (!params) return defaultParams;
810
-
811
- const keys = params.instanceKeys.split("|");
812
- if (!keys || !keys.length)
813
- return {
814
- defaultParams,
815
- ...params.values,
816
- };
817
-
818
- const customParams = { ...params.values };
819
- keys.forEach((key) => {
820
- if (this[key]) customParams[key] = this[key];
821
- else console.warn(`Cant find ${key} on 'this' instance`);
822
- });
823
-
824
- return Object.keys(customParams).length ? customParams : defaultParams;
825
- },
826
- generatePdf() {
827
- if (this.$refs.html2pdf) {
828
- this.$refs.html2pdf.generatePdf();
829
- }
830
- },
831
- formatPdfRecipients(recipients) {
832
- if (!recipients || !recipients.length) return '--';
833
- return recipients.map(r => {
834
- if (r.name && r.email) return `${r.name} <${r.email}>`;
835
- return r.name || r.email || '';
836
- }).join(', ');
837
- },
838
- formatContentForPdf() {
839
- let content = this.html || this.mensagem || '';
840
-
841
- if (!/<[a-z][\s\S]*>/i.test(content)) {
842
- content = content.replace(/\n/g, '<br>');
843
- content = this.gerarMensagemEstilizada(content);
844
- content = `<p>${content}</p>`;
845
- }
846
-
847
- return content;
848
- }
849
- }
850
- };
1
+ <template>
2
+ <div
3
+ :class="['email-item box-shadow-active', isOpen ? isOpenClass : `${isClosedClass} cursor-pointer`, showInfos ? 'info-open' : '', origem, customClass]"
4
+ @click="openByItem($event)">
5
+ <div v-if="showInfos" class="email-overlay"></div>
6
+ <div class="email-author" v-if="autor" :class="[origem, isMainEmail ? 'main' : '']" @click.stop="toggleIsOpen"
7
+ :key="autor">
8
+ <span :title="statusTitle"><fa-icon :icon="['fas', status !== 'C' ? 'reply' : 'times']" :class="[origem]" /></span>
9
+ <p class="email-author-name" v-text="returnAuthorName" :title="returnAuthorName"></p>
10
+ <span :title="`E-mail ${origem === 'outros' ? 'recebido' : 'enviado'}`">
11
+ <fa-icon :icon="['fas', origem === 'outros' ? 'user' : 'headset']" class="email-to-svg" />
12
+ </span>
13
+ </div>
14
+ <div class="email-custom-buttons" @click.stop>
15
+ <template v-if="customButtons && customButtons.length">
16
+ <span v-for="(button, index) in customButtons" :key="button.id || index" v-show="button.use"
17
+ @click="button.callback ? button.callback(returnParams(button.params)) : $emit(button.emitName, returnParams(button.params))" :class="`${button.customClass || 'menu-mensagem'}`"
18
+ v-tippy :content="button.tippyContent">
19
+ <fa-icon :icon="['fas', button.icon || 'question-circle']" v-if="!button.svgIcon" />
20
+ <span v-else v-html="button.svgIcon" :class="button.svgClass ? button.svgClass : ''"></span>
21
+ </span>
22
+ </template>
23
+ <span
24
+ v-if="status !== 'C'"
25
+ class="email-pdf-download cursor-pointer"
26
+ @click.stop="generatePdf"
27
+ title="Baixar e-mail em PDF"
28
+ >
29
+ <fa-icon :icon="['fas', 'file-pdf']" />
30
+ </span>
31
+ <span
32
+ v-if="isOpen && filteredFiles && filteredFiles.length"
33
+ class="email-clip-anchor cursor-pointer"
34
+ @click.stop="scrollToAttachments"
35
+ :title="`${filteredFiles.length} anexos`"
36
+ >
37
+ <fa-icon :icon="['fas', 'paperclip']" />
38
+ </span>
39
+ </div>
40
+ <div
41
+ v-if="customActionButtons && customActionButtons.length && customActionButtons[0].type === 'ja-spam'"
42
+ :key="customActionButtons[0].id"
43
+ class="ja-marcado-spam"
44
+ >
45
+ <span class="icon-tittle-ja-spam">
46
+ <fa-icon :icon="['fas', 'exclamation-triangle']" style="color:#FF0000; margin-right:8px; font-size:16px;" />
47
+ {{ customActionButtons[0].label }}
48
+ </span>
49
+ <div @click.stop class="ja-marcado-spam-buttons">
50
+ <button
51
+ :disabled="customActionButtons[0].btn1.disabled"
52
+ class="btn-nao-spam"
53
+ @click="customActionButtons[0].btn1.callback"
54
+ style="position: relative;"
55
+ >
56
+ <VueLoader
57
+ v-if="customActionButtons[0].btn1.loading"
58
+ :hasBg="false"
59
+ class="btn-loader"
60
+ />
61
+ <span :style="{ opacity: customActionButtons[0].btn1.loading ? 0 : 1 }">
62
+ {{ customActionButtons[0].btn1.label }}
63
+ </span>
64
+ </button>
65
+ <button
66
+ :disabled="customActionButtons[0].btn2.disabled"
67
+ class="btn-e-spam"
68
+ @click="customActionButtons[0].btn2.callback"
69
+ style="position: relative;"
70
+ >
71
+ <VueLoader
72
+ v-if="customActionButtons[0].btn2.loading"
73
+ :hasBg="false"
74
+ class="btn-loader"
75
+ />
76
+ <span :style="{ opacity: customActionButtons[0].btn2.loading ? 0 : 1 }">
77
+ <fa-icon v-if="customActionButtons[0].btn2.icon"
78
+ class="icon-btn-encerrar-spam"
79
+ :icon="['fas', customActionButtons[0].btn2.icon === 'exit' ? 'sign-out-alt' : customActionButtons[0].btn2.icon]"
80
+ style="margin-right: 6px;"
81
+ />
82
+ {{ customActionButtons[0].btn2.label }}
83
+ </span>
84
+ </button>
85
+ </div>
86
+ </div>
87
+ <div class="email-custom-action-buttons" v-if="customActionButtons && customActionButtons.length" @click.stop>
88
+ <template v-for="(button, index) in customActionButtons">
89
+ <button
90
+ v-if="button.type !== 'ja-spam'"
91
+ :key="button.id || index"
92
+ v-show="button.use"
93
+ @click="button.callback(returnParams(button.params))"
94
+ :class="`${button.customClass || 'email-custom-action-button'}`"
95
+ v-tippy
96
+ :content="button.tippyContent"
97
+ >
98
+ <fa-icon :icon="['fas', button.icon || 'question-circle']" v-if="!button.svgIcon" />
99
+ <span v-else v-html="button.svgIcon" :class="button.svgClass ? button.svgClass : ''"></span>
100
+ <span v-if="button.label" class="email-custom-action-label" v-text="button.label"></span>
101
+ </button>
102
+ </template>
103
+ </div>
104
+ <div class="email-header" @click.stop="toggleIsOpen">
105
+ <p class="email-subject" :title="returnMainValue()">
106
+ {{ returnMainValue() }}
107
+ </p>
108
+ <div
109
+ class="divBtnReplyEmail" :class="[isOpen ? 'reply-email-open' : '']" v-if="status === 'C'">
110
+ <strong class="strongMsg">{{ status_msg }} </strong>
111
+ <button
112
+ v-if="hasReplyEmail"
113
+ class="btnReplyEmail"
114
+ @click.stop="emitReplyEmail"
115
+ ><strong>Reenviar</strong>
116
+ </button>
117
+ </div>
118
+ <div class="header-container">
119
+ <div :class="`email-header-content${isOpen ? ' open' : ''}`">
120
+ <div class="email-header-infos">
121
+ <template>
122
+ <div v-if="para && para.length" class="email-to-from-container" ref="emailToContainer">
123
+ <EmailTo v-for="(recipient, index) in para" :key="`to-${recipient.email}`"
124
+ :currentName="formattedToName(recipient.name, index, para.length)" :email="recipient.email"
125
+ :showMail="isOpen" />
126
+ </div>
127
+
128
+ <div v-if="copia && copia.length" class="email-cc-container">
129
+ <EmailTo v-for="(recipient, index) in copia" :key="`cc-${recipient.email}`"
130
+ :currentName="formattedCcName(recipient.name, index, copia.length)" :email="recipient.email"
131
+ :showMail="isOpen" />
132
+ </div>
133
+ <div class="email-to-btn" v-show="isOpen" @click.stop="toggleShowInfos()">
134
+ <fa-icon :icon="['fas', 'caret-down']" />
135
+ </div>
136
+ </template>
137
+ <div v-if="!isMainEmail && isOpen" class="email-subject-secondary">
138
+ <span class="email-subject-span"
139
+ v-text="`Assunto: ${htmlEntityToEmoji(replaceUnicodeWithEmoji(assunto)) || '(Sem assunto)'}`"></span>
140
+ </div>
141
+ </div>
142
+ </div>
143
+ <span :class="`email-date ${isOpen ? isOpenClass : isClosedClass}`" :title="formattedDate" ref="emailDate"
144
+ v-show="formattedDate">
145
+ <fa-icon :icon="['fas', 'calendar']" />
146
+ {{ formattedDate }}
147
+ </span>
148
+ </div>
149
+ <ul v-if="showInfos" ref="emailToInfos" class="email-to-infos box-shadow" v-clickaway="() => showInfos = false"
150
+ @click.stop>
151
+ <li v-for="(info, index) in mailInfos" :key="`info-${index}`" class="email-info">
152
+ <span v-text="info.label"></span>
153
+ {{ info.value }}
154
+ </li>
155
+ </ul>
156
+ <span v-if="isOpen" :class="['email-actions box-shadow', { active: actionsOpen }, { main: isMainEmail }]"
157
+ @click.stop="toggleActions">
158
+ <span></span>
159
+ <span></span>
160
+ <span></span>
161
+ </span>
162
+ <ul class="email-actions-list box-shadow" v-if="actionsOpen && isOpen" v-clickaway="closeActions" @click.stop>
163
+ <li class="email-action" @click.stop="toggleOpenMessage">
164
+ {{ !openMessage ? 'Visualizar' : 'Esconder' }} texto do e-mail (html)
165
+ </li>
166
+ </ul>
167
+ </div>
168
+ <div class="email-content" v-show="isOpen" ref="emailIframeParent">
169
+ <VueLoader v-if="isIframeLoading" />
170
+ <div v-if="hasError" class="email-error-content">
171
+ <p>Erro ao carregar o e-mail</p>
172
+ <button class="box-shadow" @click="handleTryAgain()">Tentar novamente</button>
173
+ </div>
174
+ <iframe v-show="!hasError" ref="emailIframe"
175
+ :class="`email-html ${isIframeLoading ? 'visibility-hidden' : ''}`"></iframe>
176
+ <span class="email-raw" v-if="openMessage">{{ mensagem }}</span>
177
+ </div>
178
+ <div v-if="filteredFiles && filteredFiles.length" class="email-files" v-show="isOpen">
179
+ <EmailFile v-for="(anexo, index) in filteredFiles" :key="index" :anexo="anexo" :dominio="dominio" />
180
+ </div>
181
+ <div
182
+ v-if="filteredFiles && filteredFiles.length && !isOpen"
183
+ class="email-attachments-clip cursor-pointer"
184
+ @click.stop="openAndScrollToAttachments"
185
+ >
186
+ <span class="clip-icon">
187
+ <fa-icon :icon="['fas', 'paperclip']" />
188
+ <span class="clip-badge">{{ filteredFiles.length }}</span>
189
+ </span>
190
+ </div>
191
+ <!-- Status da mensagem -->
192
+ <transition name="fade" mode="out-in">
193
+ <span class="check" v-if="status == 'D'" :content="contentTooltip" v-tippy key="check-padrao">
194
+ <fa-icon :icon="['fas', 'check']" />
195
+ </span>
196
+ <span class="check cinza" v-else-if="status == 'Q'" :content="contentTooltip" v-tippy key="check-cinza">
197
+ <fa-icon :icon="['fas', 'check']" />
198
+ </span>
199
+ <span class="check preto" v-else-if="status == 'G'" :content="contentTooltip" v-tippy key="check-preto">
200
+ <fa-icon :icon="['fas', 'check']" />
201
+ </span>
202
+ <span class="check preto" v-else-if="status == 'E'" :content="contentTooltip" v-tippy key="double-check-preto">
203
+ <fa-icon :icon="['fas', 'check-double']" />
204
+ </span>
205
+ <span class="check visualizado" v-else-if="status == 'L'" :content="contentTooltip" v-tippy
206
+ key="double-check-visualizado">
207
+ <fa-icon :icon="['fas', 'check-double']" />
208
+ </span>
209
+ <!-- Status finalizador -->
210
+ <span class="check vermelho" v-else-if="status == 'C' || status == 'ERRO'" :content="contentTooltip" v-tippy
211
+ key="times-circle">
212
+ <fa-icon :icon="['fas', 'times-circle']" />
213
+ </span>
214
+ <!-- Status finalizador -->
215
+ <span class="check vermelho" v-else-if="status == 'T'" :content="contentTooltip" v-tippy key="hourglass">
216
+ <fa-icon :icon="['fas', 'hourglass']" />
217
+ </span>
218
+ <!-- Status finalizador -->
219
+ <span class="check vermelho" v-else-if="status == 'O'" :content="contentTooltip" v-tippy key="deleted">
220
+ <fa-icon :icon="['fas', 'times']" />
221
+ </span>
222
+ <!-- Status de mensagem deletada -->
223
+ </transition>
224
+ <vue-html2pdf
225
+ :show-layout="false"
226
+ :enable-download="true"
227
+ :preview-modal="false"
228
+ :paginate-elements-by-height="1400"
229
+ :filename="pdfFilename"
230
+ :pdf-quality="2"
231
+ ref="html2pdf"
232
+ :manual-pagination="true"
233
+ :html-to-pdf-options="htmlToPdfOptions"
234
+ :key="htmlToPdfOptions.filename"
235
+ >
236
+ <section slot="pdf-content" id="conteudo-pdf">
237
+ <div class="pdf-content">
238
+ <h1 class="pdf-title">{{ htmlEntityToEmoji(replaceUnicodeWithEmoji(assunto)) || '(Sem assunto)' }}</h1>
239
+
240
+ <table class="pdf-info-table">
241
+ <tbody>
242
+ <tr>
243
+ <th>De:</th>
244
+ <td>{{ formatPdfRecipients(from) }}</td>
245
+ </tr>
246
+ <tr>
247
+ <th>Para:</th>
248
+ <td>{{ formatPdfRecipients(para) }}</td>
249
+ </tr>
250
+ <tr v-if="copia && copia.length">
251
+ <th>Cópia:</th>
252
+ <td>{{ formatPdfRecipients(copia) }}</td>
253
+ </tr>
254
+ <tr>
255
+ <th>Data:</th>
256
+ <td>{{ formataDataHora(dataHora, true, false) }} - {{ formattedDate }}</td>
257
+ </tr>
258
+ </tbody>
259
+ </table>
260
+
261
+ <hr class="pdf-divisor" />
262
+
263
+ <div class="pdf-email-content">
264
+ <div v-html="formatContentForPdf()"></div>
265
+ </div>
266
+
267
+ <div v-if="filteredFiles && filteredFiles.length" class="pdf-attachments">
268
+ <h3>Anexos ({{ filteredFiles.length }}):</h3>
269
+ <ul>
270
+ <li v-for="(anexo, index) in filteredFiles" :key="index">
271
+ {{ anexo.name || `Anexo ${index + 1}` }}
272
+ </li>
273
+ </ul>
274
+ </div>
275
+ </div>
276
+ </section>
277
+ </vue-html2pdf>
278
+ </div>
279
+ </template>
280
+
281
+ <script>
282
+ import EmailFile from "./EmailFile.vue";
283
+ import EmailTo from "./EmailTo.vue";
284
+ import { textoLongo } from "@/mixins/formatarTexto";
285
+ import Clickaway from '@/directives/clickaway';
286
+ import VueHtml2pdf from 'vue-html2pdf';
287
+ import '../../styles/style.css';
288
+
289
+ export default {
290
+ mixins: [textoLongo],
291
+ directives: {
292
+ clickaway: Clickaway
293
+ },
294
+ components: { EmailFile, EmailTo, VueHtml2pdf },
295
+ props: {
296
+ dominio: {
297
+ type: String,
298
+ required: true
299
+ },
300
+ dicionario: {
301
+ type: Object,
302
+ required: true
303
+ },
304
+ customClass: {
305
+ type: String,
306
+ default: ''
307
+ },
308
+ from: {
309
+ type: Array,
310
+ default: () => []
311
+ },
312
+ para: {
313
+ type: Array,
314
+ default: () => []
315
+ },
316
+ copia: {
317
+ type: Array,
318
+ default: () => []
319
+ },
320
+ html: {
321
+ type: String,
322
+ default: ""
323
+ },
324
+ assunto: {
325
+ type: String,
326
+ default: ""
327
+ },
328
+ dataHora: {
329
+ type: String,
330
+ default: ""
331
+ },
332
+ mensagem: {
333
+ type: String,
334
+ default: ""
335
+ },
336
+ anexos: {
337
+ type: [Array, String],
338
+ default: () => []
339
+ },
340
+ iniciarAberto: {
341
+ type: Boolean,
342
+ default: false
343
+ },
344
+ dataServer: {
345
+ type: String,
346
+ default: ""
347
+ },
348
+ isOpenClass: {
349
+ type: String,
350
+ default: 'bg-dark-white-2'
351
+ },
352
+ isClosedClass: {
353
+ type: String,
354
+ default: 'bg-white'
355
+ },
356
+ refKey: {
357
+ type: String,
358
+ },
359
+ showRawMessageInsideEmail: {
360
+ type: Boolean,
361
+ default: false
362
+ },
363
+ origem: {
364
+ type: String,
365
+ default: 'outros'
366
+ },
367
+ autor: {
368
+ type: String
369
+ },
370
+ isMainEmail: {
371
+ type: Boolean,
372
+ default: true
373
+ },
374
+ customButtons: {
375
+ type: Array
376
+ },
377
+ customActionButtons: {
378
+ type: Array
379
+ },
380
+ status: {
381
+ type: String,
382
+ default: ''
383
+ },
384
+ status_msg: {
385
+ type: String,
386
+ default: ''
387
+ },
388
+ hasReplyEmail: {
389
+ type: Boolean,
390
+ default: false
391
+ },
392
+ id_lig: {
393
+ type: String,
394
+ },
395
+ token_cliente: {
396
+ type: String,
397
+ },
398
+ infos: {
399
+ type: Object,
400
+ default: () => ({}),
401
+ },
402
+ jaMarcadoSpam: {
403
+ type: Boolean,
404
+ default: false
405
+ },
406
+ msgTooltip: {
407
+ type: String,
408
+ default: ''
409
+ }
410
+ },
411
+ data() {
412
+ return {
413
+ isOpen: this.iniciarAberto,
414
+ openMessage: false,
415
+ actionsOpen: false,
416
+ showInfos: false,
417
+ isIframeLoading: true,
418
+ hasError: false,
419
+ errorCount: 0,
420
+ scrollToFile: false,
421
+ htmlToPdfOptions: {
422
+ margin: [15, 0, 15, 0],
423
+ filename: `email.pdf`,
424
+ image: {
425
+ type: 'jpeg',
426
+ quality: 0.98
427
+ },
428
+ enableLinks: false,
429
+ html2canvas: {
430
+ scale: 2,
431
+ useCORS: true,
432
+ letterRendering: true,
433
+ logging: false,
434
+ scrollY: 0,
435
+ scrollX: 0
436
+ },
437
+ jsPDF: {
438
+ unit: 'mm',
439
+ format: 'a4',
440
+ orientation: 'portrait',
441
+ compress: true
442
+ }
443
+ }
444
+ };
445
+ },
446
+ computed: {
447
+ statusTitle() {
448
+ if(this.status !== 'C') {
449
+ return `Enviado por: ${this.autor}`
450
+ }
451
+
452
+ if(!this.status_msg) {
453
+ return `Enviado com erro por: ${this.autor}`
454
+ }
455
+
456
+ return `Enviado com erro por: ${this.autor} - ${this.status_msg}`
457
+ },
458
+ paraCopia() {
459
+ if (!this.copia || !this.copia.length) return this.para && this.para.length ? this.para : []
460
+ return this.para && this.para.length ? this.para.concat(this.copia) : this.copia
461
+ },
462
+ formattedDate() {
463
+ return this.formataDateHoraDateFns(this.dataHora, this.dataServer, this.dicionario);
464
+ },
465
+ contentTooltip: {
466
+ get() {
467
+ if(!this.status || !this.msgTooltip) return ''
468
+ if (this.status && !this.msgTooltip) {
469
+ const msgStatus = "msg_status_" + this.status;
470
+ return (this.strTooltipAux = this.dicionario[msgStatus]);
471
+ } else if (this.status && this.msgTooltip) {
472
+ return (this.strTooltipAux = this.msgTooltip);
473
+ }
474
+ },
475
+ set(msg) {
476
+ return (this.contentTooltip = msg);
477
+ },
478
+ },
479
+ mailInfos() {
480
+ const formatAsString = (arr) => {
481
+ let str = ''
482
+ arr.forEach(({ name, email }, index) => {
483
+ if (!name && !email) str += ''
484
+ else str += `${name || ''} ${email ? `<${email}>` : ''}${((name || email) && index !== arr.length - 1) ? ', ' : ''}`
485
+ })
486
+ return str
487
+ }
488
+
489
+ const infos = [
490
+ {
491
+ label: 'De:',
492
+ value: this.from && this.from.length ? formatAsString(this.from) : '--'
493
+ },
494
+ {
495
+ label: 'Para:',
496
+ value: this.para && this.para.length ? formatAsString(this.para) : '--'
497
+ },
498
+ {
499
+ label: 'Assunto:',
500
+ value: this.htmlEntityToEmoji(this.replaceUnicodeWithEmoji(this.assunto)) || '(Sem assunto)'
501
+ },
502
+ {
503
+ label: 'Data:',
504
+ value: this.formataDataHora(this.dataHora) || '--'
505
+ },
506
+ ]
507
+
508
+ if (this.copia && this.copia.length) {
509
+ infos.splice(2, 0, {
510
+ label: `${this.dicionario.tit_copia}:` || 'Copia:',
511
+ value: formatAsString(this.copia)
512
+ })
513
+ }
514
+
515
+ return infos
516
+ },
517
+ filteredFiles() {
518
+ if (!this.anexos || !this.anexos.length) return
519
+
520
+ return this.anexos.filter(anexo => {
521
+ return !this.html.includes(anexo.name);
522
+ })
523
+ },
524
+ returnAuthorName() {
525
+ if (!this.from || !this.from.length) return this.autor || ''
526
+ const { email, name } = this.from[0]
527
+ if (!name) return email
528
+ if (!email) return name
529
+ if (this.isOpen) return `${name} (${email})`
530
+ return name
531
+ },
532
+ formattedToName() {
533
+ return (name, index, length) => {
534
+ let addVirgulaDps = index < length - 1
535
+ const text = index === 0 ? `Para: ${name || ''}` : name ? `${name}${addVirgulaDps ? ', ' : ''}` : '';
536
+ return text
537
+ }
538
+ },
539
+ formattedCcName() {
540
+ return (name, index, length) => {
541
+ let addVirgulaDps = index < length - 1
542
+ const text = index === 0 ? `${this.dicionario.tit_copia || 'Cópia'}: ${name || ''}` : name ? `${name}${addVirgulaDps ? ', ' : ''}` : '';
543
+ return text
544
+ }
545
+ },
546
+ pdfFilename() {
547
+ const subject = this.assunto || 'email';
548
+ const sanitized = subject.replace(/[^a-z0-9]/gi, '_').substring(0, 50);
549
+ const date = this.dataHora ? new Date(this.dataHora).toISOString().split('T')[0] : '';
550
+ const str = `email_${sanitized}_${date}`
551
+ if(str != this.htmlToPdfOptions.filename) this.htmlToPdfOptions.filename = `${str}.pdf`;
552
+ return str;
553
+ }
554
+ },
555
+ watch: {
556
+ isOpen: 'updateIframeContent'
557
+ },
558
+ mounted() {
559
+ this.updateIframeContent();
560
+ this.adjustMaxWidth()
561
+
562
+ if (this.refKey) this.$root.$refs[`${refKey}`] = this
563
+
564
+ setTimeout(() => {
565
+ if (this.isIframeLoading) this.adjustIframeHeight()
566
+ }, 15000);
567
+ },
568
+ methods: {
569
+ openAndScrollToAttachments() {
570
+ if (!this.isOpen) {
571
+ this.scrollToFile = true;
572
+ this.isIframeLoading = true;
573
+ this.isOpen = true;
574
+ } else if (!this.isIframeLoading) {
575
+ this.scrollToAttachments();
576
+ } else {
577
+ this.scrollToFile = true;
578
+ }
579
+ },
580
+ scrollToAttachments() {
581
+ this.$nextTick(() => {
582
+ const files = this.$el.querySelector('.email-files');
583
+ if (files) {
584
+ files.scrollIntoView({ behavior: 'smooth', block: 'center' });
585
+ }
586
+ });
587
+ },
588
+ handleTryAgain() {
589
+ this.isOpen = true
590
+ this.isIframeLoading = true
591
+ this.hasError = false
592
+
593
+ this.$nextTick(this.updateIframeContent())
594
+ },
595
+
596
+ emitReplyEmail() {
597
+ if(!this.hasReplyEmail) return
598
+
599
+ this.$emit('reply-email', {
600
+ html: this.html,
601
+ mensagem: this.mensagem,
602
+ assunto: this.assunto,
603
+ from: this.from,
604
+ para: this.para,
605
+ dataHora: this.dataHora,
606
+ anexos: this.anexos,
607
+ status: this.status,
608
+ status_msg: this.status_msg,
609
+ copia: this.copia,
610
+ origem: this.origem,
611
+ autor: this.autor,
612
+ id_lig: this.id_lig,
613
+ token_cliente: this.token_cliente,
614
+ infos: this.infos,
615
+ });
616
+ },
617
+
618
+ gerarMensagemEstilizada(msg) {
619
+ try {
620
+ const currentStyles = [
621
+ { tagAbertura: "<i>", tagFechamento: "</i>", find: /_(.*?)_/g },
622
+ { tagAbertura: "<b>", tagFechamento: "</b>", find: /\*(.*?)\*/g },
623
+ { tagAbertura: "<del>", tagFechamento: "</del>", find: /~(.*?)~/g }
624
+ ]
625
+
626
+ const isLetterOrUnderscore = (char) => {
627
+ return /[a-zA-Z_]/.test(char)
628
+ }
629
+
630
+ const isValidMatch = (message, fullMatch, startIndex) => {
631
+ const beforeChar = message[startIndex - 1]
632
+ const afterChar = message[startIndex + fullMatch.length]
633
+ return (
634
+ (beforeChar === undefined || !isLetterOrUnderscore(beforeChar)) &&
635
+ (afterChar === undefined || !isLetterOrUnderscore(afterChar))
636
+ )
637
+ }
638
+
639
+ let message = msg
640
+
641
+ for (const { tagAbertura, tagFechamento, find } of currentStyles) {
642
+ let match
643
+ while ((match = find.exec(message)) !== null) {
644
+ const fullMatch = match[0]
645
+ const content = match[1]
646
+ const startIndex = match.index
647
+
648
+ if (isValidMatch(message, fullMatch, startIndex)) {
649
+ const endIndex = startIndex + fullMatch.length
650
+ const replacedContent = `${tagAbertura}${content}${tagFechamento}`
651
+ message = message.slice(0, startIndex) + replacedContent + message.slice(endIndex)
652
+ }
653
+ }
654
+ }
655
+
656
+ return message
657
+ } catch (err) {
658
+ console.error('Erro ao gerar mensagem estilizada: ', err)
659
+ return msg
660
+ }
661
+ },
662
+ updateIframeContent() {
663
+ this.$nextTick(() => {
664
+ if (this.isOpen && this.isIframeLoading) {
665
+ const iframe = this.$refs.emailIframe;
666
+ if (!iframe) {
667
+ console.warn('Iframe not found');
668
+ this.isIframeLoading = false
669
+ return
670
+ }
671
+ const doc = iframe.contentDocument || iframe.contentWindow.document;
672
+ if (!doc) {
673
+ console.warn('Document not found in iframe');
674
+ this.isIframeLoading = false
675
+ return
676
+ }
677
+ let content = this.html || ''
678
+ if(!content || !content.length) {
679
+ console.warn('No content to display in iframe');
680
+ this.isIframeLoading = false
681
+ return
682
+ }
683
+ if (!/<[a-z][\s\S]*>/i.test(content)) {
684
+ content = content.replace(/\n/g, '<br>');
685
+ content = this.gerarMensagemEstilizada(content)
686
+ content = `<p>${content}</p>`;
687
+ }
688
+
689
+ doc.open();
690
+ doc.write(content);
691
+ doc.close();
692
+
693
+ const images = doc.getElementsByTagName('img');
694
+ let imagesLoaded = 0;
695
+
696
+ const imageLoadHandler = () => {
697
+ imagesLoaded++;
698
+ if (imagesLoaded === images.length) {
699
+ this.adjustIframeHeight();
700
+ }
701
+ };
702
+
703
+ if (images.length === 0) {
704
+ this.adjustIframeHeight();
705
+ } else {
706
+ for (let img of images) {
707
+ img.addEventListener('load', imageLoadHandler);
708
+ img.addEventListener('error', imageLoadHandler);
709
+ }
710
+ }
711
+ }
712
+ });
713
+ },
714
+ adjustIframeHeight() {
715
+ this.$nextTick(() => {
716
+ const iframe = this.$refs.emailIframe;
717
+ const emailParent = this.$refs.emailIframeParent
718
+ if (iframe && emailParent) {
719
+ setTimeout(() => {
720
+ const doc = iframe.contentDocument || iframe.contentWindow.document;
721
+ const margin = 20
722
+ const value = doc.documentElement.offsetHeight + margin
723
+ if (value === 0 || doc.documentElement.querySelector('body').innerHTML === '') {
724
+ this.errorCount++
725
+ if (this.errorCount > 3) {
726
+ this.hasError = true
727
+ if (this.isIframeLoading) this.isIframeLoading = false
728
+ return
729
+ }
730
+ this.isIframeLoading = true
731
+ this.updateIframeContent()
732
+ return
733
+ }
734
+ if (this.hasError || this.errorCount > 0) {
735
+ this.hasError = false
736
+ this.errorCount = 0
737
+ }
738
+ iframe.style.height = `${value}px`;
739
+ emailParent.style.minHeight = `${value}px`
740
+ if (this.scrollToFile) {
741
+ this.scrollToFile = false;
742
+ this.scrollToAttachments();
743
+ }
744
+ }, 100)
745
+ }
746
+
747
+ if (this.isIframeLoading) this.isIframeLoading = false
748
+ });
749
+ },
750
+ toggleShowInfos() {
751
+ this.showInfos = !this.showInfos;
752
+ if (this.showInfos) {
753
+ this.$nextTick(() => {
754
+ this.adjustTopPosition();
755
+ });
756
+ }
757
+ },
758
+ adjustTopPosition() {
759
+ const ulElement = this.$refs.emailToInfos;
760
+ if (!ulElement) return
761
+ const ulHeight = ulElement.offsetHeight;
762
+ const maxHeight = 300
763
+ const finalHeight = ulHeight < maxHeight ? ulHeight : maxHeight
764
+ ulElement.style.bottom = `-${finalHeight}px`;
765
+ },
766
+ toggleIsOpen() {
767
+ this.isOpen = !this.isOpen;
768
+ if (this.isOpen) {
769
+ this.isIframeLoading = true;
770
+ } else {
771
+ this.actionsOpen = false;
772
+ this.openMessage = false;
773
+ this.showInfos = false;
774
+ this.scrollToFile = false;
775
+ }
776
+ },
777
+ adjustMaxWidth() {
778
+ this.$nextTick(() => {
779
+ const toContainer = this.$refs.emailToContainer
780
+ const dateContainer = this.$refs.emailDate
781
+ if (!toContainer || !dateContainer) return
782
+ toContainer.style.maxWidth = `calc(100% - ${dateContainer.offsetWidth}px)`
783
+ })
784
+ },
785
+ toggleActions() {
786
+ this.actionsOpen = !this.actionsOpen;
787
+ },
788
+ toggleOpenMessage() {
789
+ if (this.showRawMessageInsideEmail) {
790
+ this.openMessage = !this.openMessage;
791
+ }
792
+
793
+ this.$emit('open-message', {
794
+ dicionario: this.dicionario,
795
+ from: this.from,
796
+ para: this.para,
797
+ html: this.html,
798
+ assunto: this.assunto,
799
+ dataHora: this.dataHora,
800
+ mensagem: this.mensagem,
801
+ anexos: this.filteredFiles,
802
+ dataServer: this.dataServer,
803
+ dominio: this.dominio
804
+ })
805
+ },
806
+ closeActions() {
807
+ this.actionsOpen = false;
808
+ },
809
+ returnMainValue() {
810
+ if (this.isMainEmail) return this.htmlEntityToEmoji(this.replaceUnicodeWithEmoji(this.assunto)) || '(Sem assunto)'
811
+ return ''
812
+ },
813
+ openByItem(event) {
814
+ if (this.isOpen) return;
815
+
816
+ event.preventDefault();
817
+ event.stopPropagation();
818
+
819
+ this.toggleIsOpen();
820
+ },
821
+ returnParams(params) {
822
+ const defaultParams = {
823
+ message: this.mensagem,
824
+ };
825
+
826
+ if (!params) return defaultParams;
827
+
828
+ const keys = params.instanceKeys.split("|");
829
+ if (!keys || !keys.length)
830
+ return {
831
+ defaultParams,
832
+ ...params.values,
833
+ };
834
+
835
+ const customParams = { ...params.values };
836
+ keys.forEach((key) => {
837
+ if (this[key]) customParams[key] = this[key];
838
+ else console.warn(`Cant find ${key} on 'this' instance`);
839
+ });
840
+
841
+ return Object.keys(customParams).length ? customParams : defaultParams;
842
+ },
843
+ generatePdf() {
844
+ if (this.$refs.html2pdf) {
845
+ this.$refs.html2pdf.generatePdf();
846
+ }
847
+ },
848
+ formatPdfRecipients(recipients) {
849
+ if (!recipients || !recipients.length) return '--';
850
+ return recipients.map(r => {
851
+ if (r.name && r.email) return `${r.name} <${r.email}>`;
852
+ return r.name || r.email || '';
853
+ }).join(', ');
854
+ },
855
+ formatContentForPdf() {
856
+ let content = this.html || this.mensagem || '';
857
+
858
+ if (!/<[a-z][\s\S]*>/i.test(content)) {
859
+ content = content.replace(/\n/g, '<br>');
860
+ content = this.gerarMensagemEstilizada(content);
861
+ content = `<p>${content}</p>`;
862
+ }
863
+
864
+ return content;
865
+ }
866
+ }
867
+ };
851
868
  </script>