vue-intergrall-plugins 1.1.60 → 1.1.62

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