Merge remote-tracking branch 'origin/master' into feature/springdoc

This commit is contained in:
xingyu
2023-01-29 16:11:51 +08:00
182 changed files with 6621 additions and 810 deletions

View File

@@ -1,6 +1,8 @@
# 本地开发环境
NODE_ENV=development
VITE_DEV=true
# 请求路径
VITE_BASE_URL='http://localhost:48080'

View File

@@ -1,6 +1,8 @@
# 开发环境
NODE_ENV=production
VITE_DEV=false
# 请求路径
VITE_BASE_URL='http://localhost:58080'

View File

@@ -1,6 +1,8 @@
# 生产环境
NODE_ENV=production
VITE_DEV=false
# 请求路径
VITE_BASE_URL='http://localhost:58080'

View File

@@ -1,6 +1,8 @@
# 测试环境
NODE_ENV=production
VITE_DEV=false
# 请求路径
VITE_BASE_URL='http://localhost:58080'

View File

@@ -0,0 +1,46 @@
import request from '@/config/axios'
export interface MailAccountVO {
id: number
mail: string
username: string
password: string
host: string
port: number
sslEnable: boolean
}
export interface MailAccountPageReqVO extends PageParam {
mail?: string
username?: string
}
// 查询邮箱账号列表
export const getMailAccountPageApi = async (params: MailAccountPageReqVO) => {
return await request.get({ url: '/system/mail-account/page', params })
}
// 查询邮箱账号详情
export const getMailAccountApi = async (id: number) => {
return await request.get({ url: '/system/mail-account/get?id=' + id })
}
// 新增邮箱账号
export const createMailAccountApi = async (data: MailAccountVO) => {
return await request.post({ url: '/system/mail-account/create', data })
}
// 修改邮箱账号
export const updateMailAccountApi = async (data: MailAccountVO) => {
return await request.put({ url: '/system/mail-account/update', data })
}
// 删除邮箱账号
export const deleteMailAccountApi = async (id: number) => {
return await request.delete({ url: '/system/mail-account/delete?id=' + id })
}
// 获得邮箱账号精简列表
export const getSimpleMailAccounts = async () => {
return request.get({ url: '/system/mail-account/list-all-simple' })
}

View File

@@ -0,0 +1,40 @@
import request from '@/config/axios'
export interface MailLogVO {
id: number
userId: number
userType: number
toMail: string
accountId: number
fromMail: string
templateId: number
templateCode: string
templateNickname: string
templateTitle: string
templateContent: string
templateParams: string
sendStatus: number
sendTime: Date
sendMessageId: string
sendException: string
}
export interface MailLogPageReqVO extends PageParam {
userId?: number
userType?: number
toMail?: string
accountId?: number
templateId?: number
sendStatus?: number
sendTime?: Date[]
}
// 查询邮件日志列表
export const getMailLogPageApi = async (params: MailLogPageReqVO) => {
return await request.get({ url: '/system/mail-log/page', params })
}
// 查询邮件日志详情
export const getMailLogApi = async (id: number) => {
return await request.get({ url: '/system/mail-log/get?id=' + id })
}

View File

@@ -0,0 +1,58 @@
import request from '@/config/axios'
export interface MailTemplateVO {
id: number
name: string
code: string
accountId: number
nickname: string
title: string
content: string
params: string
status: number
remark: string
}
export interface MailTemplatePageReqVO extends PageParam {
name?: string
code?: string
accountId?: number
status?: number
createTime?: Date[]
}
export interface MailSmsReqVO {
mail: string
templateCode: string
templateParams: Map<String, Object>
}
// 查询邮件模版列表
export const getMailTemplatePageApi = async (params: MailTemplatePageReqVO) => {
return await request.get({ url: '/system/mail-template/page', params })
}
// 查询邮件模版详情
export const getMailTemplateApi = async (id: number) => {
return await request.get({ url: '/system/mail-template/get?id=' + id })
}
// 新增邮件模版
export const createMailTemplateApi = async (data: MailTemplateVO) => {
return await request.post({ url: '/system/mail-template/create', data })
}
// 修改邮件模版
export const updateMailTemplateApi = async (data: MailTemplateVO) => {
return await request.put({ url: '/system/mail-template/update', data })
}
// 删除邮件模版
export const deleteMailTemplateApi = async (id: number) => {
return await request.delete({ url: '/system/mail-template/delete?id=' + id })
}
// 发送邮件
export const sendMailApi = (data: MailSmsReqVO) => {
return request.post({ url: '/system/mail-template/send-mail', data })
}

