162 lines
6.2 KiB
JavaScript
162 lines
6.2 KiB
JavaScript
import {computed, onBeforeUnmount, ref, watch} from 'vue'
|
|
import {onBeforeRouteLeave, onBeforeRouteUpdate, useRoute} from 'vue-router'
|
|
import {ElMessage, ElMessageBox} from 'element-plus'
|
|
import {http} from '@/utils/axios'
|
|
import lang from '../../i18n/i18n'
|
|
|
|
export function useDraft({router, groupStore, ruleForm, valueHtml, fileList, editorRef, showCcBcc, isSending, pendingFiles}) {
|
|
const route = useRoute()
|
|
const draftID = ref(0)
|
|
const draftVersion = ref('')
|
|
const isSaving = ref(false)
|
|
const isLoading = ref(true)
|
|
const loadFailed = ref(false)
|
|
const replyTo = ref([])
|
|
const isBusy = computed(() => isSaving.value || isSending.value || isLoading.value || loadFailed.value || pendingFiles.value > 0)
|
|
let savedSnapshot = ''
|
|
let sent = false
|
|
let loadSequence = 0
|
|
let disposed = false
|
|
|
|
const buildMessage = () => ({
|
|
draft_id: draftID.value,
|
|
draft_version: draftVersion.value,
|
|
from: {name: ruleForm.nickName, email: `${ruleForm.sender}@${ruleForm.pickedDomain}`},
|
|
to: ruleForm.receivers.map(email => ({name: '', email})),
|
|
cc: ruleForm.cc.map(email => ({name: '', email})),
|
|
bcc: ruleForm.bcc.map(email => ({name: '', email})),
|
|
reply_to: replyTo.value,
|
|
subject: ruleForm.subject,
|
|
text: editorRef.value?.getText() || '',
|
|
html: valueHtml.value,
|
|
attrs: fileList.map(item => ({name: item.name, data: item.data}))
|
|
})
|
|
|
|
const snapshot = () => JSON.stringify({
|
|
from: [ruleForm.nickName, ruleForm.sender, ruleForm.pickedDomain],
|
|
to: ruleForm.receivers, cc: ruleForm.cc, bcc: ruleForm.bcc,
|
|
subject: ruleForm.subject, html: valueHtml.value, attrs: fileList
|
|
})
|
|
const isDirty = () => !sent && savedSnapshot !== '' && snapshot() !== savedSnapshot
|
|
|
|
const loadDraft = async () => {
|
|
const sequence = ++loadSequence
|
|
isLoading.value = true
|
|
loadFailed.value = false
|
|
const rawID = route.query.draft
|
|
if (rawID === undefined) {
|
|
draftID.value = 0
|
|
draftVersion.value = ''
|
|
replyTo.value = []
|
|
ruleForm.receivers = []
|
|
ruleForm.cc = []
|
|
ruleForm.bcc = []
|
|
ruleForm.subject = ''
|
|
valueHtml.value = '<p><br></p>'
|
|
fileList.splice(0)
|
|
showCcBcc.value = false
|
|
sent = false
|
|
savedSnapshot = snapshot()
|
|
isLoading.value = false
|
|
return
|
|
}
|
|
try {
|
|
const id = Number(rawID)
|
|
if (!Number.isSafeInteger(id) || id <= 0) throw new Error(lang.draft_load_failed)
|
|
const result = await http.post('/api/v1/email/draft/detail', {id})
|
|
if (disposed || sequence !== loadSequence) return
|
|
if (result.errorNo !== 0) throw new Error(result.errorMsg || lang.draft_load_failed)
|
|
const data = result.data
|
|
const message = data?.message
|
|
if (data?.id !== id || !data.version || !message?.from?.email) throw new Error(lang.draft_load_failed)
|
|
const split = message.from.email.lastIndexOf('@')
|
|
if (split <= 0) throw new Error(lang.draft_load_failed)
|
|
draftID.value = data.id
|
|
draftVersion.value = data.version
|
|
ruleForm.sender = message.from.email.slice(0, split)
|
|
ruleForm.pickedDomain = message.from.email.slice(split + 1)
|
|
ruleForm.nickName = message.from.name || ''
|
|
ruleForm.receivers = (message.to || []).map(item => item.email)
|
|
ruleForm.cc = (message.cc || []).map(item => item.email)
|
|
ruleForm.bcc = (message.bcc || []).map(item => item.email)
|
|
replyTo.value = message.reply_to || []
|
|
ruleForm.subject = message.subject || ''
|
|
valueHtml.value = message.html || '<p><br></p>'
|
|
fileList.splice(0, fileList.length, ...(message.attrs || []))
|
|
showCcBcc.value = ruleForm.cc.length > 0 || ruleForm.bcc.length > 0
|
|
sent = false
|
|
savedSnapshot = snapshot()
|
|
} catch (error) {
|
|
if (disposed || sequence !== loadSequence) return
|
|
loadFailed.value = true
|
|
ElMessage.error(error.message || lang.draft_load_failed)
|
|
} finally {
|
|
if (!disposed && sequence === loadSequence) isLoading.value = false
|
|
}
|
|
}
|
|
|
|
const saveDraft = async () => {
|
|
if (isBusy.value || !editorRef.value) return
|
|
isSaving.value = true
|
|
const submittedSnapshot = snapshot()
|
|
try {
|
|
const result = await http.post('/api/v1/email/draft/save', buildMessage())
|
|
if (result.errorNo !== 0) throw new Error(result.errorMsg || lang.fail)
|
|
if (!Number.isSafeInteger(result.data?.id) || result.data.id <= 0 || !result.data.version) throw new Error(lang.fail)
|
|
draftID.value = result.data.id
|
|
draftVersion.value = result.data.version
|
|
savedSnapshot = submittedSnapshot
|
|
groupStore.invalidate()
|
|
// 将已保存草稿写入地址,刷新后仍可恢复;先释放保存锁再更新路由。
|
|
isSaving.value = false
|
|
await router.replace({name: 'editer', query: {draft: String(draftID.value)}})
|
|
ElMessage.success(lang.draft_saved)
|
|
} catch (error) {
|
|
ElMessage.error(error.message || lang.fail)
|
|
} finally {
|
|
isSaving.value = false
|
|
}
|
|
}
|
|
|
|
const confirmLeave = async () => {
|
|
if (sent) return true
|
|
if (isSaving.value || isSending.value || pendingFiles.value > 0) return false
|
|
if (!isDirty()) return true
|
|
try {
|
|
await ElMessageBox.confirm(lang.draft_leave_warning, lang.draft_title, {
|
|
confirmButtonText: lang.draft_leave, cancelButtonText: lang.draft_stay, type: 'warning'
|
|
})
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
onBeforeRouteLeave(confirmLeave)
|
|
onBeforeRouteUpdate((to, from) => to.query.draft === from.query.draft ? true : confirmLeave())
|
|
watch(() => route.query.draft, value => {
|
|
if (String(value || '') !== String(draftID.value || '')) loadDraft()
|
|
})
|
|
watch([editorRef, isBusy], ([editor, busy]) => {
|
|
if (!editor || editor.isDestroyed) return
|
|
if (busy) editor.disable()
|
|
else editor.enable()
|
|
})
|
|
const beforeUnload = event => {
|
|
if (isDirty() || isSaving.value || isSending.value || pendingFiles.value > 0) {
|
|
event.preventDefault()
|
|
event.returnValue = ''
|
|
}
|
|
}
|
|
window.addEventListener('beforeunload', beforeUnload)
|
|
onBeforeUnmount(() => {
|
|
disposed = true
|
|
loadSequence += 1
|
|
window.removeEventListener('beforeunload', beforeUnload)
|
|
})
|
|
const markSent = () => {
|
|
sent = true
|
|
groupStore.invalidate()
|
|
}
|
|
return {draftID, draftVersion, isSaving, isLoading, isBusy, loadFailed, loadDraft, saveDraft, buildMessage, markSent}
|
|
}
|