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>
<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>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
@ -8,8 +62,8 @@
</Dialog>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
import * as MailAccountApi from '@/api/system/mail/account'
import { allSchemas, rules } from './account.data'
defineOptions({ name: 'SystemMailAccountForm' })
@ -20,6 +74,28 @@ const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
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
/** 打开弹窗 */
@ -27,12 +103,12 @@ const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
const data = await MailAccountApi.getMailAccount(id)
formRef.value.setValues(data)
formData.value = await MailAccountApi.getMailAccount(id)
} finally {
formLoading.value = false
}
@ -45,12 +121,12 @@ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.getElFormRef().validate()
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
try {
const data = formRef.value.formModel as MailAccountApi.MailAccountVO
const data = formData.value as MailAccountApi.MailAccountVO
if (formType.value === 'create') {
await MailAccountApi.createMailAccount(data)
message.success(t('common.createSuccess'))
@ -65,4 +141,19 @@ const submitForm = async () => {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
mail: '',
username: '',
password: '',
host: '',
port: 465,
sslEnable: true,
starttlsEnable: false
}
formRef.value?.resetFields()
}
</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>
<doc-alert title="邮件配置" url="https://doc.iocoder.cn/mail" />
<!-- 搜索工作栏 -->
<ContentWrap>
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams">
<!-- 新增等操作按钮 -->
<template #actionMore>
<el-form
class="-mb-15px"
: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
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['system:mail-account:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<Icon icon="ep:plus" class="mr-5px" /> 新增</el-button
>
<el-button
type="danger"
plain
:disabled="checkedIds.length === 0"
@click="handleDeleteBatch"
:disabled="!isSelected"
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>
</template>
</Search>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<Table
:columns="allSchemas.tableColumns"
:data="tableObject.tableList"
:loading="tableObject.loading"
:pagination="{
total: tableObject.total
}"
v-model:pageSize="tableObject.pageSize"
v-model:currentPage="tableObject.currentPage"
:selection="true"
>
<template #action="{ row }">
<el-button
link
type="primary"
@click="openForm('update', row.id)"
v-hasPermi="['system:mail-account:update']"
>
编辑
</el-button>
<el-button
link
type="primary"
@click="openDetail(row.id)"
v-hasPermi="['system:mail-account:query']"
>
详情
</el-button>
<el-button
link
type="danger"
v-hasPermi="['system:mail-account:delete']"
@click="handleDelete(row.id)"
>
删除
</el-button>
</template>
</Table>
<el-table v-loading="loading" :data="list" @selection-change="handleRowCheckboxChange">
<el-table-column type="selection" width="55" />
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="邮箱" align="center" prop="mail" />
<el-table-column label="用户名" align="center" prop="username" />
<el-table-column label="SMTP 服务器域名" align="center" prop="host" />
<el-table-column label="SMTP 服务器端口" align="center" prop="port" />
<el-table-column label="是否开启 SSL" align="center" prop="sslEnable">
<template #default="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.sslEnable" />
</template>
</el-table-column>
<el-table-column label="是否开启 STARTTLS" align="center" prop="starttlsEnable">
<template #default="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.starttlsEnable" />
</template>
</el-table-column>
<el-table-column
label="创建时间"
align="center"
prop="createTime"
width="180"
:formatter="dateFormatter"
/>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['system:mail-account:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@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>
<!-- 表单弹窗添加/修改 -->
<MailAccountForm ref="formRef" @success="getList" />
<!-- 详情弹窗 -->
<MailAccountDetail ref="detailRef" />
</template>
<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 download from '@/utils/download'
import MailAccountForm from './MailAccountForm.vue'
import MailAccountDetail from './MailAccountDetail.vue'
defineOptions({ name: 'SystemMailAccount' })
// tableObject
// tableMethods
// https://doc.iocoder.cn/vue3/crud-schema/
const { tableObject, tableMethods } = useTable({
getListApi: MailAccountApi.getMailAccountPage, //
delListApi: MailAccountApi.deleteMailAccount //
const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) //
const total = ref(0) //
const list = ref([]) //
const queryFormRef = ref() //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
mail: '',
username: '',
createTime: []
})
//
const { getList, setSearchParams } = tableMethods
const exportLoading = ref(false) //
/** 查询列表 */
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()
@ -98,28 +185,50 @@ const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 详情操作 */
const detailRef = ref()
const openDetail = (id: number) => {
detailRef.value.open(id)
}
/** 删除按钮操作 */
const handleDelete = (id: number) => {
tableMethods.delList(id, false)
const handleDelete = async (id: number) => {
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 ids = tableObject.selections.map(item => item.id)
if (ids.length === 0) return
await MailAccountApi.deleteMailAccountList(ids)
tableMethods.getList()
try {
//
await message.delConfirm()
//
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
}
}
/** 初始化 **/