feat: 邮箱管理

pull/66/head
puhui999 2025-04-04 12:54:03 +08:00
parent 28d1df74ad
commit 3d692a583d
13 changed files with 150 additions and 154 deletions

View File

@ -1,10 +1,10 @@
import type { PageResult } from '@vben/request';
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
// TODO @puhui999代码风格的统一
export namespace SystemMailAccountApi {
export interface MailAccountVO {
/** 邮箱信息 */
export interface MailAccount {
id: number;
mail: string;
username: string;
@ -19,16 +19,17 @@ export namespace SystemMailAccountApi {
}
}
// 查询邮箱账号列表
export const getMailAccountPage = async (params: any) => {
return await requestClient.get<
PageResult<SystemMailAccountApi.MailAccountVO>
>('/system/mail-account/page', { params });
/** 查询邮箱账号列表 */
export const getMailAccountPage = async (params: PageParam) => {
return await requestClient.get<PageResult<SystemMailAccountApi.MailAccount>>(
'/system/mail-account/page',
{ params },
);
};
// 查询邮箱账号详情
/** 查询邮箱账号详情 */
export const getMailAccount = async (id: number) => {
return await requestClient.get<SystemMailAccountApi.MailAccountVO>(
return await requestClient.get<SystemMailAccountApi.MailAccount>(
'/system/mail-account/get',
{
params: { id },
@ -36,41 +37,41 @@ export const getMailAccount = async (id: number) => {
);
};
// 新增邮箱账号
/** 新增邮箱账号 */
export const createMailAccount = async (
data: SystemMailAccountApi.MailAccountVO,
data: SystemMailAccountApi.MailAccount,
) => {
return await requestClient.post<SystemMailAccountApi.MailAccountVO>(
return await requestClient.post<SystemMailAccountApi.MailAccount>(
'/system/mail-account/create',
data,
);
};
// 修改邮箱账号
/** 修改邮箱账号 */
export const updateMailAccount = async (
data: SystemMailAccountApi.MailAccountVO,
data: SystemMailAccountApi.MailAccount,
) => {
return await requestClient.put<SystemMailAccountApi.MailAccountVO>(
return await requestClient.put<SystemMailAccountApi.MailAccount>(
'/system/mail-account/update',
data,
);
};
// 删除邮箱账号
/** 删除邮箱账号 */
export const deleteMailAccount = async (id: number) => {
return await requestClient.delete<boolean>('/system/mail-account/delete', {
params: { id },
});
};
// 获得邮箱账号精简列表
/** 获得邮箱账号精简列表 */
export const getSimpleMailAccountList = async () => {
return await requestClient.get<SystemMailAccountApi.MailAccountVO[]>(
return await requestClient.get<SystemMailAccountApi.MailAccount[]>(
'/system/mail-account/simple-list',
);
};
// 测试邮箱连接
/** 测试邮箱连接 */
export const testMailAccount = async (id: number) => {
return await requestClient.post<boolean>('/system/mail-account/test', null, {
params: { id },

View File

@ -1,10 +1,10 @@
import type { PageResult } from '@vben/request';
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
// TODO @puhui999代码风格的统一
export namespace SystemMailLogApi {
export interface MailLogVO {
/** 邮件日志 */
export interface MailLog {
id: number;
userId: number;
userType: number;
@ -18,24 +18,24 @@ export namespace SystemMailLogApi {
templateContent: string;
templateParams: string;
sendStatus: number;
sendTime: Date;
sendTime: string;
sendMessageId: string;
sendException: string;
createTime: Date;
createTime: string;
}
}
// 查询邮件日志列表
export const getMailLogPage = async (params: any) => {
return await requestClient.get<PageResult<SystemMailLogApi.MailLogVO>>(
/** 查询邮件日志列表 */
export const getMailLogPage = async (params: PageParam) => {
return await requestClient.get<PageResult<SystemMailLogApi.MailLog>>(
'/system/mail-log/page',
{ params },
);
};
// 查询邮件日志详情
/** 查询邮件日志详情 */
export const getMailLog = async (id: number) => {
return await requestClient.get<SystemMailLogApi.MailLogVO>(
return await requestClient.get<SystemMailLogApi.MailLog>(
'/system/mail-log/get',
{
params: { id },
@ -43,14 +43,14 @@ export const getMailLog = async (id: number) => {
);
};
// 重新发送邮件
/** 重新发送邮件 */
export const resendMail = async (id: number) => {
return await requestClient.put<boolean>('/system/mail-log/resend', null, {
params: { id },
});
};
// 批量删除邮件日志
/** 批量删除邮件日志 */
export const deleteMailLogs = async (ids: number[]) => {
return await requestClient.delete<boolean>('/system/mail-log/delete', {
data: { ids },

View File

@ -1,10 +1,10 @@
import type { PageResult } from '@vben/request';
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
// TODO @puhui999代码风格的统一
export namespace SystemMailTemplateApi {
export interface MailTemplateVO {
/** 邮件模版信息 */
export interface MailTemplate {
id: number;
name: string;
code: string;
@ -18,23 +18,24 @@ export namespace SystemMailTemplateApi {
createTime: Date;
}
export interface MailSendReqVO {
/** 邮件发送信息 */
export interface MailSendReq {
mail: string;
templateCode: string;
templateParams: Record<string, any>;
}
}
// 查询邮件模版列表
export const getMailTemplatePage = async (params: any) => {
/** 查询邮件模版列表 */
export const getMailTemplatePage = async (params: PageParam) => {
return await requestClient.get<
PageResult<SystemMailTemplateApi.MailTemplateVO>
PageResult<SystemMailTemplateApi.MailTemplate>
>('/system/mail-template/page', { params });
};
// 查询邮件模版详情
/** 查询邮件模版详情 */
export const getMailTemplate = async (id: number) => {
return await requestClient.get<SystemMailTemplateApi.MailTemplateVO>(
return await requestClient.get<SystemMailTemplateApi.MailTemplate>(
'/system/mail-template/get',
{
params: { id },
@ -42,35 +43,35 @@ export const getMailTemplate = async (id: number) => {
);
};
// 新增邮件模版
/** 新增邮件模版 */
export const createMailTemplate = async (
data: SystemMailTemplateApi.MailTemplateVO,
data: SystemMailTemplateApi.MailTemplate,
) => {
return await requestClient.post<SystemMailTemplateApi.MailTemplateVO>(
return await requestClient.post<SystemMailTemplateApi.MailTemplate>(
'/system/mail-template/create',
data,
);
};
// 修改邮件模版
/** 修改邮件模版 */
export const updateMailTemplate = async (
data: SystemMailTemplateApi.MailTemplateVO,
data: SystemMailTemplateApi.MailTemplate,
) => {
return await requestClient.put<SystemMailTemplateApi.MailTemplateVO>(
return await requestClient.put<SystemMailTemplateApi.MailTemplate>(
'/system/mail-template/update',
data,
);
};
// 删除邮件模版
/** 删除邮件模版 */
export const deleteMailTemplate = async (id: number) => {
return await requestClient.delete<boolean>('/system/mail-template/delete', {
params: { id },
});
};
// 发送邮件
export const sendMail = async (data: SystemMailTemplateApi.MailSendReqVO) => {
/** 发送邮件 */
export const sendMail = async (data: SystemMailTemplateApi.MailSendReq) => {
return await requestClient.post<boolean>(
'/system/mail-template/send-mail',
data,

View File

@ -106,7 +106,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemMailAccountApi.MailAccountVO>(
export function useGridColumns<T = SystemMailAccountApi.MailAccount>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
return [

View File

@ -37,12 +37,12 @@ function onCreate() {
}
/** 编辑邮箱账号 */
function onEdit(row: SystemMailAccountApi.MailAccountVO) {
function onEdit(row: SystemMailAccountApi.MailAccount) {
formModalApi.setData(row).open();
}
/** 测试邮箱连接 */
async function onTest(row: SystemMailAccountApi.MailAccountVO) {
async function onTest(row: SystemMailAccountApi.MailAccount) {
const hideLoading = message.loading({
content: '正在测试邮箱连接...',
duration: 0,
@ -60,7 +60,7 @@ async function onTest(row: SystemMailAccountApi.MailAccountVO) {
}
/** 删除邮箱账号 */
async function onDelete(row: SystemMailAccountApi.MailAccountVO) {
async function onDelete(row: SystemMailAccountApi.MailAccount) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.mail]),
duration: 0,
@ -82,7 +82,7 @@ async function onDelete(row: SystemMailAccountApi.MailAccountVO) {
function onActionClick({
code,
row,
}: OnActionClickParams<SystemMailAccountApi.MailAccountVO>) {
}: OnActionClickParams<SystemMailAccountApi.MailAccount>) {
switch (code) {
case 'delete': {
onDelete(row);
@ -125,7 +125,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
refresh: { code: 'query' },
search: true,
},
} as VxeTableGridOptions<SystemMailAccountApi.MailAccountVO>,
} as VxeTableGridOptions<SystemMailAccountApi.MailAccount>,
});
</script>
<template>

View File

@ -18,7 +18,7 @@ import { $t } from '#/locales';
import { useFormSchema } from '../data';
const emit = defineEmits(['success']);
const formData = ref<SystemMailAccountApi.MailAccountVO>();
const formData = ref<SystemMailAccountApi.MailAccount>();
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', ['邮箱账号'])
@ -40,7 +40,7 @@ const [Modal, modalApi] = useVbenModal({
modalApi.lock();
//
const data =
(await formApi.getValues()) as SystemMailAccountApi.MailAccountVO;
(await formApi.getValues()) as SystemMailAccountApi.MailAccount;
try {
await (formData.value?.id
? updateMailAccount(data)
@ -61,7 +61,7 @@ const [Modal, modalApi] = useVbenModal({
return;
}
//
const data = modalApi.getData<SystemMailAccountApi.MailAccountVO>();
const data = modalApi.getData<SystemMailAccountApi.MailAccount>();
if (!data || !data.id) {
return;
}

View File

@ -64,7 +64,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemMailLogApi.MailLogVO>(
export function useGridColumns<T = SystemMailLogApi.MailLog>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
return [

View File

@ -26,12 +26,12 @@ function onRefresh() {
}
/** 查看邮件日志详情 */
function onView(row: SystemMailLogApi.MailLogVO) {
function onView(row: SystemMailLogApi.MailLog) {
formModalApi.setData(row).open();
}
/** 重新发送邮件 */
async function onResend(row: SystemMailLogApi.MailLogVO) {
async function onResend(row: SystemMailLogApi.MailLog) {
const hideLoading = message.loading({
content: '重新发送邮件中...',
duration: 0,
@ -53,7 +53,7 @@ async function onResend(row: SystemMailLogApi.MailLogVO) {
function onActionClick({
code,
row,
}: OnActionClickParams<SystemMailLogApi.MailLogVO>) {
}: OnActionClickParams<SystemMailLogApi.MailLog>) {
switch (code) {
case 'resend': {
onResend(row);
@ -92,7 +92,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
refresh: { code: 'query' },
search: true,
},
} as VxeTableGridOptions<SystemMailLogApi.MailLogVO>,
} as VxeTableGridOptions<SystemMailLogApi.MailLog>,
});
</script>
<template>

View File

@ -4,8 +4,13 @@ import type { SystemMailLogApi } from '#/api/system/mail/log';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { formatDateTime } from '@vben/utils';
const formData = ref<SystemMailLogApi.MailLogVO>();
import { Descriptions, Tag } from 'ant-design-vue';
import { DICT_TYPE, getDictLabel } from '#/utils/dict';
const formData = ref<SystemMailLogApi.MailLog>();
const getTitle = computed(() => {
return '邮件日志详情';
});
@ -16,7 +21,7 @@ const [Modal, modalApi] = useVbenModal({
return;
}
//
const data = modalApi.getData<SystemMailLogApi.MailLogVO>();
const data = modalApi.getData<SystemMailLogApi.MailLog>();
if (!data || !data.id) {
return;
}
@ -33,79 +38,56 @@ const [Modal, modalApi] = useVbenModal({
<template>
<Modal :title="getTitle">
<div class="p-4">
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<div class="form-item">
<div class="form-label">编号</div>
<div>{{ formData?.id }}</div>
</div>
<div class="form-item">
<div class="form-label">创建时间</div>
<div>{{ formData?.createTime }}</div>
</div>
<div class="form-item">
<div class="form-label">收件邮箱</div>
<div>{{ formData?.toMail }}</div>
</div>
<div class="form-item">
<div class="form-label">发送邮箱</div>
<div>{{ formData?.fromMail }}</div>
</div>
<div class="form-item">
<div class="form-label">用户编号</div>
<div>{{ formData?.userId }}</div>
</div>
<div class="form-item">
<div class="form-label">用户类型</div>
<div>{{ formData?.userType }}</div>
</div>
<div class="form-item">
<div class="form-label">模板编号</div>
<div>{{ formData?.templateId }}</div>
</div>
<div class="form-item">
<div class="form-label">模板编码</div>
<div>{{ formData?.templateCode }}</div>
</div>
</div>
<div class="mt-4">
<div class="form-label">邮件标题</div>
<div>{{ formData?.templateTitle }}</div>
</div>
<div class="mt-4">
<div class="form-label">邮件内容</div>
<Descriptions :column="2" bordered>
<Descriptions.Item label="编号">{{ formData?.id }}</Descriptions.Item>
<Descriptions.Item label="创建时间">
{{ formatDateTime(formData?.createTime || '') }}
</Descriptions.Item>
<Descriptions.Item label="收件邮箱">
{{ formData?.toMail }}
</Descriptions.Item>
<Descriptions.Item label="发送邮箱">
{{ formData?.fromMail }}
</Descriptions.Item>
<Descriptions.Item label="用户编号">
{{ formData?.userId }}
</Descriptions.Item>
<Descriptions.Item label="用户类型">
{{ formData?.userType }}
</Descriptions.Item>
<Descriptions.Item label="模板编号">
{{ formData?.templateId }}
</Descriptions.Item>
<Descriptions.Item label="模板编码">
{{ formData?.templateCode }}
</Descriptions.Item>
<Descriptions.Item label="邮件标题" :span="2">
{{ formData?.templateTitle }}
</Descriptions.Item>
<Descriptions.Item label="邮件内容" :span="2">
<div v-html="formData?.templateContent"></div>
</div>
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2">
<div class="form-item">
<div class="form-label">发送状态</div>
<div>{{ formData?.sendStatus }}</div>
</div>
<div class="form-item">
<div class="form-label">发送时间</div>
<div>{{ formData?.sendTime }}</div>
</div>
<div class="form-item">
<div class="form-label">发送消息编号</div>
<div>{{ formData?.sendMessageId }}</div>
</div>
<div class="form-item">
<div class="form-label">发送异常</div>
<div>{{ formData?.sendException }}</div>
</div>
</div>
</Descriptions.Item>
<Descriptions.Item label="发送状态">
<!-- TODO @芋艿: 数据字典-->
<Tag color="processing">
{{
getDictLabel(
DICT_TYPE.SYSTEM_MAIL_SEND_STATUS,
formData?.sendStatus,
)
}}
</Tag>
</Descriptions.Item>
<Descriptions.Item label="发送时间">
{{ formatDateTime(formData?.sendTime || '') }}
</Descriptions.Item>
<Descriptions.Item label="发送消息编号">
{{ formData?.sendMessageId }}
</Descriptions.Item>
<Descriptions.Item label="发送异常">
{{ formData?.sendException }}
</Descriptions.Item>
</Descriptions>
</div>
</Modal>
</template>
<style scoped>
.form-item {
@apply mb-2;
}
.form-label {
@apply font-medium;
}
</style>

View File

@ -54,11 +54,10 @@ export function useFormSchema(): VbenFormSchema[] {
component: 'Input',
rules: 'required',
},
// TODO puhui999: 富文本组件缺失
{
fieldName: 'content',
label: '模板内容',
component: 'Editor',
component: 'Textarea',
componentProps: {
height: 300,
},
@ -130,6 +129,14 @@ export function useGridFormSchema(): VbenFormSchema[] {
/** 发送邮件表单 */
export function useSendMailFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'content',
label: '模板内容',
component: 'Textarea',
componentProps: {
disabled: true,
},
},
{
fieldName: 'mail',
label: '收件邮箱',
@ -149,7 +156,7 @@ export function useSendMailFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemMailTemplateApi.MailTemplateVO>(
export function useGridColumns<T = SystemMailTemplateApi.MailTemplate>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
return [

View File

@ -42,17 +42,17 @@ function onCreate() {
}
/** 编辑邮件模板 */
function onEdit(row: SystemMailTemplateApi.MailTemplateVO) {
function onEdit(row: SystemMailTemplateApi.MailTemplate) {
formModalApi.setData(row).open();
}
/** 发送测试邮件 */
function onSend(row: SystemMailTemplateApi.MailTemplateVO) {
function onSend(row: SystemMailTemplateApi.MailTemplate) {
sendModalApi.setData(row).open();
}
/** 删除邮件模板 */
async function onDelete(row: SystemMailTemplateApi.MailTemplateVO) {
async function onDelete(row: SystemMailTemplateApi.MailTemplate) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
@ -74,7 +74,7 @@ async function onDelete(row: SystemMailTemplateApi.MailTemplateVO) {
function onActionClick({
code,
row,
}: OnActionClickParams<SystemMailTemplateApi.MailTemplateVO>) {
}: OnActionClickParams<SystemMailTemplateApi.MailTemplate>) {
switch (code) {
case 'delete': {
onDelete(row);
@ -117,7 +117,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
refresh: { code: 'query' },
search: true,
},
} as VxeTableGridOptions<SystemMailTemplateApi.MailTemplateVO>,
} as VxeTableGridOptions<SystemMailTemplateApi.MailTemplate>,
});
</script>
<template>

View File

@ -18,7 +18,7 @@ import { $t } from '#/locales';
import { useFormSchema } from '../data';
const emit = defineEmits(['success']);
const formData = ref<SystemMailTemplateApi.MailTemplateVO>();
const formData = ref<SystemMailTemplateApi.MailTemplate>();
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', ['邮件模板'])
@ -40,7 +40,7 @@ const [Modal, modalApi] = useVbenModal({
modalApi.lock();
//
const data =
(await formApi.getValues()) as SystemMailTemplateApi.MailTemplateVO;
(await formApi.getValues()) as SystemMailTemplateApi.MailTemplate;
try {
await (formData.value?.id
? updateMailTemplate(data)
@ -61,7 +61,7 @@ const [Modal, modalApi] = useVbenModal({
return;
}
//
const data = modalApi.getData<SystemMailTemplateApi.MailTemplateVO>();
const data = modalApi.getData<SystemMailTemplateApi.MailTemplate>();
if (!data || !data.id) {
return;
}

View File

@ -14,7 +14,7 @@ import { $t } from '#/locales';
import { useSendMailFormSchema } from '../data';
const emit = defineEmits(['success']);
const templateData = ref<SystemMailTemplateApi.MailTemplateVO>();
const templateData = ref<SystemMailTemplateApi.MailTemplate>();
const getTitle = computed(() => {
return $t('ui.actionTitle.send', ['邮件']);
});
@ -63,7 +63,7 @@ const [Modal, modalApi] = useVbenModal({
}
//
const data: SystemMailTemplateApi.MailSendReqVO = {
const data: SystemMailTemplateApi.MailSendReq = {
mail: values.mail,
templateCode: templateData.value?.code || '',
templateParams: paramsObj,
@ -89,7 +89,7 @@ const [Modal, modalApi] = useVbenModal({
return;
}
//
const data = modalApi.getData<SystemMailTemplateApi.MailTemplateVO>();
const data = modalApi.getData<SystemMailTemplateApi.MailTemplate>();
if (!data) {
return;
}
@ -97,7 +97,12 @@ const [Modal, modalApi] = useVbenModal({
templateData.value = data;
//
const schema = buildSchema();
formApi.updateSchema(schema);
formApi.setState({ schema });
//
await formApi.setValues({
content: data.content,
});
},
});
</script>