Commit 4996d05d1e29bb8b85b95a973649d204c03ecc18

Authored by YunaiV
1 parent 4cbf7398

邮箱模块:vue3 邮件模版的管理

yudao-ui-admin-vue3/src/api/system/mail/account/index.ts
@@ -39,3 +39,8 @@ export const updateMailAccountApi = async (data: MailAccountVO) => { @@ -39,3 +39,8 @@ export const updateMailAccountApi = async (data: MailAccountVO) => {
39 export const deleteMailAccountApi = async (id: number) => { 39 export const deleteMailAccountApi = async (id: number) => {
40 return await request.delete({ url: '/system/mail-account/delete?id=' + id }) 40 return await request.delete({ url: '/system/mail-account/delete?id=' + id })
41 } 41 }
  42 +
  43 +// 获得邮箱账号精简列表
  44 +export const getSimpleMailAccounts = async () => {
  45 + return request.get({ url: '/system/mail-account/list-all-simple' })
  46 +}
yudao-ui-admin-vue3/src/api/system/mail/template/index.ts 0 → 100644
  1 +import request from '@/config/axios'
  2 +
  3 +export interface MailTemplateVO {
  4 + id: number
  5 + name: string
  6 + code: string
  7 + accountId: number
  8 + nickname: string
  9 + title: string
  10 + content: string
  11 + params: string
  12 + status: number
  13 + remark: string
  14 +}
  15 +
  16 +export interface MailTemplatePageReqVO extends PageParam {
  17 + name?: string
  18 + code?: string
  19 + accountId?: number
  20 + status?: number
  21 + createTime?: Date[]
  22 +}
  23 +
  24 +export interface MailSmsReqVO {
  25 + mail: string
  26 + templateCode: string
  27 + templateParams: Map<String, Object>
  28 +}
  29 +
  30 +// 查询邮件模版列表
  31 +export const getMailTemplatePageApi = async (params: MailTemplatePageReqVO) => {
  32 + return await request.get({ url: '/system/mail-template/page', params })
  33 +}
  34 +
  35 +// 查询邮件模版详情
  36 +export const getMailTemplateApi = async (id: number) => {
  37 + return await request.get({ url: '/system/mail-template/get?id=' + id })
  38 +}
  39 +
  40 +// 新增邮件模版
  41 +export const createMailTemplateApi = async (data: MailTemplateVO) => {
  42 + return await request.post({ url: '/system/mail-template/create', data })
  43 +}
  44 +
  45 +// 修改邮件模版
  46 +export const updateMailTemplateApi = async (data: MailTemplateVO) => {
  47 + return await request.put({ url: '/system/mail-template/update', data })
  48 +}
  49 +
  50 +// 删除邮件模版
  51 +export const deleteMailTemplateApi = async (id: number) => {
  52 + return await request.delete({ url: '/system/mail-template/delete?id=' + id })
  53 +}
  54 +
  55 +// 发送邮件
  56 +export const sendMailApi = (data: MailSmsReqVO) => {
  57 + return request.post({ url: '/system/mail-template/send-mail', data })
  58 +}
yudao-ui-admin-vue3/src/views/system/mail/account/account.template.data.ts renamed to yudao-ui-admin-vue3/src/views/system/mail/account/account.data.ts
yudao-ui-admin-vue3/src/views/system/mail/account/index.vue
@@ -69,7 +69,7 @@ @@ -69,7 +69,7 @@
69 <script setup lang="ts" name="MailAccount"> 69 <script setup lang="ts" name="MailAccount">
70 import { FormExpose } from '@/components/Form' 70 import { FormExpose } from '@/components/Form'
71 // 业务相关的 import 71 // 业务相关的 import
72 -import { rules, allSchemas } from './account.template.data' 72 +import { rules, allSchemas } from './account.data'
73 import * as MailAccountApi from '@/api/system/mail/account' 73 import * as MailAccountApi from '@/api/system/mail/account'
74 74
75 const { t } = useI18n() // 国际化 75 const { t } = useI18n() // 国际化
yudao-ui-admin-vue3/src/views/system/mail/template/index.vue 0 → 100644
  1 +<template>
  2 + <ContentWrap>
  3 + <!-- 列表 -->
  4 + <XTable @register="registerTable">
  5 + <template #toolbar_buttons>
  6 + <!-- 操作:新增 -->
  7 + <XButton
  8 + type="primary"
  9 + preIcon="ep:zoom-in"
  10 + :title="t('action.add')"
  11 + v-hasPermi="['system:mail-template:create']"
  12 + @click="handleCreate()"
  13 + />
  14 + </template>
  15 + <template #accountId_default="{ row }">
  16 + <span>{{ accountOptions.find((account) => account.id === row.accountId)?.mail }}</span>
  17 + </template>
  18 + <template #actionbtns_default="{ row }">
  19 + <!-- 操作:测试短信 -->
  20 + <XTextButton
  21 + preIcon="ep:cpu"
  22 + :title="t('action.test')"
  23 + v-hasPermi="['system:mail-template:send-mail']"
  24 + @click="handleSendMail(row)"
  25 + />
  26 + <!-- 操作:修改 -->
  27 + <XTextButton
  28 + preIcon="ep:edit"
  29 + :title="t('action.edit')"
  30 + v-hasPermi="['system:mail-template:update']"
  31 + @click="handleUpdate(row.id)"
  32 + />
  33 + <!-- 操作:详情 -->
  34 + <XTextButton
  35 + preIcon="ep:view"
  36 + :title="t('action.detail')"
  37 + v-hasPermi="['system:mail-template:query']"
  38 + @click="handleDetail(row.id)"
  39 + />
  40 + <!-- 操作:删除 -->
  41 + <XTextButton
  42 + preIcon="ep:delete"
  43 + :title="t('action.del')"
  44 + v-hasPermi="['system:mail-template:delete']"
  45 + @click="deleteData(row.id)"
  46 + />
  47 + </template>
  48 + </XTable>
  49 + </ContentWrap>
  50 +
  51 + <!-- 添加/修改/详情的弹窗 -->
  52 + <XModal id="mailTemplateModel" :loading="modelLoading" v-model="modelVisible" :title="modelTitle">
  53 + <!-- 表单:添加/修改 -->
  54 + <Form
  55 + ref="formRef"
  56 + v-if="['create', 'update'].includes(actionType)"
  57 + :schema="allSchemas.formSchema"
  58 + :rules="rules"
  59 + >
  60 + <template #accountId="form">
  61 + <el-select v-model="form.accountId">
  62 + <el-option
  63 + v-for="item in accountOptions"
  64 + :key="item.id"
  65 + :label="item.mail"
  66 + :value="item.id"
  67 + />
  68 + </el-select>
  69 + </template>
  70 + </Form>
  71 + <!-- 表单:详情 -->
  72 + <Descriptions
  73 + v-if="actionType === 'detail'"
  74 + :schema="allSchemas.detailSchema"
  75 + :data="detailData"
  76 + />
  77 + <template #footer>
  78 + <!-- 按钮:保存 -->
  79 + <XButton
  80 + v-if="['create', 'update'].includes(actionType)"
  81 + type="primary"
  82 + :title="t('action.save')"
  83 + :loading="actionLoading"
  84 + @click="submitForm()"
  85 + />
  86 + <!-- 按钮:关闭 -->
  87 + <XButton :loading="actionLoading" :title="t('dialog.close')" @click="modelVisible = false" />
  88 + </template>
  89 + </XModal>
  90 +
  91 + <!-- 测试邮件的弹窗 -->
  92 + <XModal id="sendTest" v-model="sendVisible" title="测试">
  93 + <el-form :model="sendForm" :rules="sendRules" label-width="200px" label-position="top">
  94 + <el-form-item label="模板内容" prop="content">
  95 + <Editor :model-value="sendForm.content" readonly height="150px" />
  96 + </el-form-item>
  97 + <el-form-item label="收件邮箱" prop="mail">
  98 + <el-input v-model="sendForm.mail" placeholder="请输入收件邮箱" />
  99 + </el-form-item>
  100 + <el-form-item
  101 + v-for="param in sendForm.params"
  102 + :key="param"
  103 + :label="'参数 {' + param + '}'"
  104 + :prop="'templateParams.' + param"
  105 + >
  106 + <el-input
  107 + v-model="sendForm.templateParams[param]"
  108 + :placeholder="'请输入 ' + param + ' 参数'"
  109 + />
  110 + </el-form-item>
  111 + </el-form>
  112 + <!-- 操作按钮 -->
  113 + <template #footer>
  114 + <XButton
  115 + type="primary"
  116 + :title="t('action.test')"
  117 + :loading="actionLoading"
  118 + @click="sendTest()"
  119 + />
  120 + <XButton :title="t('dialog.close')" @click="sendVisible = false" />
  121 + </template>
  122 + </XModal>
  123 +</template>
  124 +<script setup lang="ts" name="MailTemplate">
  125 +import { FormExpose } from '@/components/Form'
  126 +// 业务相关的 import
  127 +import { rules, allSchemas } from './template.data'
  128 +import * as MailTemplateApi from '@/api/system/mail/template'
  129 +import * as MailAccountApi from '@/api/system/mail/account'
  130 +
  131 +const { t } = useI18n() // 国际化
  132 +const message = useMessage() // 消息弹窗
  133 +
  134 +// 列表相关的变量
  135 +const [registerTable, { reload, deleteData }] = useXTable({
  136 + allSchemas: allSchemas,
  137 + getListApi: MailTemplateApi.getMailTemplatePageApi,
  138 + deleteApi: MailTemplateApi.deleteMailTemplateApi
  139 +})
  140 +const accountOptions = ref([]) // 账号下拉选项
  141 +
  142 +// 弹窗相关的变量
  143 +const modelVisible = ref(false) // 是否显示弹出层
  144 +const modelTitle = ref('edit') // 弹出层标题
  145 +const modelLoading = ref(false) // 弹出层loading
  146 +const actionType = ref('') // 操作按钮的类型
  147 +const actionLoading = ref(false) // 按钮 Loading
  148 +const formRef = ref<FormExpose>() // 表单 Ref
  149 +const detailData = ref() // 详情 Ref
  150 +
  151 +// 设置标题
  152 +const setDialogTile = (type: string) => {
  153 + modelLoading.value = true
  154 + modelTitle.value = t('action.' + type)
  155 + actionType.value = type
  156 + modelVisible.value = true
  157 +}
  158 +
  159 +// 新增操作
  160 +const handleCreate = () => {
  161 + setDialogTile('create')
  162 + modelLoading.value = false
  163 +}
  164 +
  165 +// 修改操作
  166 +const handleUpdate = async (rowId: number) => {
  167 + setDialogTile('update')
  168 + // 设置数据
  169 + const res = await MailTemplateApi.getMailTemplateApi(rowId)
  170 + unref(formRef)?.setValues(res)
  171 + modelLoading.value = false
  172 +}
  173 +
  174 +// 详情操作
  175 +const handleDetail = async (rowId: number) => {
  176 + setDialogTile('detail')
  177 + const res = await MailTemplateApi.getMailTemplateApi(rowId)
  178 + detailData.value = res
  179 + modelLoading.value = false
  180 +}
  181 +
  182 +// 提交按钮
  183 +const submitForm = async () => {
  184 + const elForm = unref(formRef)?.getElFormRef()
  185 + if (!elForm) return
  186 + elForm.validate(async (valid) => {
  187 + if (valid) {
  188 + actionLoading.value = true
  189 + // 提交请求
  190 + try {
  191 + const data = unref(formRef)?.formModel as MailTemplateApi.MailTemplateVO
  192 + if (actionType.value === 'create') {
  193 + await MailTemplateApi.createMailTemplateApi(data)
  194 + message.success(t('common.createSuccess'))
  195 + } else {
  196 + await MailTemplateApi.updateMailTemplateApi(data)
  197 + message.success(t('common.updateSuccess'))
  198 + }
  199 + modelVisible.value = false
  200 + } finally {
  201 + actionLoading.value = false
  202 + // 刷新列表
  203 + await reload()
  204 + }
  205 + }
  206 + })
  207 +}
  208 +
  209 +// ========== 测试相关 ==========
  210 +const sendForm = ref({
  211 + content: '',
  212 + params: {},
  213 + mail: '',
  214 + templateCode: '',
  215 + templateParams: {}
  216 +})
  217 +const sendRules = ref({
  218 + mail: [{ required: true, message: '邮箱不能为空', trigger: 'blur' }],
  219 + templateCode: [{ required: true, message: '模版编号不能为空', trigger: 'blur' }],
  220 + templateParams: {}
  221 +})
  222 +const sendVisible = ref(false)
  223 +
  224 +const handleSendMail = (row: any) => {
  225 + sendForm.value.content = row.content
  226 + sendForm.value.params = row.params
  227 + sendForm.value.templateCode = row.code
  228 + sendForm.value.templateParams = row.params.reduce(function (obj, item) {
  229 + obj[item] = undefined
  230 + return obj
  231 + }, {})
  232 + sendRules.value.templateParams = row.params.reduce(function (obj, item) {
  233 + obj[item] = { required: true, message: '参数 ' + item + ' 不能为空', trigger: 'change' }
  234 + return obj
  235 + }, {})
  236 + sendVisible.value = true
  237 +}
  238 +
  239 +const sendTest = async () => {
  240 + const data: MailTemplateApi.MailSmsReqVO = {
  241 + mail: sendForm.value.mail,
  242 + templateCode: sendForm.value.templateCode,
  243 + templateParams: sendForm.value.templateParams as unknown as Map<string, Object>
  244 + }
  245 + const res = await MailTemplateApi.sendMailApi(data)
  246 + if (res) {
  247 + message.success('提交发送成功!发送结果,见发送日志编号:' + res)
  248 + }
  249 + sendVisible.value = false
  250 +}
  251 +
  252 +// ========== 初始化 ==========
  253 +onMounted(() => {
  254 + MailAccountApi.getSimpleMailAccounts().then((data) => {
  255 + accountOptions.value = data
  256 + })
  257 +})
  258 +</script>
yudao-ui-admin-vue3/src/views/system/mail/template/template.data.ts 0 → 100644
  1 +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
  2 +
  3 +// 表单校验
  4 +export const rules = reactive({
  5 + name: [required],
  6 + code: [required],
  7 + accountId: [required],
  8 + title: [required],
  9 + content: [required],
  10 + params: [required],
  11 + status: [required]
  12 +})
  13 +
  14 +// CrudSchema
  15 +const crudSchemas = reactive<VxeCrudSchema>({
  16 + primaryKey: 'id', // 默认的主键ID
  17 + primaryTitle: '编号', // 默认显示的值
  18 + primaryType: null,
  19 + action: true,
  20 + actionWidth: '260',
  21 + columns: [
  22 + {
  23 + title: '模板编码',
  24 + field: 'code',
  25 + isSearch: true
  26 + },
  27 + {
  28 + title: '模板名称',
  29 + field: 'name',
  30 + isSearch: true
  31 + },
  32 + {
  33 + title: '模板标题',
  34 + field: 'title'
  35 + },
  36 + {
  37 + title: '模板内容',
  38 + field: 'content',
  39 + form: {
  40 + component: 'Editor',
  41 + colProps: {
  42 + span: 24
  43 + },
  44 + componentProps: {
  45 + valueHtml: ''
  46 + }
  47 + }
  48 + },
  49 + {
  50 + title: '邮箱账号',
  51 + field: 'accountId',
  52 + isSearch: true,
  53 + table: {
  54 + width: 200,
  55 + slots: {
  56 + default: 'accountId_default'
  57 + }
  58 + }
  59 + },
  60 + {
  61 + title: '发送人名称',
  62 + field: 'nickname'
  63 + },
  64 + {
  65 + title: '开启状态',
  66 + field: 'status',
  67 + isSearch: true,
  68 + dictType: DICT_TYPE.COMMON_STATUS,
  69 + dictClass: 'number'
  70 + },
  71 + {
  72 + title: '备注',
  73 + field: 'remark',
  74 + isTable: false
  75 + },
  76 + {
  77 + title: '创建时间',
  78 + field: 'createTime',
  79 + isForm: false,
  80 + formatter: 'formatDate',
  81 + table: {
  82 + width: 180
  83 + },
  84 + search: {
  85 + show: true,
  86 + itemRender: {
  87 + name: 'XDataTimePicker'
  88 + }
  89 + }
  90 + }
  91 + ]
  92 +})
  93 +export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
yudao-ui-admin-vue3/src/views/system/sms/smsTemplate/index.vue
@@ -197,7 +197,7 @@ const sendSmsForm = ref({ @@ -197,7 +197,7 @@ const sendSmsForm = ref({
197 }) 197 })
198 const sendSmsRules = ref({ 198 const sendSmsRules = ref({
199 mobile: [{ required: true, message: '手机不能为空', trigger: 'blur' }], 199 mobile: [{ required: true, message: '手机不能为空', trigger: 'blur' }],
200 - templateCode: [{ required: true, message: '手机不能为空', trigger: 'blur' }], 200 + templateCode: [{ required: true, message: '模版编号不能为空', trigger: 'blur' }],
201 templateParams: {} 201 templateParams: {}
202 }) 202 })
203 const sendVisible = ref(false) 203 const sendVisible = ref(false)
@@ -225,7 +225,7 @@ const sendSmsTest = async () =&gt; { @@ -225,7 +225,7 @@ const sendSmsTest = async () =&gt; {
225 } 225 }
226 const res = await SmsTemplateApi.sendSmsApi(data) 226 const res = await SmsTemplateApi.sendSmsApi(data)
227 if (res) { 227 if (res) {
228 - message.success('发送成功') 228 + message.success('提交发送成功!发送结果,见发送日志编号:' + res)
229 } 229 }
230 sendVisible.value = false 230 sendVisible.value = false
231 } 231 }