新建邮件弹窗页面改用jeditor渲染信息字段
This commit is contained in:
parent
c7d6f976c0
commit
2bff091e92
@ -93,9 +93,12 @@
|
||||
|
||||
<!-- Rich Text Body -->
|
||||
<div class="compose-field compose-body">
|
||||
<label class="field-label">{{ t('Message') }}</label>
|
||||
<div class="text-editor-wrapper">
|
||||
<div ref="editorHost"></div>
|
||||
<div class="message-editor-wrapper">
|
||||
<Jeditor
|
||||
v-model="editorContent"
|
||||
:df="{ fieldname: 'message', fieldtype: 'Jeditor', label: t('Message') }"
|
||||
:disabled="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -174,16 +177,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, nextTick, onBeforeUnmount, watch } from 'vue'
|
||||
import { ref, reactive, computed, nextTick, watch } from 'vue'
|
||||
import { NDrawer, NDrawerContent, NSelect, NInput, NButton, NCheckbox, NDatePicker, useMessage } from 'naive-ui'
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { api } from '@/shared/api/common'
|
||||
import { t } from '@/shared/i18n'
|
||||
import LinkField from '@/shared/components/LinkField.vue'
|
||||
import Quill from 'quill'
|
||||
import ImageResize from 'quill-image-resize'
|
||||
import MagicUrl from 'quill-magic-url'
|
||||
import 'quill/dist/quill.snow.css'
|
||||
import Jeditor from '@/core/pagetype/form/controls/Jeditor.vue'
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Public API
|
||||
@ -238,17 +238,16 @@ function open(opts?: Partial<typeof props>) {
|
||||
}
|
||||
visible.value = true
|
||||
nextTick(async () => {
|
||||
initEditor()
|
||||
// Pre-fill content if provided
|
||||
if (propsRef.defaultContent) {
|
||||
editorContent.value = propsRef.defaultContent || ''
|
||||
}
|
||||
// Append email signature
|
||||
await appendSignature()
|
||||
// Append reply quoting
|
||||
if (propsRef.isReply && propsRef.lastEmail) {
|
||||
appendReplyQuote()
|
||||
}
|
||||
// Pre-fill content if provided
|
||||
if (propsRef.defaultContent) {
|
||||
setEditorHtml(propsRef.defaultContent || '')
|
||||
}
|
||||
// Restore draft if available
|
||||
await restoreDraft()
|
||||
// Load existing attachments from reference document
|
||||
@ -259,7 +258,6 @@ function open(opts?: Partial<typeof props>) {
|
||||
function close() {
|
||||
saveDraft() // Save draft before closing
|
||||
visible.value = false
|
||||
destroyEditor()
|
||||
}
|
||||
|
||||
// Internal props ref (mutable copy for open())
|
||||
@ -271,6 +269,7 @@ defineExpose({ open, close, visible })
|
||||
// Form state
|
||||
// -------------------------------------------------------
|
||||
const showCcBcc = ref(false)
|
||||
const editorContent = ref('')
|
||||
const form = reactive({
|
||||
sender: '',
|
||||
recipients: [] as string[],
|
||||
@ -301,7 +300,7 @@ function resetForm() {
|
||||
showCcBcc.value = false
|
||||
signatureLoaded.value = false
|
||||
replyQuoteAdded.value = false
|
||||
nextTick(() => setEditorHtml(''))
|
||||
editorContent.value = ''
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
@ -367,11 +366,11 @@ async function appendSignature() {
|
||||
currentSignature.value = sig
|
||||
signatureLoaded.value = true
|
||||
// Append signature to editor
|
||||
const currentHtml = getEditorHtml()
|
||||
const currentHtml = editorContent.value
|
||||
if (currentHtml && !stripHtml(currentHtml).includes(stripHtml(sig))) {
|
||||
setEditorHtml(currentHtml + sig)
|
||||
editorContent.value = currentHtml + sig
|
||||
} else if (!currentHtml) {
|
||||
setEditorHtml(sig)
|
||||
editorContent.value = sig
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -380,12 +379,12 @@ function onSenderChange(newSender: string) {
|
||||
// Re-append signature for new sender
|
||||
if (signatureLoaded.value && currentSignature.value) {
|
||||
// Remove old signature first
|
||||
const html = getEditorHtml()
|
||||
const html = editorContent.value
|
||||
const sigHtml = stripHtml(currentSignature.value)
|
||||
if (html) {
|
||||
const plain = stripHtml(html)
|
||||
if (plain.includes(sigHtml)) {
|
||||
setEditorHtml(html.replace(currentSignature.value, ''))
|
||||
editorContent.value = html.replace(currentSignature.value, '')
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -467,7 +466,7 @@ async function onTemplateChange(templateName: string) {
|
||||
const data = await fetchTemplateContent(templateName)
|
||||
if (!data) return
|
||||
if (data.subject) form.subject = data.subject
|
||||
if (data.message) setEditorHtml(data.message)
|
||||
if (data.message) editorContent.value = data.message
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
@ -530,9 +529,9 @@ function appendReplyQuote() {
|
||||
const quoteHeader = `<p>${t('On {0}, {1} wrote:', [date, senderName])}</p>`
|
||||
const quotedBlock = `<blockquote>${content}</blockquote>`
|
||||
|
||||
const current = getEditorHtml()
|
||||
const current = editorContent.value
|
||||
const quoteHtml = separator + quoteHeader + quotedBlock
|
||||
setEditorHtml((current || '') + '<div><br></div>' + quoteHtml)
|
||||
editorContent.value = (current || '') + '<div><br></div>' + quoteHtml
|
||||
replyQuoteAdded.value = true
|
||||
}
|
||||
|
||||
@ -606,13 +605,12 @@ function getDraftKey(): string {
|
||||
}
|
||||
|
||||
function saveDraft() {
|
||||
if (!quill) return
|
||||
const draft = {
|
||||
recipients: form.recipients,
|
||||
cc: form.cc,
|
||||
bcc: form.bcc,
|
||||
subject: form.subject,
|
||||
content: getEditorHtml(),
|
||||
content: editorContent.value,
|
||||
sender: form.sender,
|
||||
emailTemplate: form.emailTemplate,
|
||||
}
|
||||
@ -630,7 +628,7 @@ async function restoreDraft() {
|
||||
if (draft.cc?.length) form.cc = draft.cc
|
||||
if (draft.bcc?.length) form.bcc = draft.bcc
|
||||
if (draft.subject) form.subject = draft.subject
|
||||
if (draft.content) setEditorHtml(draft.content)
|
||||
if (draft.content) editorContent.value = draft.content
|
||||
if (draft.sender) form.sender = draft.sender
|
||||
if (draft.emailTemplate) form.emailTemplate = draft.emailTemplate
|
||||
localStorage.removeItem(getDraftKey()) // Remove after restore
|
||||
@ -654,57 +652,6 @@ watch(visible, (v) => {
|
||||
}
|
||||
})
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Rich text editor (Quill)
|
||||
// -------------------------------------------------------
|
||||
const editorHost = ref<HTMLDivElement | null>(null)
|
||||
let quill: Quill | null = null
|
||||
let syncTimer: any = null
|
||||
|
||||
function registerQuillExtensions() {
|
||||
try { Quill.register('modules/imageResize', ImageResize, true) } catch {}
|
||||
try { Quill.register('modules/magicUrl', MagicUrl, true) } catch {}
|
||||
}
|
||||
|
||||
function initEditor() {
|
||||
if (!editorHost.value || quill) return
|
||||
registerQuillExtensions()
|
||||
|
||||
const toolbar = [
|
||||
[{ header: [1, 2, 3, false] }],
|
||||
['bold', 'italic', 'underline', 'strike', 'clean'],
|
||||
[{ color: [] }, { background: [] }],
|
||||
['link', 'image'],
|
||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||
[{ align: [] }],
|
||||
]
|
||||
|
||||
quill = new Quill(editorHost.value, {
|
||||
theme: 'snow',
|
||||
placeholder: t('Write your message...'),
|
||||
modules: { toolbar, imageResize: {}, magicUrl: true },
|
||||
})
|
||||
}
|
||||
|
||||
function destroyEditor() {
|
||||
if (syncTimer) clearTimeout(syncTimer)
|
||||
quill = null
|
||||
}
|
||||
|
||||
function getEditorHtml(): string {
|
||||
if (!quill) return ''
|
||||
return (quill.root as HTMLElement).innerHTML || ''
|
||||
}
|
||||
|
||||
function setEditorHtml(html: string) {
|
||||
if (!quill) return
|
||||
if (!html) { quill.setText(''); return }
|
||||
const delta = (quill as any).clipboard.convert({ html, text: '' })
|
||||
quill.setContents(delta)
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => destroyEditor())
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Attachments
|
||||
// -------------------------------------------------------
|
||||
@ -763,7 +710,7 @@ async function sendEmail() {
|
||||
cc: form.cc.join(', ') || undefined,
|
||||
bcc: form.bcc.join(', ') || undefined,
|
||||
subject: form.subject,
|
||||
content: getEditorHtml(),
|
||||
content: editorContent.value,
|
||||
sender: form.sender || undefined,
|
||||
send_email: 1,
|
||||
send_me_a_copy: form.sendMeCopy ? 1 : 0,
|
||||
@ -869,22 +816,12 @@ function splitEmailList(str: string | undefined): string[] {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.text-editor-wrapper {
|
||||
min-height: 260px;
|
||||
border: 1px solid var(--n-border-color, #e0e0e6);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
.message-editor-wrapper :deep(.jeditor-control .field-label) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.text-editor-wrapper :deep(.ql-toolbar) {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--n-border-color, #e0e0e6);
|
||||
}
|
||||
|
||||
.text-editor-wrapper :deep(.ql-container) {
|
||||
border: none;
|
||||
.message-editor-wrapper :deep(.jeditor-control .jeditor) {
|
||||
min-height: 220px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.attachment-area {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user