View File

@@ -255,16 +255,17 @@ const getToolBarConfig = (options: XTableProps) => {
if (toolbarConfig) return
if (toolBar) {
if (!isBoolean(toolBar)) {
console.info(2)
options.toolbarConfig = toolBar
return
}
} else if (!topActionSlots) {
} else if (topActionSlots != false) {
options.toolbarConfig = {
enabled: true
slots: { buttons: 'toolbar_buttons' }
}
} else {
options.toolbarConfig = {
slots: { buttons: 'toolbar_buttons' }
enabled: true
}
}
}

View File

@@ -37,6 +37,13 @@ import App from './App.vue'
import './permission'
import { isDevMode } from '@/utils/env'
if (isDevMode()) {
console.info(isDevMode())
import('element-plus/dist/index.css')
}
// 创建实例
const setupAll = async () => {
const app = createApp(App)

View File

@@ -21,6 +21,7 @@ declare module '@vue/runtime-core' {
DictTag: typeof import('./../components/DictTag/src/DictTag.vue')['default']
Echart: typeof import('./../components/Echart/src/Echart.vue')['default']
Editor: typeof import('./../components/Editor/src/Editor.vue')['default']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBadge: typeof import('element-plus/es')['ElBadge']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
@@ -30,6 +31,7 @@ declare module '@vue/runtime-core' {
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
@@ -38,6 +40,7 @@ declare module '@vue/runtime-core' {
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
@@ -48,10 +51,14 @@ declare module '@vue/runtime-core' {
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElScroll: typeof import('element-plus/es')['ElScroll']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
ElSpace: typeof import('element-plus/es')['ElSpace']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']

View File

@@ -90,6 +90,7 @@ export enum DICT_TYPE {
SYSTEM_SMS_RECEIVE_STATUS = 'system_sms_receive_status',
SYSTEM_ERROR_CODE_TYPE = 'system_error_code_type',
SYSTEM_OAUTH2_GRANT_TYPE = 'system_oauth2_grant_type',
SYSTEM_MAIL_SEND_STATUS = 'system_mail_send_status',
// ========== INFRA 模块 ==========
INFRA_BOOLEAN_STRING = 'infra_boolean_string',

View File

@@ -0,0 +1,8 @@
export const isDevMode = () => {
const dev = import.meta.env.VITE_DEV
if (dev && dev === true) {
return true
} else {
return false
}
}

View File

@@ -5,5 +5,6 @@
</template>
<script setup lang="ts" name="Swagger">
const BASE_URL = import.meta.env.VITE_BASE_URL
const src = ref(BASE_URL + '/doc.html')
// const src = ref(BASE_URL + '/doc.html')
const src = ref(BASE_URL + '/swagger-ui')
</script>

View File

@@ -0,0 +1,65 @@
import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
// 表单校验
export const rules = reactive({
mail: [required],
username: [required],
password: [required],
host: [required],
port: [required],
sslEnable: [required]
})
// CrudSchema
const crudSchemas = reactive<VxeCrudSchema>({
primaryKey: 'id', // 默认的主键 ID
primaryTitle: '编号',
primaryType: 'id',
action: true,
actionWidth: '200', // 3 个按钮默认 200如有删减对应增减即可
columns: [
{
title: '邮箱',
field: 'mail',
isSearch: true
},
{
title: '用户名',
field: 'username',
isSearch: true
},
{
title: '密码',
field: 'password',
isTable: false
},
{
title: 'SMTP 服务器域名',
field: 'host'
},
{
title: 'SMTP 服务器端口',
field: 'port',
form: {
component: 'InputNumber',
value: 465
}
},
{
title: '是否开启 SSL',
field: 'sslEnable',
dictType: DICT_TYPE.INFRA_BOOLEAN_STRING,
dictClass: 'boolean'
},
{
title: '创建时间',
field: 'createTime',
isForm: false,
formatter: 'formatDate',
table: {
width: 180
}
}
]
})
export const { allSchemas } = useVxeCrudSchemas(crudSchemas)

View File

@@ -0,0 +1,151 @@
<template>
<ContentWrap>
<!-- 列表 -->
<XTable @register="registerTable">
<template #toolbar_buttons>
<!-- 操作新增 -->
<XButton
type="primary"
preIcon="ep:zoom-in"
:title="t('action.add')"
v-hasPermi="['system:mail-account:create']"
@click="handleCreate()"
/>
</template>
<template #actionbtns_default="{ row }">
<!-- 操作修改 -->
<XTextButton
preIcon="ep:edit"
:title="t('action.edit')"
v-hasPermi="['system:mail-account:update']"
@click="handleUpdate(row.id)"
/>
<!-- 操作详情 -->
<XTextButton
preIcon="ep:view"
:title="t('action.detail')"
v-hasPermi="['system:mail-account:query']"
@click="handleDetail(row.id)"
/>
<!-- 操作删除 -->
<XTextButton
preIcon="ep:delete"
:title="t('action.del')"
v-hasPermi="['system:mail-account:delete']"
@click="deleteData(row.id)"
/>
</template>
</XTable>
</ContentWrap>
<!-- 弹窗 -->
<XModal id="mailAccountModel" :loading="modelLoading" v-model="modelVisible" :title="modelTitle">
<!-- 表单添加/修改 -->
<Form
ref="formRef"
v-if="['create', 'update'].includes(actionType)"
:schema="allSchemas.formSchema"
:rules="rules"
/>
<!-- 表单详情 -->
<Descriptions
v-if="actionType === 'detail'"
:schema="allSchemas.detailSchema"
:data="detailData"
/>
<template #footer>
<!-- 按钮保存 -->
<XButton
v-if="['create', 'update'].includes(actionType)"
type="primary"
:title="t('action.save')"
:loading="actionLoading"
@click="submitForm()"
/>
<!-- 按钮关闭 -->
<XButton :loading="actionLoading" :title="t('dialog.close')" @click="modelVisible = false" />
</template>
</XModal>
</template>
<script setup lang="ts" name="MailAccount">
import { FormExpose } from '@/components/Form'
// 业务相关的 import
import { rules, allSchemas } from './account.data'
import * as MailAccountApi from '@/api/system/mail/account'
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
// 列表相关的变量
const [registerTable, { reload, deleteData }] = useXTable({
allSchemas: allSchemas,
getListApi: MailAccountApi.getMailAccountPageApi,
deleteApi: MailAccountApi.deleteMailAccountApi
})
// 弹窗相关的变量
const modelVisible = ref(false) // 是否显示弹出层
const modelTitle = ref('edit') // 弹出层标题
const modelLoading = ref(false) // 弹出层loading
const actionType = ref('') // 操作按钮的类型
const actionLoading = ref(false) // 按钮 Loading
const formRef = ref<FormExpose>() // 表单 Ref
const detailData = ref() // 详情 Ref
// 设置标题
const setDialogTile = (type: string) => {
modelLoading.value = true
modelTitle.value = t('action.' + type)
actionType.value = type
modelVisible.value = true
}
// 新增操作
const handleCreate = () => {
setDialogTile('create')
modelLoading.value = false
}
// 修改操作
const handleUpdate = async (rowId: number) => {
setDialogTile('update')
// 设置数据
const res = await MailAccountApi.getMailAccountApi(rowId)
unref(formRef)?.setValues(res)
modelLoading.value = false
}
// 详情操作
const handleDetail = async (rowId: number) => {
setDialogTile('detail')
const res = await MailAccountApi.getMailAccountApi(rowId)
detailData.value = res
modelLoading.value = false
}
// 提交按钮
const submitForm = async () => {
const elForm = unref(formRef)?.getElFormRef()
if (!elForm) return
elForm.validate(async (valid) => {
if (valid) {
actionLoading.value = true
// 提交请求
try {
const data = unref(formRef)?.formModel as MailAccountApi.MailAccountVO
if (actionType.value === 'create') {
await MailAccountApi.createMailAccountApi(data)
message.success(t('common.createSuccess'))
} else {
await MailAccountApi.updateMailAccountApi(data)
message.success(t('common.updateSuccess'))
}
modelVisible.value = false
} finally {
actionLoading.value = false
// 刷新列表
await reload()
}
}
})
}
</script>

View File

@@ -0,0 +1,96 @@
<template>
<ContentWrap>
<!-- 列表 -->
<XTable @register="registerTable">
<template #accountId_search>
<el-select v-model="queryParams.accountId">
<el-option :key="undefined" label="全部" :value="undefined" />
<el-option
v-for="item in accountOptions"
:key="item.id"
:label="item.mail"
:value="item.id"
/>
</el-select>
</template>
<template #toMail_default="{ row }">
<div>{{ row.toMail }}</div>
<div v-if="row.userType && row.userId">
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="row.userType" />{{ '(' + row.userId + ')' }}
</div>
</template>
<template #actionbtns_default="{ row }">
<!-- 操作详情 -->
<XTextButton
preIcon="ep:view"
:title="t('action.detail')"
v-hasPermi="['system:mail-log:query']"
@click="handleDetail(row.id)"
/>
</template>
</XTable>
</ContentWrap>
<!-- 弹窗 -->
<XModal id="mailLogModel" :loading="modelLoading" v-model="modelVisible" :title="modelTitle">
<!-- 表单详情 -->
<Descriptions
v-if="actionType === 'detail'"
:schema="allSchemas.detailSchema"
:data="detailData"
/>
<template #footer>
<!-- 按钮关闭 -->
<XButton :loading="actionLoading" :title="t('dialog.close')" @click="modelVisible = false" />
</template>
</XModal>
</template>
<script setup lang="ts" name="MailLog">
// 业务相关的 import
import { allSchemas } from './log.data'
import * as MailLogApi from '@/api/system/mail/log'
import * as MailAccountApi from '@/api/system/mail/account'
const { t } = useI18n() // 国际化
// 列表相关的变量
const queryParams = reactive({
accountId: null
})
const [registerTable] = useXTable({
allSchemas: allSchemas,
params: queryParams,
getListApi: MailLogApi.getMailLogPageApi
})
const accountOptions = ref([]) // 账号下拉选项
// 弹窗相关的变量
const modelVisible = ref(false) // 是否显示弹出层
const modelTitle = ref('edit') // 弹出层标题
const modelLoading = ref(false) // 弹出层loading
const actionType = ref('') // 操作按钮的类型
const actionLoading = ref(false) // 按钮 Loading
const detailData = ref() // 详情 Ref
// 设置标题
const setDialogTile = (type: string) => {
modelLoading.value = true
modelTitle.value = t('action.' + type)
actionType.value = type
modelVisible.value = true
}
// 详情操作
const handleDetail = async (rowId: number) => {
setDialogTile('detail')
const res = await MailLogApi.getMailLogApi(rowId)
detailData.value = res
modelLoading.value = false
}
// ========== 初始化 ==========
onMounted(() => {
MailAccountApi.getSimpleMailAccounts().then((data) => {
accountOptions.value = data
})
})
</script>

View File

@@ -0,0 +1,121 @@
import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
// CrudSchema
const crudSchemas = reactive<VxeCrudSchema>({
primaryKey: 'id',
primaryTitle: '编号',
primaryType: 'id',
action: true,
actionWidth: '70',
columns: [
{
title: '发送时间',
field: 'sendTime',
table: {
width: 180
},
formatter: 'formatDate',
search: {
show: true,
itemRender: {
name: 'XDataTimePicker'
}
}
},
{
title: '接收邮箱',
field: 'toMail',
isSearch: true,
table: {
width: 180,
slots: {
default: 'toMail_default'
}
}
},
{
title: '用户编号',
field: 'userId',
isSearch: true,
isTable: false
},
{
title: '用户类型',
field: 'userType',
dictType: DICT_TYPE.USER_TYPE,
dictClass: 'number',
isSearch: true,
isTable: false
},
{
title: '邮件标题',
field: 'templateTitle'
},
{
title: '邮件内容',
field: 'templateContent',
isTable: false
},
{
title: '邮箱参数',
field: 'templateParams',
isTable: false
},
{
title: '发送状态',
field: 'sendStatus',
dictType: DICT_TYPE.SYSTEM_MAIL_SEND_STATUS,
dictClass: 'string',
isSearch: true
},
{
title: '邮箱账号',
field: 'accountId',
isSearch: true,
isTable: false,
search: {
slots: {
default: 'accountId_search'
}
}
},
{
title: '发送邮箱地址',
field: 'fromMail',
table: {
title: '邮箱账号'
}
},
{
title: '模板编号',
field: 'templateId',
isSearch: true
},
{
title: '模板编码',
field: 'templateCode',
isTable: false
},
{
title: '模版发送人名称',
field: 'templateNickname',
isTable: false
},
{
title: '发送返回的消息编号',
field: 'sendMessageId',
isTable: false
},
{
title: '发送异常',
field: 'sendException',
isTable: false
},
{
title: '创建时间',
field: 'createTime',
isTable: false
}
]
})
export const { allSchemas } = useVxeCrudSchemas(crudSchemas)

View File

@@ -0,0 +1,273 @@
<template>
<ContentWrap>
<!-- 列表 -->
<XTable @register="registerTable">
<template #accountId_search>
<el-select v-model="queryParams.accountId">
<el-option :key="undefined" label="全部" :value="undefined" />
<el-option
v-for="item in accountOptions"
:key="item.id"
:label="item.mail"
:value="item.id"
/>
</el-select>
</template>
<template #toolbar_buttons>
<!-- 操作新增 -->
<XButton
type="primary"
preIcon="ep:zoom-in"
:title="t('action.add')"
v-hasPermi="['system:mail-template:create']"
@click="handleCreate()"
/>
</template>
<template #accountId_default="{ row }">
<span>{{ accountOptions.find((account) => account.id === row.accountId)?.mail }}</span>
</template>
<template #actionbtns_default="{ row }">
<!-- 操作测试短信 -->
<XTextButton
preIcon="ep:cpu"
:title="t('action.test')"
v-hasPermi="['system:mail-template:send-mail']"
@click="handleSendMail(row)"
/>
<!-- 操作修改 -->
<XTextButton
preIcon="ep:edit"
:title="t('action.edit')"
v-hasPermi="['system:mail-template:update']"
@click="handleUpdate(row.id)"
/>
<!-- 操作详情 -->
<XTextButton
preIcon="ep:view"
:title="t('action.detail')"
v-hasPermi="['system:mail-template:query']"
@click="handleDetail(row.id)"
/>
<!-- 操作删除 -->
<XTextButton
preIcon="ep:delete"
:title="t('action.del')"
v-hasPermi="['system:mail-template:delete']"
@click="deleteData(row.id)"
/>
</template>
</XTable>
</ContentWrap>
<!-- 添加/修改/详情的弹窗 -->
<XModal id="mailTemplateModel" :loading="modelLoading" v-model="modelVisible" :title="modelTitle">
<!-- 表单添加/修改 -->
<Form
ref="formRef"
v-if="['create', 'update'].includes(actionType)"
:schema="allSchemas.formSchema"
:rules="rules"
>
<template #accountId="form">
<el-select v-model="form.accountId">
<el-option
v-for="item in accountOptions"
:key="item.id"
:label="item.mail"
:value="item.id"
/>
</el-select>
</template>
</Form>
<!-- 表单详情 -->
<Descriptions
v-if="actionType === 'detail'"
:schema="allSchemas.detailSchema"
:data="detailData"
/>
<template #footer>
<!-- 按钮保存 -->
<XButton
v-if="['create', 'update'].includes(actionType)"
type="primary"
:title="t('action.save')"
:loading="actionLoading"
@click="submitForm()"
/>
<!-- 按钮关闭 -->
<XButton :loading="actionLoading" :title="t('dialog.close')" @click="modelVisible = false" />
</template>
</XModal>
<!-- 测试邮件的弹窗 -->
<XModal id="sendTest" v-model="sendVisible" title="测试">
<el-form :model="sendForm" :rules="sendRules" label-width="200px" label-position="top">
<el-form-item label="模板内容" prop="content">
<Editor :model-value="sendForm.content" readonly height="150px" />
</el-form-item>
<el-form-item label="收件邮箱" prop="mail">
<el-input v-model="sendForm.mail" placeholder="请输入收件邮箱" />
</el-form-item>
<el-form-item
v-for="param in sendForm.params"
:key="param"
:label="'参数 {' + param + '}'"
:prop="'templateParams.' + param"
>
<el-input
v-model="sendForm.templateParams[param]"
:placeholder="'请输入 ' + param + ' 参数'"
/>
</el-form-item>
</el-form>
<!-- 操作按钮 -->
<template #footer>
<XButton
type="primary"
:title="t('action.test')"
:loading="actionLoading"
@click="sendTest()"
/>
<XButton :title="t('dialog.close')" @click="sendVisible = false" />
</template>
</XModal>
</template>
<script setup lang="ts" name="MailTemplate">
import { FormExpose } from '@/components/Form'
// 业务相关的 import
import { rules, allSchemas } from './template.data'
import * as MailTemplateApi from '@/api/system/mail/template'
import * as MailAccountApi from '@/api/system/mail/account'
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
// 列表相关的变量
const queryParams = reactive({
accountId: null
})
const [registerTable, { reload, deleteData }] = useXTable({
allSchemas: allSchemas,
params: queryParams,
getListApi: MailTemplateApi.getMailTemplatePageApi,
deleteApi: MailTemplateApi.deleteMailTemplateApi
})
const accountOptions = ref([]) // 账号下拉选项
// 弹窗相关的变量
const modelVisible = ref(false) // 是否显示弹出层
const modelTitle = ref('edit') // 弹出层标题
const modelLoading = ref(false) // 弹出层loading
const actionType = ref('') // 操作按钮的类型
const actionLoading = ref(false) // 按钮 Loading
const formRef = ref<FormExpose>() // 表单 Ref
const detailData = ref() // 详情 Ref
// 设置标题
const setDialogTile = (type: string) => {
modelLoading.value = true
modelTitle.value = t('action.' + type)
actionType.value = type
modelVisible.value = true
}
// 新增操作
const handleCreate = () => {
setDialogTile('create')
modelLoading.value = false
}
// 修改操作
const handleUpdate = async (rowId: number) => {
setDialogTile('update')
// 设置数据
const res = await MailTemplateApi.getMailTemplateApi(rowId)
unref(formRef)?.setValues(res)
modelLoading.value = false
}
// 详情操作
const handleDetail = async (rowId: number) => {
setDialogTile('detail')
const res = await MailTemplateApi.getMailTemplateApi(rowId)
detailData.value = res
modelLoading.value = false
}
// 提交按钮
const submitForm = async () => {
const elForm = unref(formRef)?.getElFormRef()
if (!elForm) return
elForm.validate(async (valid) => {
if (valid) {
actionLoading.value = true
// 提交请求
try {
const data = unref(formRef)?.formModel as MailTemplateApi.MailTemplateVO
if (actionType.value === 'create') {
await MailTemplateApi.createMailTemplateApi(data)
message.success(t('common.createSuccess'))
} else {
await MailTemplateApi.updateMailTemplateApi(data)
message.success(t('common.updateSuccess'))
}
modelVisible.value = false
} finally {
actionLoading.value = false
// 刷新列表
await reload()
}
}
})
}
// ========== 测试相关 ==========
const sendForm = ref({
content: '',
params: {},
mail: '',
templateCode: '',
templateParams: {}
})
const sendRules = ref({
mail: [{ required: true, message: '邮箱不能为空', trigger: 'blur' }],
templateCode: [{ required: true, message: '模版编号不能为空', trigger: 'blur' }],
templateParams: {}
})
const sendVisible = ref(false)
const handleSendMail = (row: any) => {
sendForm.value.content = row.content
sendForm.value.params = row.params
sendForm.value.templateCode = row.code
sendForm.value.templateParams = row.params.reduce(function (obj, item) {
obj[item] = undefined
return obj
}, {})
sendRules.value.templateParams = row.params.reduce(function (obj, item) {
obj[item] = { required: true, message: '参数 ' + item + ' 不能为空', trigger: 'change' }
return obj
}, {})
sendVisible.value = true
}
const sendTest = async () => {
const data: MailTemplateApi.MailSmsReqVO = {
mail: sendForm.value.mail,
templateCode: sendForm.value.templateCode,
templateParams: sendForm.value.templateParams as unknown as Map<string, Object>
}
const res = await MailTemplateApi.sendMailApi(data)
if (res) {
message.success('提交发送成功!发送结果,见发送日志编号:' + res)
}
sendVisible.value = false
}
// ========== 初始化 ==========
onMounted(() => {
MailAccountApi.getSimpleMailAccounts().then((data) => {
accountOptions.value = data
})
})
</script>

View File

@@ -0,0 +1,98 @@
import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
// 表单校验
export const rules = reactive({
name: [required],
code: [required],
accountId: [required],
title: [required],
content: [required],
params: [required],
status: [required]
})
// CrudSchema
const crudSchemas = reactive<VxeCrudSchema>({
primaryKey: 'id', // 默认的主键ID
primaryTitle: '编号', // 默认显示的值
primaryType: null,
action: true,
actionWidth: '260',
columns: [
{
title: '模板编码',
field: 'code',
isSearch: true
},
{
title: '模板名称',
field: 'name',
isSearch: true
},
{
title: '模板标题',
field: 'title'
},
{
title: '模板内容',
field: 'content',
form: {
component: 'Editor',
colProps: {
span: 24
},
componentProps: {
valueHtml: ''
}
}
},
{
title: '邮箱账号',
field: 'accountId',
isSearch: true,
table: {
width: 200,
slots: {
default: 'accountId_default'
}
},
search: {
slots: {
default: 'accountId_search'
}
}
},
{
title: '发送人名称',
field: 'nickname'
},
{
title: '开启状态',
field: 'status',
isSearch: true,
dictType: DICT_TYPE.COMMON_STATUS,
dictClass: 'number'
},
{
title: '备注',
field: 'remark',
isTable: false
},
{
title: '创建时间',
field: 'createTime',
isForm: false,
formatter: 'formatDate',
table: {
width: 180
},
search: {
show: true,
itemRender: {
name: 'XDataTimePicker'
}
}
}
]
})
export const { allSchemas } = useVxeCrudSchemas(crudSchemas)

View File

@@ -197,7 +197,7 @@ const sendSmsForm = ref({
})
const sendSmsRules = ref({
mobile: [{ required: true, message: '手机不能为空', trigger: 'blur' }],
templateCode: [{ required: true, message: '手机不能为空', trigger: 'blur' }],
templateCode: [{ required: true, message: '模版编号不能为空', trigger: 'blur' }],
templateParams: {}
})
const sendVisible = ref(false)
@@ -225,7 +225,7 @@ const sendSmsTest = async () => {
}
const res = await SmsTemplateApi.sendSmsApi(data)
if (res) {
message.success('发送成功')
message.success('提交发送成功!发送结果,见发送日志编号:' + res)
}
sendVisible.value = false
}

View File

@@ -8,17 +8,19 @@
</div>
</template>
<el-input v-model="filterText" placeholder="搜索部门" />
<el-tree
ref="treeRef"
node-key="id"
default-expand-all
:data="deptOptions"
:props="defaultProps"
:highlight-current="true"
:filter-node-method="filterNode"
:expand-on-click-node="false"
@node-click="handleDeptNodeClick"
/>
<el-scrollbar height="650">
<el-tree
ref="treeRef"
node-key="id"
default-expand-all
:data="deptOptions"
:props="defaultProps"
:highlight-current="true"
:filter-node-method="filterNode"
:expand-on-click-node="false"
@node-click="handleDeptNodeClick"
/>
</el-scrollbar>
</el-card>
<el-card class="w-4/5 user" style="margin-left: 10px" :gutter="12" shadow="hover">
<template #header>
@@ -534,8 +536,8 @@ onMounted(async () => {
<style scoped>
.user {
height: 900px;
max-height: 960px;
height: 780px;
max-height: 800px;
}
.card-header {
display: flex;

View File

@@ -11,6 +11,7 @@ interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
readonly VITE_PORT: number
readonly VITE_OPEN: string
readonly VITE_DEV: boolean
readonly VITE_ROUTE_ALWAYSSHOW_ENABLE: string
readonly VITE_APP_CAPTCHA_ENABLE: string
readonly VITE_APP_TENANT_ENABLE: string