reactor:【SYSTEM 系统管理】邮箱账号,重构成 element-plus 标准界面

pull/804/head
YunaiV 2025-07-26 21:58:26 +08:00
parent e380994dc2
commit 4f67951757
4 changed files with 283 additions and 211 deletions

View File

@ -1,28 +0,0 @@
<template>
<Dialog v-model="dialogVisible" title="详情">
<Descriptions :data="detailData" :schema="allSchemas.detailSchema" />
</Dialog>
</template>
<script lang="ts" setup>
import * as MailAccountApi from '@/api/system/mail/account'
import { allSchemas } from './account.data'
defineOptions({ name: 'SystemMailAccountDetail' })
const dialogVisible = ref(false) //
const detailLoading = ref(false) //
const detailData = ref() //
/** 打开弹窗 */
const open = async (id: number) => {
dialogVisible.value = true
//
detailLoading.value = true
try {
detailData.value = await MailAccountApi.getMailAccount(id)
} finally {
detailLoading.value = false
}
}
defineExpose({ open }) // open
</script>

View File

@ -1,6 +1,60 @@
<template> <template>
<Dialog v-model="dialogVisible" :title="dialogTitle"> <Dialog v-model="dialogVisible" :title="dialogTitle">
<Form ref="formRef" v-loading="formLoading" :rules="rules" :schema="allSchemas.formSchema" /> <el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="150px"
>
<el-form-item label="邮箱" prop="mail">
<el-input v-model="formData.mail" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input v-model="formData.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
v-model="formData.password"
placeholder="请输入密码"
type="password"
show-password
/>
</el-form-item>
<el-form-item label="SMTP 服务器域名" prop="host">
<el-input v-model="formData.host" placeholder="请输入 SMTP 服务器域名" />
</el-form-item>
<el-form-item label="SMTP 服务器端口" prop="port">
<el-input-number
v-model="formData.port"
placeholder="请输入 SMTP 服务器端口"
:min="1"
:max="65535"
/>
</el-form-item>
<el-form-item label="是否开启 SSL">
<el-radio-group v-model="formData.sslEnable">
<el-radio
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="是否开启 STARTTLS">
<el-radio-group v-model="formData.starttlsEnable">
<el-radio
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer> <template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button> <el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
@ -8,8 +62,8 @@
</Dialog> </Dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
import * as MailAccountApi from '@/api/system/mail/account' import * as MailAccountApi from '@/api/system/mail/account'
import { allSchemas, rules } from './account.data'
defineOptions({ name: 'SystemMailAccountForm' }) defineOptions({ name: 'SystemMailAccountForm' })
@ -20,6 +74,28 @@ const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // const dialogTitle = ref('') //
const formLoading = ref(false) // 12 const formLoading = ref(false) // 12
const formType = ref('') // create - update - const formType = ref('') // create - update -
const formData = ref({
id: undefined,
mail: '',
username: '',
password: '',
host: '',
port: 465,
sslEnable: true,
starttlsEnable: false
})
const formRules = reactive({
mail: [
{ required: true, message: '邮箱不能为空', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱格式', trigger: ['blur', 'change'] }
],
username: [{ required: true, message: '用户名不能为空', trigger: 'blur' }],
password: [{ required: true, message: '密码不能为空', trigger: 'blur' }],
host: [{ required: true, message: 'SMTP 服务器域名不能为空', trigger: 'blur' }],
port: [{ required: true, message: 'SMTP 服务器端口不能为空', trigger: 'blur' }],
sslEnable: [{ required: true, message: '是否开启 SSL 不能为空', trigger: 'blur' }],
starttlsEnable: [{ required: true, message: '是否开启 STARTTLS 不能为空', trigger: 'blur' }]
})
const formRef = ref() // Ref const formRef = ref() // Ref
/** 打开弹窗 */ /** 打开弹窗 */
@ -27,12 +103,12 @@ const open = async (type: string, id?: number) => {
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = t('action.' + type) dialogTitle.value = t('action.' + type)
formType.value = type formType.value = type
resetForm()
// //
if (id) { if (id) {
formLoading.value = true formLoading.value = true
try { try {
const data = await MailAccountApi.getMailAccount(id) formData.value = await MailAccountApi.getMailAccount(id)
formRef.value.setValues(data)
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
@ -45,12 +121,12 @@ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成
const submitForm = async () => { const submitForm = async () => {
// //
if (!formRef) return if (!formRef) return
const valid = await formRef.value.getElFormRef().validate() const valid = await formRef.value.validate()
if (!valid) return if (!valid) return
// //
formLoading.value = true formLoading.value = true
try { try {
const data = formRef.value.formModel as MailAccountApi.MailAccountVO const data = formData.value as MailAccountApi.MailAccountVO
if (formType.value === 'create') { if (formType.value === 'create') {
await MailAccountApi.createMailAccount(data) await MailAccountApi.createMailAccount(data)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
@ -65,4 +141,19 @@ const submitForm = async () => {
formLoading.value = false formLoading.value = false
} }
} }
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
mail: '',
username: '',
password: '',
host: '',
port: 465,
sslEnable: true,
starttlsEnable: false
}
formRef.value?.resetFields()
}
</script> </script>

View File

@ -1,100 +0,0 @@
import type { CrudSchema } from '@/hooks/web/useCrudSchemas'
import { dateFormatter } from '@/utils/formatTime'
const { t } = useI18n() // 国际化
// 表单校验
export const rules = reactive({
mail: [
{ required: true, message: t('profile.rules.mail'), trigger: 'blur' },
{
type: 'email',
message: t('profile.rules.truemail'),
trigger: ['blur', 'change']
}
],
username: [required],
password: [required],
host: [required],
port: [required],
sslEnable: [required],
starttlsEnable: [required]
})
// CrudSchemahttps://doc.iocoder.cn/vue3/crud-schema/
const crudSchemas = reactive<CrudSchema[]>([
{
label: '邮箱',
field: 'mail',
isSearch: true,
search: {
componentProps: {
style: {
width: '240px'
}
}
}
},
{
label: '用户名',
field: 'username',
isSearch: true,
search: {
componentProps: {
style: {
width: '240px'
}
}
}
},
{
label: '密码',
field: 'password',
isTable: false
},
{
label: 'SMTP 服务器域名',
field: 'host'
},
{
label: 'SMTP 服务器端口',
field: 'port',
form: {
component: 'InputNumber',
value: 465
}
},
{
label: '是否开启 SSL',
field: 'sslEnable',
dictType: DICT_TYPE.INFRA_BOOLEAN_STRING,
dictClass: 'boolean',
form: {
component: 'Radio'
}
},
{
label: '是否开启 STARTTLS',
field: 'starttlsEnable',
dictType: DICT_TYPE.INFRA_BOOLEAN_STRING,
dictClass: 'boolean',
form: {
component: 'Radio'
}
},
{
label: '创建时间',
field: 'createTime',
isForm: false,
formatter: dateFormatter,
detail: {
dateFormat: 'YYYY-MM-DD HH:mm:ss'
}
},
{
label: '操作',
field: 'action',
isForm: false,
isDetail: false
}
])
export const { allSchemas } = useCrudSchemas(crudSchemas)

View File

@ -1,96 +1,183 @@
<template> <template>
<doc-alert title="邮件配置" url="https://doc.iocoder.cn/mail" /> <doc-alert title="邮件配置" url="https://doc.iocoder.cn/mail" />
<!-- 搜索工作栏 -->
<ContentWrap> <ContentWrap>
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams"> <el-form
<!-- 新增等操作按钮 --> class="-mb-15px"
<template #actionMore> :model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="邮箱" prop="mail">
<el-input
v-model="queryParams.mail"
placeholder="请输入邮箱"
clearable
class="!w-240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input
v-model="queryParams.username"
placeholder="请输入用户名"
clearable
class="!w-240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button <el-button
type="primary" type="primary"
plain plain
@click="openForm('create')" @click="openForm('create')"
v-hasPermi="['system:mail-account:create']" v-hasPermi="['system:mail-account:create']"
> >
<Icon icon="ep:plus" class="mr-5px" /> 新增 <Icon icon="ep:plus" class="mr-5px" /> 新增</el-button
</el-button> >
<el-button <el-button
type="danger" type="danger"
plain plain
:disabled="checkedIds.length === 0"
@click="handleDeleteBatch" @click="handleDeleteBatch"
:disabled="!isSelected"
v-hasPermi="['system:mail-account:delete']" v-hasPermi="['system:mail-account:delete']"
> >
<Icon icon="ep:delete" class="mr-5px" /> 批量删除 <Icon icon="ep:delete" class="mr-5px" /> 批量删除</el-button
>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['system:mail-account:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button> </el-button>
</template> </el-form-item>
</Search> </el-form>
</ContentWrap> </ContentWrap>
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<Table <el-table v-loading="loading" :data="list" @selection-change="handleRowCheckboxChange">
:columns="allSchemas.tableColumns" <el-table-column type="selection" width="55" />
:data="tableObject.tableList" <el-table-column label="编号" align="center" prop="id" />
:loading="tableObject.loading" <el-table-column label="邮箱" align="center" prop="mail" />
:pagination="{ <el-table-column label="用户名" align="center" prop="username" />
total: tableObject.total <el-table-column label="SMTP 服务器域名" align="center" prop="host" />
}" <el-table-column label="SMTP 服务器端口" align="center" prop="port" />
v-model:pageSize="tableObject.pageSize" <el-table-column label="是否开启 SSL" align="center" prop="sslEnable">
v-model:currentPage="tableObject.currentPage" <template #default="scope">
:selection="true" <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.sslEnable" />
> </template>
<template #action="{ row }"> </el-table-column>
<el-button <el-table-column label="是否开启 STARTTLS" align="center" prop="starttlsEnable">
link <template #default="scope">
type="primary" <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.starttlsEnable" />
@click="openForm('update', row.id)" </template>
v-hasPermi="['system:mail-account:update']" </el-table-column>
> <el-table-column
编辑 label="创建时间"
</el-button> align="center"
<el-button prop="createTime"
link width="180"
type="primary" :formatter="dateFormatter"
@click="openDetail(row.id)" />
v-hasPermi="['system:mail-account:query']" <el-table-column label="操作" align="center">
> <template #default="scope">
详情 <el-button
</el-button> link
<el-button type="primary"
link @click="openForm('update', scope.row.id)"
type="danger" v-hasPermi="['system:mail-account:update']"
v-hasPermi="['system:mail-account:delete']" >
@click="handleDelete(row.id)" 编辑
> </el-button>
删除 <el-button
</el-button> link
</template> type="danger"
</Table> @click="handleDelete(scope.row.id)"
v-hasPermi="['system:mail-account:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap> </ContentWrap>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<MailAccountForm ref="formRef" @success="getList" /> <MailAccountForm ref="formRef" @success="getList" />
<!-- 详情弹窗 -->
<MailAccountDetail ref="detailRef" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { allSchemas } from './account.data' import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import * as MailAccountApi from '@/api/system/mail/account' import * as MailAccountApi from '@/api/system/mail/account'
import download from '@/utils/download'
import MailAccountForm from './MailAccountForm.vue' import MailAccountForm from './MailAccountForm.vue'
import MailAccountDetail from './MailAccountDetail.vue'
defineOptions({ name: 'SystemMailAccount' }) defineOptions({ name: 'SystemMailAccount' })
// tableObject const { t } = useI18n() //
// tableMethods const message = useMessage() //
// https://doc.iocoder.cn/vue3/crud-schema/
const { tableObject, tableMethods } = useTable({ const loading = ref(false) //
getListApi: MailAccountApi.getMailAccountPage, // const total = ref(0) //
delListApi: MailAccountApi.deleteMailAccount // const list = ref([]) //
const queryFormRef = ref() //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
mail: '',
username: '',
createTime: []
}) })
// const exportLoading = ref(false) //
const { getList, setSearchParams } = tableMethods
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await MailAccountApi.getMailAccountPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */ /** 添加/修改操作 */
const formRef = ref() const formRef = ref()
@ -98,28 +185,50 @@ const openForm = (type: string, id?: number) => {
formRef.value.open(type, id) formRef.value.open(type, id)
} }
/** 详情操作 */
const detailRef = ref()
const openDetail = (id: number) => {
detailRef.value.open(id)
}
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = (id: number) => { const handleDelete = async (id: number) => {
tableMethods.delList(id, false) try {
//
await message.delConfirm()
//
await MailAccountApi.deleteMailAccount(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
} }
/** 是否有选中行 */
const isSelected = computed(() => {
return tableObject.selections && tableObject.selections.length > 0
})
/** 批量删除按钮操作 */ /** 批量删除按钮操作 */
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (rows: MailAccountApi.MailAccountVO[]) => {
checkedIds.value = rows.map((row) => row.id)
}
const handleDeleteBatch = async () => { const handleDeleteBatch = async () => {
const ids = tableObject.selections.map(item => item.id) try {
if (ids.length === 0) return //
await MailAccountApi.deleteMailAccountList(ids) await message.delConfirm()
tableMethods.getList() //
await MailAccountApi.deleteMailAccountList(checkedIds.value)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await MailAccountApi.exportMailAccount(queryParams)
download.excel(data, '邮箱账号.xls')
} catch {
} finally {
exportLoading.value = false
}
} }
/** 初始化 **/ /** 初始化 **/