mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-05-13 08:40:11 +08:00
feat: allow admin and user delete mail, sendbox, send access(only admin) (#329)
This commit is contained in:
@@ -90,7 +90,7 @@ const { t } = useI18n({
|
||||
saveToS3: 'Save to S3',
|
||||
multiAction: 'Multi Action',
|
||||
cancelMultiAction: 'Cancel Multi Action',
|
||||
selectAll: 'Select All',
|
||||
selectAll: 'Select All of This Page',
|
||||
unselectAll: 'Unselect All',
|
||||
},
|
||||
zh: {
|
||||
@@ -109,7 +109,7 @@ const { t } = useI18n({
|
||||
saveToS3: '保存到S3',
|
||||
multiAction: '多选',
|
||||
cancelMultiAction: '取消多选',
|
||||
selectAll: '全选',
|
||||
selectAll: '全选本页',
|
||||
unselectAll: '取消全选',
|
||||
}
|
||||
}
|
||||
@@ -387,7 +387,8 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</template>
|
||||
<template #2>
|
||||
<n-card v-if="curMail" class="mail-item" :title="curMail.subject" style="overflow: auto; max-height: 100vh;">
|
||||
<n-card :bordered="false" embedded v-if="curMail" class="mail-item" :title="curMail.subject"
|
||||
style="overflow: auto; max-height: 100vh;">
|
||||
<n-space>
|
||||
<n-tag type="info">
|
||||
ID: {{ curMail.id }}
|
||||
@@ -434,7 +435,7 @@ onBeforeUnmount(() => {
|
||||
</iframe>
|
||||
<div v-else v-html="curMail.message" style="margin-top: 10px;"></div>
|
||||
</n-card>
|
||||
<n-card class="mail-item" v-else>
|
||||
<n-card :bordered="false" embedded class="mail-item" v-else>
|
||||
<n-result status="info" :title="t('pleaseSelectMail')">
|
||||
</n-result>
|
||||
</n-card>
|
||||
@@ -483,7 +484,7 @@ onBeforeUnmount(() => {
|
||||
<n-drawer v-model:show="curMail" width="100%" placement="bottom" :trap-focus="false" :block-scroll="false"
|
||||
style="height: 80vh;">
|
||||
<n-drawer-content :title="curMail ? curMail.subject : ''" closable>
|
||||
<n-card style="overflow: auto;">
|
||||
<n-card :bordered="false" embedded style="overflow: auto;">
|
||||
<n-space>
|
||||
<n-tag type="info">
|
||||
ID: {{ curMail.id }}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { watch, onMounted, ref } from "vue";
|
||||
import { watch, onMounted, ref, computed } from "vue";
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useGlobalState } from '../store'
|
||||
@@ -9,6 +9,11 @@ const message = useMessage()
|
||||
const isMobile = useIsMobile()
|
||||
|
||||
const props = defineProps({
|
||||
enableUserDeleteEmail: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
requried: false
|
||||
},
|
||||
showEMailFrom: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@@ -18,6 +23,11 @@ const props = defineProps({
|
||||
default: () => { },
|
||||
requried: true
|
||||
},
|
||||
deleteMail: {
|
||||
type: Function,
|
||||
default: () => { },
|
||||
requried: false
|
||||
},
|
||||
})
|
||||
|
||||
const { isDark, mailboxSplitSize } = useGlobalState()
|
||||
@@ -30,19 +40,35 @@ const pageSize = ref(20)
|
||||
const curMail = ref(null);
|
||||
const showCode = ref(false)
|
||||
|
||||
const multiActionMode = ref(false)
|
||||
const showMultiActionDelete = ref(false)
|
||||
const multiActionDeleteProgress = ref({ percentage: 0, tip: '0/0' })
|
||||
|
||||
const { t } = useI18n({
|
||||
messages: {
|
||||
en: {
|
||||
success: 'Success',
|
||||
refresh: 'Refresh',
|
||||
showCode: 'Change View Original Code',
|
||||
pleaseSelectMail: "Please select a mail to view."
|
||||
pleaseSelectMail: "Please select a mail to view.",
|
||||
delete: 'Delete',
|
||||
deleteMailTip: 'Are you sure you want to delete mail?',
|
||||
multiAction: 'Multi Action',
|
||||
cancelMultiAction: 'Cancel Multi Action',
|
||||
selectAll: 'Select All of This Page',
|
||||
unselectAll: 'Unselect All',
|
||||
},
|
||||
zh: {
|
||||
success: '成功',
|
||||
refresh: '刷新',
|
||||
showCode: '切换查看元数据',
|
||||
pleaseSelectMail: "请选择一封邮件查看。",
|
||||
delete: '删除',
|
||||
deleteMailTip: '确定要删除邮件吗?',
|
||||
multiAction: '多选',
|
||||
cancelMultiAction: '取消多选',
|
||||
selectAll: '全选本页',
|
||||
unselectAll: '取消全选',
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -105,6 +131,71 @@ const onSpiltSizeChange = (size) => {
|
||||
mailboxSplitSize.value = size;
|
||||
}
|
||||
|
||||
const deleteMail = async () => {
|
||||
try {
|
||||
await props.deleteMail(curMail.value.id);
|
||||
message.success(t("success"));
|
||||
curMail.value = null;
|
||||
await refresh();
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
}
|
||||
};
|
||||
|
||||
const showMultiActionMode = computed(() => {
|
||||
return props.enableUserDeleteEmail;
|
||||
});
|
||||
|
||||
const multiActionModeClick = (enableMulti) => {
|
||||
if (enableMulti) {
|
||||
data.value.forEach((item) => {
|
||||
item.checked = false;
|
||||
});
|
||||
multiActionMode.value = true;
|
||||
} else {
|
||||
multiActionMode.value = false;
|
||||
data.value.forEach((item) => {
|
||||
item.checked = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const multiActionSelectAll = (checked) => {
|
||||
data.value.forEach((item) => {
|
||||
item.checked = checked;
|
||||
});
|
||||
}
|
||||
|
||||
const multiActionDeleteMail = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const selectedMails = data.value.filter((item) => item.checked);
|
||||
if (selectedMails.length === 0) {
|
||||
message.error(t('pleaseSelectMail'));
|
||||
return;
|
||||
}
|
||||
multiActionDeleteProgress.value = {
|
||||
percentage: 0,
|
||||
tip: `0/${selectedMails.length}`
|
||||
};
|
||||
for (const [index, mail] of selectedMails.entries()) {
|
||||
await props.deleteMail(mail.id);
|
||||
showMultiActionDelete.value = true;
|
||||
multiActionDeleteProgress.value = {
|
||||
percentage: Math.floor((index + 1) / selectedMails.length * 100),
|
||||
tip: `${index + 1}/${selectedMails.length}`
|
||||
};
|
||||
}
|
||||
message.success(t("success"));
|
||||
await refresh();
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
showMultiActionDelete.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await refresh();
|
||||
});
|
||||
@@ -112,70 +203,105 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<n-split class="left" v-if="!isMobile" direction="horizontal" :max="0.75" :min="0.25"
|
||||
:default-size="mailboxSplitSize" :on-update:size="onSpiltSizeChange">
|
||||
<template #1>
|
||||
<div class="center">
|
||||
<div v-if="!isMobile" class="left">
|
||||
<div style="margin-bottom: 10px;">
|
||||
<n-space v-if="multiActionMode">
|
||||
<n-button @click="multiActionModeClick(false)" tertiary>
|
||||
{{ t('cancelMultiAction') }}
|
||||
</n-button>
|
||||
<n-button @click="multiActionSelectAll(true)" tertiary>
|
||||
{{ t('selectAll') }}
|
||||
</n-button>
|
||||
<n-button @click="multiActionSelectAll(false)" tertiary>
|
||||
{{ t('unselectAll') }}
|
||||
</n-button>
|
||||
<n-popconfirm v-if="enableUserDeleteEmail" @positive-click="multiActionDeleteMail">
|
||||
<template #trigger>
|
||||
<n-button tertiary type="error">{{ t('delete') }}</n-button>
|
||||
</template>
|
||||
{{ t('deleteMailTip') }}
|
||||
</n-popconfirm>
|
||||
</n-space>
|
||||
<n-space v-else>
|
||||
<n-button v-if="showMultiActionMode" @click="multiActionModeClick(true)" type="primary" tertiary>
|
||||
{{ t('multiAction') }}
|
||||
</n-button>
|
||||
<div style="display: inline-block; margin-right: 10px;">
|
||||
<n-pagination v-model:page="page" v-model:page-size="pageSize" :item-count="count" simple size="small" />
|
||||
<n-pagination v-model:page="page" v-model:page-size="pageSize" :item-count="count"
|
||||
:page-sizes="[20, 50, 100]" show-size-picker />
|
||||
</div>
|
||||
<n-button @click="refresh" size="small" type="primary" tertiary>
|
||||
<n-button @click="refresh" type="primary" tertiary>
|
||||
{{ t('refresh') }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div style="overflow: auto; height: 80vh;">
|
||||
<n-list hoverable clickable>
|
||||
<n-list-item v-for="row in data" v-bind:key="row.id" @click="() => clickRow(row)"
|
||||
:class="mailItemClass(row)">
|
||||
<n-thing :title="row.subject">
|
||||
<template #description>
|
||||
<n-tag type="info">
|
||||
ID: {{ row.id }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
{{ `${row.created_at} UTC` }}
|
||||
</n-tag>
|
||||
<n-tag v-if="showEMailFrom" type="info">
|
||||
FROM: {{ row.address }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
TO: {{ row.to_mail }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</div>
|
||||
<n-split direction="horizontal" :max="0.75" :min="0.25" :default-size="mailboxSplitSize"
|
||||
:on-update:size="onSpiltSizeChange">
|
||||
<template #1>
|
||||
<div style="overflow: auto; height: 80vh;">
|
||||
<n-list hoverable clickable>
|
||||
<n-list-item v-for="row in data" v-bind:key="row.id" @click="() => clickRow(row)"
|
||||
:class="mailItemClass(row)">
|
||||
<template #prefix v-if="multiActionMode">
|
||||
<n-checkbox v-model:checked="row.checked" />
|
||||
</template>
|
||||
</n-thing>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</div>
|
||||
</template>
|
||||
<template #2>
|
||||
<n-card v-if="curMail" class="mail-item" :title="curMail.subject" style="overflow: auto; max-height: 100vh;">
|
||||
<n-space>
|
||||
<n-tag type="info">
|
||||
ID: {{ curMail.id }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
{{ `${curMail.created_at} UTC` }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
FROM: {{ curMail.address }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
TO: {{ curMail.to_mail }}
|
||||
</n-tag>
|
||||
<n-button size="small" tertiary type="info" @click="showCode = !showCode">
|
||||
{{ t('showCode') }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
<pre v-if="showCode" style="margin-top: 10px;">{{ curMail.raw }}</pre>
|
||||
<pre v-else-if="!curMail.is_html" style="margin-top: 10px;">{{ curMail.content }}</pre>
|
||||
<div v-else v-html="curMail.content" style="margin-top: 10px;"></div>
|
||||
</n-card>
|
||||
<n-card class="mail-item" v-else>
|
||||
<n-result status="info" :title="t('pleaseSelectMail')">
|
||||
</n-result>
|
||||
</n-card>
|
||||
</template>
|
||||
</n-split>
|
||||
<n-thing :title="row.subject">
|
||||
<template #description>
|
||||
<n-tag type="info">
|
||||
ID: {{ row.id }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
{{ `${row.created_at} UTC` }}
|
||||
</n-tag>
|
||||
<n-tag v-if="showEMailFrom" type="info">
|
||||
FROM: {{ row.address }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
TO: {{ row.to_mail }}
|
||||
</n-tag>
|
||||
</template>
|
||||
</n-thing>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</div>
|
||||
</template>
|
||||
<template #2>
|
||||
<n-card :bordered="false" embedded v-if="curMail" class="mail-item" :title="curMail.subject"
|
||||
style="overflow: auto; max-height: 100vh;">
|
||||
<n-space>
|
||||
<n-tag type="info">
|
||||
ID: {{ curMail.id }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
{{ `${curMail.created_at} UTC` }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
FROM: {{ curMail.address }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
TO: {{ curMail.to_mail }}
|
||||
</n-tag>
|
||||
<n-button size="small" tertiary type="info" @click="showCode = !showCode">
|
||||
{{ t('showCode') }}
|
||||
</n-button>
|
||||
<n-popconfirm v-if="enableUserDeleteEmail" @positive-click="deleteMail">
|
||||
<template #trigger>
|
||||
<n-button tertiary type="error" size="small">{{ t('delete') }}</n-button>
|
||||
</template>
|
||||
{{ t('deleteMailTip') }}
|
||||
</n-popconfirm>
|
||||
</n-space>
|
||||
<pre v-if="showCode" style="margin-top: 10px;">{{ curMail.raw }}</pre>
|
||||
<pre v-else-if="!curMail.is_html" style="margin-top: 10px;">{{ curMail.content }}</pre>
|
||||
<div v-else v-html="curMail.content" style="margin-top: 10px;"></div>
|
||||
</n-card>
|
||||
<n-card :bordered="false" embedded class="mail-item" v-else>
|
||||
<n-result status="info" :title="t('pleaseSelectMail')">
|
||||
</n-result>
|
||||
</n-card>
|
||||
</template>
|
||||
</n-split>
|
||||
</div>
|
||||
<div class="left" v-else>
|
||||
<div class="center">
|
||||
<div style="display: inline-block; margin-right: 10px;">
|
||||
@@ -210,7 +336,7 @@ onMounted(async () => {
|
||||
<n-drawer v-model:show="curMail" width="100%" placement="bottom" :trap-focus="false" :block-scroll="false"
|
||||
style="height: 80vh;">
|
||||
<n-drawer-content :title="curMail ? curMail.subject : ''" closable>
|
||||
<n-card style="overflow: auto;">
|
||||
<n-card :bordered="false" embedded style="overflow: auto;">
|
||||
<n-space>
|
||||
<n-tag type="info">
|
||||
ID: {{ curMail.id }}
|
||||
@@ -224,6 +350,15 @@ onMounted(async () => {
|
||||
<n-tag type="info">
|
||||
TO: {{ curMail.to_mail }}
|
||||
</n-tag>
|
||||
<n-button size="small" tertiary type="info" @click="showCode = !showCode">
|
||||
{{ t('showCode') }}
|
||||
</n-button>
|
||||
<n-popconfirm v-if="enableUserDeleteEmail" @positive-click="deleteMail">
|
||||
<template #trigger>
|
||||
<n-button tertiary type="error" size="small">{{ t('delete') }}</n-button>
|
||||
</template>
|
||||
{{ t('deleteMailTip') }}
|
||||
</n-popconfirm>
|
||||
</n-space>
|
||||
<pre v-if="showCode" style="margin-top: 10px;">{{ curMail.raw }}</pre>
|
||||
<pre v-else-if="!curMail.is_html" style="margin-top: 10px;">{{ curMail.content }}</pre>
|
||||
|
||||
@@ -51,6 +51,10 @@ const deleteMail = async (curMailId) => {
|
||||
await api.fetch(`/api/mails/${curMailId}`, { method: 'DELETE' });
|
||||
};
|
||||
|
||||
const deleteSenboxMail = async (curMailId) => {
|
||||
await api.fetch(`/api/sendbox/${curMailId}`, { method: 'DELETE' });
|
||||
};
|
||||
|
||||
const fetchSenboxData = async (limit, offset) => {
|
||||
return await api.fetch(`/api/sendbox?limit=${limit}&offset=${offset}`);
|
||||
};
|
||||
@@ -86,7 +90,8 @@ const saveToS3 = async (mail_id, filename, blob) => {
|
||||
:deleteMail="deleteMail" />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="sendbox" :tab="t('sendbox')">
|
||||
<SendBox :fetchMailData="fetchSenboxData" />
|
||||
<SendBox :fetchMailData="fetchSenboxData" :enableUserDeleteEmail="openSettings.enableUserDeleteEmail"
|
||||
:deleteMail="deleteSenboxMail" />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="sendmail" :tab="t('sendmail')">
|
||||
<SendMail />
|
||||
|
||||
@@ -268,7 +268,7 @@ onMounted(async () => {
|
||||
<span>
|
||||
<p>{{ t("addressCredentialTip") }}</p>
|
||||
</span>
|
||||
<n-card>
|
||||
<n-card :bordered="false" embedded>
|
||||
<b>{{ curEmailCredential }}</b>
|
||||
</n-card>
|
||||
<template #action>
|
||||
@@ -296,7 +296,7 @@ onMounted(async () => {
|
||||
</template>
|
||||
</n-pagination>
|
||||
</div>
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" />
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" embedded />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card style="max-width: 600px;">
|
||||
<n-card :bordered="false" embedded style="max-width: 600px;">
|
||||
<n-form-item-row :label="t('address_block_list')">
|
||||
<n-select v-model:value="addressBlockList" filterable multiple tag
|
||||
:placeholder="t('address_block_list_placeholder')" />
|
||||
|
||||
@@ -71,11 +71,11 @@ onMounted(async () => {
|
||||
<div class="center">
|
||||
<n-modal v-model:show="showReultModal" preset="dialog" :title="t('addressCredential')">
|
||||
<p>{{ t('addressCredential') }}</p>
|
||||
<n-card>
|
||||
<n-card :bordered="false" embedded>
|
||||
<b>{{ result }}</b>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<n-card style="max-width: 600px;">
|
||||
<n-card :bordered="false" embedded style="max-width: 600px;">
|
||||
<n-form-item-row v-if="openSettings.prefix" :label="t('enablePrefix')">
|
||||
<n-checkbox v-model:checked="enablePrefix" />
|
||||
</n-form-item-row>
|
||||
|
||||
@@ -48,6 +48,10 @@ const fetchMailData = async (limit, offset) => {
|
||||
);
|
||||
}
|
||||
|
||||
const deleteMail = async (curMailId) => {
|
||||
await api.fetch(`/admin/mails/${curMailId}`, { method: 'DELETE' });
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
if (!adminAuth.value) {
|
||||
showAdminAuth.value = true;
|
||||
@@ -66,6 +70,7 @@ onMounted(async () => {
|
||||
</n-button>
|
||||
</n-input-group>
|
||||
<div style="margin-top: 10px;"></div>
|
||||
<MailBox :key="mailBoxKey" :enableUserDeleteEmail="false" :fetchMailData="fetchMailData" />
|
||||
<MailBox :key="mailBoxKey" :enableUserDeleteEmail="true" :fetchMailData="fetchMailData"
|
||||
:deleteMail="deleteMail" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -15,6 +15,10 @@ const fetchMailUnknowData = async (limit, offset) => {
|
||||
);
|
||||
}
|
||||
|
||||
const deleteMail = async (curMailId) => {
|
||||
await api.fetch(`/api/mails/${curMailId}`, { method: 'DELETE' });
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
if (!adminAuth.value) {
|
||||
showAdminAuth.value = true;
|
||||
@@ -25,6 +29,6 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div v-if="adminAuth" style="margin-top: 10px;">
|
||||
<MailBox :enableUserDeleteEmail="false" :fetchMailData="fetchMailUnknowData" />
|
||||
<MailBox :enableUserDeleteEmail="true" :fetchMailData="fetchMailUnknowData" :deleteMail="deleteMail" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -91,8 +91,8 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card>
|
||||
<n-alert :show-icon="false">
|
||||
<n-card :bordered="false" embedded>
|
||||
<n-alert :show-icon="false" :bordered="false">
|
||||
<span>{{ t('cronTip') }}</span>
|
||||
</n-alert>
|
||||
<n-form :model="cleanupModel">
|
||||
|
||||
@@ -26,6 +26,10 @@ const fetchData = async (limit, offset) => {
|
||||
+ (adminSendBoxTabAddress.value ? `&address=${adminSendBoxTabAddress.value}` : '')
|
||||
);
|
||||
}
|
||||
|
||||
const deleteSenboxMail = async (curMailId) => {
|
||||
await api.fetch(`/admin/sendbox/${curMailId}`, { method: 'DELETE' });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -36,7 +40,8 @@ const fetchData = async (limit, offset) => {
|
||||
{{ t('query') }}
|
||||
</n-button>
|
||||
</n-input-group>
|
||||
<SendBox style="margin-top: 10px;" :fetchMailData="fetchData" :showEMailFrom="true" />
|
||||
<SendBox style="margin-top: 10px;" :enableUserDeleteEmail="true" :deleteMail="deleteSenboxMail"
|
||||
:fetchMailData="fetchData" :showEMailFrom="true" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ const { t } = useI18n({
|
||||
enable: 'Enable',
|
||||
disable: 'Disable',
|
||||
modify: 'Modify',
|
||||
delete: 'Delete',
|
||||
deleteTip: 'Are you sure to delete this?',
|
||||
created_at: 'Created At',
|
||||
action: 'Action',
|
||||
itemCount: 'itemCount',
|
||||
@@ -32,6 +34,8 @@ const { t } = useI18n({
|
||||
enable: '启用',
|
||||
disable: '禁用',
|
||||
modify: '修改',
|
||||
delete: '删除',
|
||||
deleteTip: '确定删除吗?',
|
||||
created_at: '创建时间',
|
||||
action: '操作',
|
||||
itemCount: '总数',
|
||||
@@ -134,7 +138,25 @@ const columns = [
|
||||
}
|
||||
},
|
||||
{ default: () => t('modify') }
|
||||
)
|
||||
),
|
||||
h(NPopconfirm,
|
||||
{
|
||||
onPositiveClick: async () => {
|
||||
await api.fetch(`/admin/address_sender/${row.id}`, { method: 'DELETE' });
|
||||
await fetchData();
|
||||
}
|
||||
},
|
||||
{
|
||||
trigger: () => h(NButton,
|
||||
{
|
||||
tertiary: true,
|
||||
type: "error",
|
||||
},
|
||||
{ default: () => t('delete') }
|
||||
),
|
||||
default: () => t('deleteTip')
|
||||
}
|
||||
),
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -183,7 +205,7 @@ onMounted(async () => {
|
||||
</template>
|
||||
</n-pagination>
|
||||
</div>
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" />
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" embedded />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-card>
|
||||
<n-card :bordered="false" embedded>
|
||||
<n-row>
|
||||
<n-col :span="6">
|
||||
<n-statistic :label="t('userCount')" :value="statistics.userCount">
|
||||
|
||||
@@ -112,8 +112,8 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card style="max-width: 800px; overflow: auto;">
|
||||
<n-card>
|
||||
<n-card :bordered="false" embedded style="max-width: 800px; overflow: auto;">
|
||||
<n-card :bordered="false" embedded>
|
||||
<n-form-item-row :label="t('enableTelegramAllowList')">
|
||||
<n-input-group>
|
||||
<n-checkbox v-model:checked="settings.enableAllowList" style="width: 20%;">
|
||||
|
||||
@@ -276,7 +276,7 @@ onMounted(async () => {
|
||||
</template>
|
||||
</n-pagination>
|
||||
</div>
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" />
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" embedded />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card style="max-width: 600px;">
|
||||
<n-card :bordered="false" embedded style="max-width: 600px;">
|
||||
<n-form :model="userSettings">
|
||||
<n-form-item-row :label="t('enableUserRegister')">
|
||||
<n-checkbox v-model:checked="userSettings.enable" />
|
||||
|
||||
@@ -62,7 +62,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card style="max-width: 800px; overflow: auto;">
|
||||
<n-card :bordered="false" embedded style="max-width: 800px; overflow: auto;">
|
||||
<n-form-item-row :label="t('webhookAllowList')">
|
||||
<n-select v-model:value="webhookSettings.allowList" filterable multiple tag
|
||||
:placeholder="t('webhookAllowList')" />
|
||||
|
||||
@@ -4,7 +4,7 @@ import { GithubAlt, Discord, Telegram } from '@vicons/fa'
|
||||
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card>
|
||||
<n-card :bordered="false" embedded>
|
||||
<n-button tag="a" target="_blank" href="https://github.com/dreamhunter2333/cloudflare_temp_email">
|
||||
<template #icon>
|
||||
<n-icon :component="GithubAlt" />
|
||||
|
||||
@@ -16,7 +16,7 @@ const { t } = useI18n({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-alert v-if="openSettings.adminContact" :show-icon="false">
|
||||
<n-alert v-if="openSettings.adminContact" :show-icon="false" :bordered="false">
|
||||
<span>{{ t('adminContact', { msg: openSettings.adminContact }) }}</span>
|
||||
</n-alert>
|
||||
</template>
|
||||
|
||||
@@ -40,7 +40,7 @@ const { t } = useI18n({
|
||||
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card>
|
||||
<n-card :bordered="false" embedded>
|
||||
<n-form-item-row v-if="!isMobile" :label="t('mailboxSplitSize')">
|
||||
<n-slider v-model:value="mailboxSplitSize" :min="0.25" :max="0.75" :step="0.01" :marks="{
|
||||
0.25: '0.25',
|
||||
|
||||
@@ -150,7 +150,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<n-alert v-if="userSettings.user_email" :show-icon="false" closable>
|
||||
<n-alert v-if="userSettings.user_email" :show-icon="false" :bordered="false" closable>
|
||||
<span>{{ t('bindUserInfo') }}</span>
|
||||
</n-alert>
|
||||
<n-tabs v-model:value="tabValue" size="large" justify-content="space-evenly">
|
||||
@@ -206,7 +206,7 @@ onMounted(async () => {
|
||||
</n-spin>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="help" :tab="t('help')">
|
||||
<n-alert :show-icon="false">
|
||||
<n-alert :show-icon="false" :bordered="false">
|
||||
<span>{{ t('pleaseGetNewEmail') }}</span>
|
||||
</n-alert>
|
||||
<AdminContact />
|
||||
|
||||
@@ -59,7 +59,7 @@ const deleteAccount = async () => {
|
||||
|
||||
<template>
|
||||
<div class="center" v-if="settings.address">
|
||||
<n-card>
|
||||
<n-card :bordered="false" embedded>
|
||||
<Appearance />
|
||||
<n-button @click="showAddressCredential = true" type="primary" secondary block strong>
|
||||
{{ t('showAddressCredential') }}
|
||||
|
||||
@@ -84,11 +84,11 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<n-card v-if="!settings.fetched">
|
||||
<n-card :bordered="false" embedded v-if="!settings.fetched">
|
||||
<n-skeleton style="height: 50vh" />
|
||||
</n-card>
|
||||
<div v-else-if="settings.address">
|
||||
<n-alert type="info" :show-icon="false">
|
||||
<n-alert type="info" :show-icon="false" :bordered="false">
|
||||
<span>
|
||||
<b>{{ addressLabel }}</b>
|
||||
<n-button v-if="isTelegram" style="margin-left: 10px" @click="showTelegramChangeAddress = true"
|
||||
@@ -113,8 +113,8 @@ onMounted(async () => {
|
||||
<TelegramAddress />
|
||||
</div>
|
||||
<div v-else class="center">
|
||||
<n-card style="max-width: 600px;">
|
||||
<n-alert v-if="jwt" type="warning" :show-icon="false" closable>
|
||||
<n-card :bordered="false" embedded style="max-width: 600px;">
|
||||
<n-alert v-if="jwt" type="warning" :show-icon="false" :bordered="false" closable>
|
||||
<span>{{ t('fetchAddressError') }}</span>
|
||||
</n-alert>
|
||||
<Login />
|
||||
@@ -140,7 +140,7 @@ onMounted(async () => {
|
||||
<span>
|
||||
<p>{{ t("addressCredentialTip") }}</p>
|
||||
</span>
|
||||
<n-card>
|
||||
<n-card :bordered="false" embedded>
|
||||
<b>{{ jwt }}</b>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
|
||||
@@ -86,6 +86,6 @@ onMounted(async () => {
|
||||
{{ t('download') }}
|
||||
</n-button>
|
||||
</n-modal>
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" />
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" embedded />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -81,7 +81,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card v-if="settings.address" :title='t("settings")'>
|
||||
<n-card :bordered="false" embedded v-if="settings.address" :title='t("settings")'>
|
||||
<div class="right">
|
||||
<n-button type="primary" @click="saveData">{{ t('save') }}</n-button>
|
||||
</div>
|
||||
|
||||
@@ -144,12 +144,12 @@ const columns = [
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<n-alert type="warning" :show-icon="false">
|
||||
<n-alert type="warning" :show-icon="false" :bordered="false">
|
||||
<span>{{ t('tip') }}</span>
|
||||
</n-alert>
|
||||
<n-tabs type="segment" v-model:value="tabValue">
|
||||
<n-tab-pane name="address" :tab="t('address')">
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" />
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" embedded />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="bind" :tab="t('bind')">
|
||||
<Login :bindUserAddress="bindAddress" />
|
||||
|
||||
@@ -142,9 +142,9 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center" v-if="settings.address">
|
||||
<n-card>
|
||||
<n-card :bordered="false" embedded>
|
||||
<div v-if="!settings.send_balance || settings.send_balance <= 0">
|
||||
<n-alert type="warning" :show-icon="false">
|
||||
<n-alert type="warning" :show-icon="false" :bordered="false">
|
||||
{{ t('requestAccessTip') }}
|
||||
<n-button type="primary" tertiary @click="requestAccess" size="small">{{ t('requestAccess')
|
||||
}}</n-button>
|
||||
@@ -153,7 +153,7 @@ onMounted(async () => {
|
||||
<AdminContact />
|
||||
</div>
|
||||
<div v-else>
|
||||
<n-alert type="info" :show-icon="false">
|
||||
<n-alert type="info" :show-icon="false" :bordered="false">
|
||||
{{ t('send_balance') }}: {{ settings.send_balance }}
|
||||
</n-alert>
|
||||
<div class="right">
|
||||
@@ -187,7 +187,7 @@ onMounted(async () => {
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item :label="t('content')" label-placement="top">
|
||||
<n-card v-if="isPreview">
|
||||
<n-card :bordered="false" embedded v-if="isPreview">
|
||||
<div v-html="sendMailModel.content" />
|
||||
</n-card>
|
||||
<div v-else-if="sendMailModel.contentType == 'rich'" style="border: 1px solid #ccc">
|
||||
|
||||
@@ -148,7 +148,7 @@ onMounted(async () => {
|
||||
<div>
|
||||
<n-tabs type="segment">
|
||||
<n-tab-pane name="address" :tab="t('address')">
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" />
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" embedded />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="bind" :tab="t('bind')">
|
||||
<Login :newAddressPath="newAddressPath" :bindUserAddress="bindAddress" />
|
||||
|
||||
@@ -89,7 +89,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center" v-if="settings.address">
|
||||
<n-card v-if="enableWebhook" style="max-width: 800px; overflow: auto;">
|
||||
<n-card :bordered="false" embedded v-if="enableWebhook" style="max-width: 800px; overflow: auto;">
|
||||
<n-form-item-row label="URL">
|
||||
<n-input v-model:value="webhookSettings.url" />
|
||||
</n-form-item-row>
|
||||
|
||||
@@ -41,7 +41,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card v-if="curMail.message" style="max-width: 800px; overflow: auto;">
|
||||
<n-card :bordered="false" embedded v-if="curMail.message" style="max-width: 800px; overflow: auto;">
|
||||
<n-tag type="info">
|
||||
ID: {{ curMail.id }}
|
||||
</n-tag>
|
||||
|
||||
@@ -165,6 +165,6 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" />
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" embedded />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -29,7 +29,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center" v-if="userSettings.user_email">
|
||||
<n-card style="max-width: 600px;">
|
||||
<n-card :bordered="false" embedded style="max-width: 600px;">
|
||||
<Login />
|
||||
</n-card>
|
||||
</div>
|
||||
|
||||
@@ -37,19 +37,19 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<n-card v-if="!userSettings.fetched">
|
||||
<n-card :bordered="false" embedded v-if="!userSettings.fetched">
|
||||
<n-skeleton style="height: 50vh" />
|
||||
</n-card>
|
||||
<div v-else-if="userSettings.user_email">
|
||||
<n-alert type="success" :show-icon="false">
|
||||
<n-alert type="success" :show-icon="false" :bordered="false">
|
||||
<span>
|
||||
<b>{{ t('currentUser') }} <b>{{ userSettings.user_email }}</b></b>
|
||||
</span>
|
||||
</n-alert>
|
||||
</div>
|
||||
<div v-else class="center">
|
||||
<n-card style="max-width: 600px;">
|
||||
<n-alert v-if="userJwt" type="warning" :show-icon="false" closable>
|
||||
<n-card :bordered="false" embedded style="max-width: 600px;">
|
||||
<n-alert v-if="userJwt" type="warning" :show-icon="false" :bordered="false" closable>
|
||||
<span>{{ t('fetchUserSettingsError') }}</span>
|
||||
</n-alert>
|
||||
<UserLogin />
|
||||
|
||||
@@ -228,7 +228,7 @@ onMounted(async () => {
|
||||
{{ t('resetPassword') }}
|
||||
</n-button>
|
||||
</n-form>
|
||||
<n-alert v-else :show-icon="false">
|
||||
<n-alert v-else :show-icon="false" :bordered="false">
|
||||
<span>
|
||||
{{ t('cannotForgotPassword') }}
|
||||
</span>
|
||||
|
||||
@@ -43,8 +43,8 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center" v-if="userSettings.user_email">
|
||||
<n-card>
|
||||
<n-alert :show-icon="false">
|
||||
<n-card :bordered="false" embedded>
|
||||
<n-alert :show-icon="false" :bordered="false">
|
||||
<span>
|
||||
{{ t('passordTip') }}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user