fix: 1,新建的草稿邮件为什么同时也显示在已发送里面

2,邮件的默认的删除和批量删除改为移动到回收站
This commit is contained in:
jingrow 2026-05-16 16:44:25 +08:00
parent af45db8da3
commit ac6e6baa4a
2 changed files with 73 additions and 35 deletions

View File

@ -101,9 +101,12 @@
<button class="restore-btn" @click.stop="handleRestoreSingle(row)" :title="t('Restore')">
<Icon icon="tabler:arrow-back-up" :size="16" />
</button>
<button class="delete-btn" @click.stop="handlePermanentDeleteSingle(row)" :title="t('Delete Permanently')">
<Icon icon="tabler:trash-x" :size="16" />
</button>
</template>
<template v-else>
<button class="delete-btn" @click.stop="handleDeleteSingle(row)" :title="t('Delete')">
<button class="delete-btn" @click.stop="handleDeleteSingle(row)" :title="t('Move to Trash')">
<Icon icon="tabler:trash" :size="16" />
</button>
</template>
@ -407,8 +410,8 @@ async function loadData() {
} else if (activeFilter.value === 'trash') {
filterConditions.push(['email_status', '=', 'Trash'])
} else {
// all / inbox / sent: Spam Trash
filterConditions.push(['email_status', 'not in', ['Spam', 'Trash']])
// all / inbox / sent: DraftSpam Trash
filterConditions.push(['email_status', 'not in', ['Draft', 'Spam', 'Trash']])
}
// sent_or_received
@ -485,27 +488,27 @@ function toggleSelection(name: string) {
async function handleDeleteSelected() {
if (selectedKeys.value.length === 0) return
dialog.warning({
title: t('Delete Selected Records'),
content: `${t('Are you sure you want to delete')} ${selectedKeys.value.length} ${t('selected record(s)? This action cannot be undone.')}`,
positiveText: t('Delete'),
title: t('Move to Trash'),
content: `${t('Are you sure you want to move')} ${selectedKeys.value.length} ${t('selected record(s) to Trash?')}`,
positiveText: t('Move to Trash'),
negativeText: t('Cancel'),
onPositiveClick: async () => {
loading.value = true
try {
const result = await deleteRecords(entity.value, selectedKeys.value)
if (result.success) {
message.success(result.message || t('Deleted successfully'))
selectedKeys.value = []
//
if (previewName.value && !rows.value.find(r => r.name === previewName.value)) {
previewVisible.value = false
}
await loadData()
} else {
message.error(result.message || t('Failed to delete'))
//
for (const name of selectedKeys.value) {
try {
await api.call('jingrow.email.inbox.mark_as_trash', { email: name })
} catch { /* skip failed */ }
}
message.success(t('Moved to trash'))
selectedKeys.value = []
if (previewName.value && !rows.value.find(r => r.name === previewName.value)) {
previewVisible.value = false
}
await loadData()
} catch {
message.error(t('Failed to delete'))
message.error(t('Failed to move to trash'))
} finally {
loading.value = false
}
@ -515,21 +518,20 @@ async function handleDeleteSelected() {
function handleDeleteSingle(row: any) {
dialog.warning({
title: t('Delete Record'),
content: t('Are you sure you want to delete this record? This action cannot be undone.'),
positiveText: t('Delete'),
title: t('Move to Trash'),
content: t('Are you sure you want to move this email to Trash?'),
positiveText: t('Move to Trash'),
negativeText: t('Cancel'),
onPositiveClick: async () => {
try {
await deleteRecords(entity.value, [row.name])
message.success(t('Deleted successfully'))
//
await api.call('jingrow.email.inbox.mark_as_trash', { email: row.name })
message.success(t('Moved to trash'))
if (previewName.value === row.name) {
previewVisible.value = false
}
await loadData()
} catch (err: any) {
message.error(err?.message || t('Delete failed'))
message.error(err?.message || t('Failed to move to trash'))
}
}
})
@ -548,6 +550,27 @@ async function handleRestoreSingle(row: any) {
}
}
async function handlePermanentDeleteSingle(row: any) {
dialog.warning({
title: t('Delete Permanently'),
content: t('Are you sure you want to permanently delete this email? This action cannot be undone.'),
positiveText: t('Delete Permanently'),
negativeText: t('Cancel'),
onPositiveClick: async () => {
try {
await deleteRecords(entity.value, [row.name])
message.success(t('Deleted permanently'))
if (previewName.value === row.name) {
previewVisible.value = false
}
await loadData()
} catch (err: any) {
message.error(err?.message || t('Delete failed'))
}
}
})
}
// ===== =====
// ===== =====
function scrollPreviewToTop() {
@ -685,18 +708,18 @@ function navigatePreviewNext(name: string) {
async function handleDeletePreview() {
if (!previewName.value) return
dialog.warning({
title: t('Delete'),
content: t('Are you sure you want to delete this record?'),
positiveText: t('Delete'),
title: t('Move to Trash'),
content: t('Are you sure you want to move this email to Trash?'),
positiveText: t('Move to Trash'),
negativeText: t('Cancel'),
onPositiveClick: async () => {
try {
await deleteRecords(entity.value, [previewName.value])
message.success(t('Deleted successfully'))
await api.call('jingrow.email.inbox.mark_as_trash', { email: previewName.value })
message.success(t('Moved to trash'))
previewVisible.value = false
reload()
} catch (err: any) {
message.error(err?.message || t('Delete failed'))
message.error(err?.message || t('Failed to move to trash'))
}
}
})

View File

@ -57,13 +57,14 @@
{{ record.reference_name }}
</n-button>
<!-- Delete -->
<!-- Move to Trash -->
<n-button
v-if="!isTrash"
type="default"
size="medium"
@click="$emit('delete')"
@click="moveToTrash"
:disabled="loading || isNew"
:title="t('Delete')"
:title="t('Move to Trash')"
class="header-action-btn delete-btn"
>
<template #icon>
@ -201,7 +202,7 @@ import {
import { Icon } from '@iconify/vue'
import { t } from '@/shared/i18n'
import { usePageTypeSlug, pageTypeToSlug } from '@/shared/utils/slug'
import { getRecords, renameRecord, duplicateRecord, api } from '@/shared/api/common'
import { getRecords, renameRecord, duplicateRecord, deleteRecords, api } from '@/shared/api/common'
import { toggleLike as toggleLikeApi } from '@/shared/api/timeline'
import { useAuthStore } from '@/shared/stores/auth'
import EmailComposeModal from '@/shared/components/EmailComposeModal.vue'
@ -587,6 +588,17 @@ async function markAsOpen() {
}
}
// ===== Permanent Delete (from Trash/Spam) =====
async function permanentDelete() {
try {
await deleteRecords(props.entity, [props.id])
message.success(t('Deleted permanently'))
emit('refresh')
} catch (err: any) {
message.error(err?.message || t('Delete failed'))
}
}
// ===== Add to Contact =====
function addToContact() {
const fullname = senderFullName.value || ''
@ -747,6 +759,8 @@ const moreOptions = computed<DropdownOption[]>(() => {
} else {
const restore = makeOption('restore', t('Restore'), 'tabler:arrow-back-up')
if (restore) opts.push(restore)
const permanentDelete = makeOption('permanent_delete', t('Delete Permanently'), 'tabler:trash-x')
if (permanentDelete) opts.push(permanentDelete)
}
if (isDraft.value) {
@ -806,6 +820,7 @@ function handleMoreSelect(key: string) {
case 'not_spam': markAsOpen(); break
case 'trash': moveToTrash(); break
case 'restore': markAsOpen(); break
case 'permanent_delete': permanentDelete(); break
case 'edit_draft': markAsOpen(); break
case 'toggle_close': markAsClosedOpen(); break
case 'contact': addToContact(); break