sys/setting.vue是系统设置页面组件,提供系统参数配置、基础设置、安全配置等功能,支持多种配置类型和实时保存。
文件位置: src/views/sys/setting.vue
- 基础信息设置
- 系统参数配置
- 安全策略设置
- 邮件配置
- 短信配置
- 文件上传设置
- 缓存配置
- 日志配置
<template>
<div class="setting-container">
<!-- 页面头部 -->
<div class="page-header">
<div class="header-title">
<h2>系统设置</h2>
<p>配置系统运行参数和基础设置</p>
</div>
<div class="header-actions">
<el-button type="primary" @click="saveAllSettings" :loading="saveLoading">
<el-icon><Check /></el-icon>
保存所有设置
</el-button>
<el-button @click="resetSettings">
<el-icon><Refresh /></el-icon>
重置设置
</el-button>
</div>
</div>
<!-- 设置标签页 -->
<el-tabs v-model="activeTab" type="card" class="setting-tabs">
<!-- 基础设置 -->
<el-tab-pane label="基础设置" name="basic">
<div class="setting-panel">
<el-form
ref="basicFormRef"
:model="basicSettings"
:rules="basicRules"
label-width="120px"
>
<el-card header="网站信息">
<el-form-item label="网站名称" prop="siteName">
<el-input
v-model="basicSettings.siteName"
placeholder="请输入网站名称"
maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item label="网站标题" prop="siteTitle">
<el-input
v-model="basicSettings.siteTitle"
placeholder="请输入网站标题"
maxlength="100"
show-word-limit
/>
</el-form-item>
<el-form-item label="网站描述" prop="siteDescription">
<el-input
v-model="basicSettings.siteDescription"
type="textarea"
:rows="3"
placeholder="请输入网站描述"
maxlength="200"
show-word-limit
/>
</el-form-item>
<el-form-item label="网站关键词" prop="siteKeywords">
<el-input
v-model="basicSettings.siteKeywords"
placeholder="请输入网站关键词,用逗号分隔"
maxlength="100"
show-word-limit
/>
</el-form-item>
<el-form-item label="网站Logo" prop="siteLogo">
<el-upload
class="logo-uploader"
:action="uploadUrl"
:headers="uploadHeaders"
:show-file-list="false"
:on-success="handleLogoSuccess"
:before-upload="beforeLogoUpload"
>
<img v-if="basicSettings.siteLogo" :src="basicSettings.siteLogo" class="logo" />
<el-icon v-else class="logo-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
<el-form-item label="网站图标" prop="siteFavicon">
<el-upload
class="favicon-uploader"
:action="uploadUrl"
:headers="uploadHeaders"
:show-file-list="false"
:on-success="handleFaviconSuccess"
:before-upload="beforeFaviconUpload"
>
<img v-if="basicSettings.siteFavicon" :src="basicSettings.siteFavicon" class="favicon" />
<el-icon v-else class="favicon-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-card>
<el-card header="联系信息" style="margin-top: 20px;">
<el-form-item label="联系电话" prop="contactPhone">
<el-input
v-model="basicSettings.contactPhone"
placeholder="请输入联系电话"
/>
</el-form-item>
<el-form-item label="联系邮箱" prop="contactEmail">
<el-input
v-model="basicSettings.contactEmail"
placeholder="请输入联系邮箱"
/>
</el-form-item>
<el-form-item label="联系地址" prop="contactAddress">
<el-input
v-model="basicSettings.contactAddress"
placeholder="请输入联系地址"
/>
</el-form-item>
<el-form-item label="版权信息" prop="copyright">
<el-input
v-model="basicSettings.copyright"
placeholder="请输入版权信息"
/>
</el-form-item>
</el-card>
</el-form>
</div>
</el-tab-pane>
<!-- 系统参数 -->
<el-tab-pane label="系统参数" name="system">
<div class="setting-panel">
<el-form
ref="systemFormRef"
:model="systemSettings"
:rules="systemRules"
label-width="120px"
>
<el-card header="系统配置">
<el-form-item label="系统版本" prop="version">
<el-input
v-model="systemSettings.version"
placeholder="请输入系统版本"
readonly
/>
</el-form-item>
<el-form-item label="时区设置" prop="timezone">
<el-select
v-model="systemSettings.timezone"
placeholder="请选择时区"
style="width: 100%;"
>
<el-option label="北京时间 (UTC+8)" value="Asia/Shanghai" />
<el-option label="东京时间 (UTC+9)" value="Asia/Tokyo" />
<el-option label="纽约时间 (UTC-5)" value="America/New_York" />
<el-option label="伦敦时间 (UTC+0)" value="Europe/London" />
</el-select>
</el-form-item>
<el-form-item label="默认语言" prop="defaultLanguage">
<el-select
v-model="systemSettings.defaultLanguage"
placeholder="请选择默认语言"
style="width: 100%;"
>
<el-option label="简体中文" value="zh-CN" />
<el-option label="繁体中文" value="zh-TW" />
<el-option label="English" value="en-US" />
<el-option label="日本語" value="ja-JP" />
</el-select>
</el-form-item>
<el-form-item label="分页大小" prop="pageSize">
<el-input-number
v-model="systemSettings.pageSize"
:min="10"
:max="100"
:step="10"
placeholder="默认分页大小"
/>
</el-form-item>
<el-form-item label="会话超时" prop="sessionTimeout">
<el-input-number
v-model="systemSettings.sessionTimeout"
:min="30"
:max="1440"
placeholder="会话超时时间(分钟)"
/>
</el-form-item>
<el-form-item label="启用调试" prop="debugMode">
<el-switch
v-model="systemSettings.debugMode"
active-text="开启"
inactive-text="关闭"
/>
</el-form-item>
<el-form-item label="启用缓存" prop="cacheEnabled">
<el-switch
v-model="systemSettings.cacheEnabled"
active-text="开启"
inactive-text="关闭"
/>
</el-form-item>
</el-card>
</el-form>
</div>
</el-tab-pane>
<!-- 安全设置 -->
<el-tab-pane label="安全设置" name="security">
<div class="setting-panel">
<el-form
ref="securityFormRef"
:model="securitySettings"
:rules="securityRules"
label-width="120px"
>
<el-card header="密码策略">
<el-form-item label="最小长度" prop="passwordMinLength">
<el-input-number
v-model="securitySettings.passwordMinLength"
:min="6"
:max="20"
placeholder="密码最小长度"
/>
</el-form-item>
<el-form-item label="密码复杂度" prop="passwordComplexity">
<el-checkbox-group v-model="securitySettings.passwordComplexity">
<el-checkbox label="uppercase">包含大写字母</el-checkbox>
<el-checkbox label="lowercase">包含小写字母</el-checkbox>
<el-checkbox label="number">包含数字</el-checkbox>
<el-checkbox label="special">包含特殊字符</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="密码有效期" prop="passwordExpireDays">
<el-input-number
v-model="securitySettings.passwordExpireDays"
:min="0"
:max="365"
placeholder="密码有效期(天),0表示永不过期"
/>
</el-form-item>
<el-form-item label="历史密码" prop="passwordHistoryCount">
<el-input-number
v-model="securitySettings.passwordHistoryCount"
:min="0"
:max="10"
placeholder="不能重复使用的历史密码数量"
/>
</el-form-item>
</el-card>
<el-card header="登录安全" style="margin-top: 20px;">
<el-form-item label="最大登录失败" prop="maxLoginAttempts">
<el-input-number
v-model="securitySettings.maxLoginAttempts"
:min="3"
:max="10"
placeholder="最大登录失败次数"
/>
</el-form-item>
<el-form-item label="锁定时间" prop="lockoutDuration">
<el-input-number
v-model="securitySettings.lockoutDuration"
:min="5"
:max="60"
placeholder="账户锁定时间(分钟)"
/>
</el-form-item>
<el-form-item label="启用验证码" prop="captchaEnabled">
<el-switch
v-model="securitySettings.captchaEnabled"
active-text="开启"
inactive-text="关闭"
/>
</el-form-item>
<el-form-item label="启用双因子认证" prop="twoFactorEnabled">
<el-switch
v-model="securitySettings.twoFactorEnabled"
active-text="开启"
inactive-text="关闭"
/>
</el-form-item>
<el-form-item label="IP白名单" prop="ipWhitelist">
<el-input
v-model="securitySettings.ipWhitelist"
type="textarea"
:rows="3"
placeholder="IP白名单,每行一个IP或IP段"
/>
</el-form-item>
</el-card>
</el-form>
</div>
</el-tab-pane>
<!-- 邮件设置 -->
<el-tab-pane label="邮件设置" name="email">
<div class="setting-panel">
<el-form
ref="emailFormRef"
:model="emailSettings"
:rules="emailRules"
label-width="120px"
>
<el-card header="SMTP配置">
<el-form-item label="SMTP服务器" prop="smtpHost">
<el-input
v-model="emailSettings.smtpHost"
placeholder="请输入SMTP服务器地址"
/>
</el-form-item>
<el-form-item label="SMTP端口" prop="smtpPort">
<el-input-number
v-model="emailSettings.smtpPort"
:min="1"
:max="65535"
placeholder="SMTP端口"
/>
</el-form-item>
<el-form-item label="加密方式" prop="smtpSecurity">
<el-select
v-model="emailSettings.smtpSecurity"
placeholder="请选择加密方式"
style="width: 100%;"
>
<el-option label="无加密" value="none" />
<el-option label="SSL" value="ssl" />
<el-option label="TLS" value="tls" />
</el-select>
</el-form-item>
<el-form-item label="用户名" prop="smtpUsername">
<el-input
v-model="emailSettings.smtpUsername"
placeholder="请输入SMTP用户名"
/>
</el-form-item>
<el-form-item label="密码" prop="smtpPassword">
<el-input
v-model="emailSettings.smtpPassword"
type="password"
placeholder="请输入SMTP密码"
show-password
/>
</el-form-item>
<el-form-item label="发件人名称" prop="fromName">
<el-input
v-model="emailSettings.fromName"
placeholder="请输入发件人名称"
/>
</el-form-item>
<el-form-item label="发件人邮箱" prop="fromEmail">
<el-input
v-model="emailSettings.fromEmail"
placeholder="请输入发件人邮箱"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="testEmailConnection" :loading="testingEmail">
测试连接
</el-button>
</el-form-item>
</el-card>
</el-form>
</div>
</el-tab-pane>
<!-- 文件设置 -->
<el-tab-pane label="文件设置" name="file">
<div class="setting-panel">
<el-form
ref="fileFormRef"
:model="fileSettings"
:rules="fileRules"
label-width="120px"
>
<el-card header="上传配置">
<el-form-item label="最大文件大小" prop="maxFileSize">
<el-input-number
v-model="fileSettings.maxFileSize"
:min="1"
:max="100"
placeholder="最大文件大小(MB)"
/>
</el-form-item>
<el-form-item label="允许的文件类型" prop="allowedFileTypes">
<el-checkbox-group v-model="fileSettings.allowedFileTypes">
<el-checkbox label="jpg">JPG</el-checkbox>
<el-checkbox label="png">PNG</el-checkbox>
<el-checkbox label="gif">GIF</el-checkbox>
<el-checkbox label="pdf">PDF</el-checkbox>
<el-checkbox label="doc">DOC</el-checkbox>
<el-checkbox label="docx">DOCX</el-checkbox>
<el-checkbox label="xls">XLS</el-checkbox>
<el-checkbox label="xlsx">XLSX</el-checkbox>
<el-checkbox label="txt">TXT</el-checkbox>
<el-checkbox label="zip">ZIP</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="存储路径" prop="uploadPath">
<el-input
v-model="fileSettings.uploadPath"
placeholder="请输入文件存储路径"
/>
</el-form-item>
<el-form-item label="URL前缀" prop="urlPrefix">
<el-input
v-model="fileSettings.urlPrefix"
placeholder="请输入文件访问URL前缀"
/>
</el-form-item>
<el-form-item label="启用缩略图" prop="thumbnailEnabled">
<el-switch
v-model="fileSettings.thumbnailEnabled"
active-text="开启"
inactive-text="关闭"
/>
</el-form-item>
<el-form-item label="缩略图尺寸" prop="thumbnailSize" v-if="fileSettings.thumbnailEnabled">
<el-input-number
v-model="fileSettings.thumbnailSize"
:min="50"
:max="500"
placeholder="缩略图尺寸(像素)"
/>
</el-form-item>
</el-card>
</el-form>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template><script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
getSystemSettings,
updateSystemSettings,
testEmailConnection as testEmailConnectionApi,
resetSystemSettings
} from '@/api/setting'
import {
Check,
Refresh,
Plus
} from '@element-plus/icons-vue'
// 响应式数据
const basicFormRef = ref(null)
const systemFormRef = ref(null)
const securityFormRef = ref(null)
const emailFormRef = ref(null)
const fileFormRef = ref(null)
const activeTab = ref('basic')
const saveLoading = ref(false)
const testingEmail = ref(false)
// 基础设置
const basicSettings = reactive({
siteName: '',
siteTitle: '',
siteDescription: '',
siteKeywords: '',
siteLogo: '',
siteFavicon: '',
contactPhone: '',
contactEmail: '',
contactAddress: '',
copyright: ''
})
// 系统设置
const systemSettings = reactive({
version: '1.0.0',
timezone: 'Asia/Shanghai',
defaultLanguage: 'zh-CN',
pageSize: 20,
sessionTimeout: 120,
debugMode: false,
cacheEnabled: true
})
// 安全设置
const securitySettings = reactive({
passwordMinLength: 8,
passwordComplexity: ['lowercase', 'number'],
passwordExpireDays: 90,
passwordHistoryCount: 3,
maxLoginAttempts: 5,
lockoutDuration: 15,
captchaEnabled: true,
twoFactorEnabled: false,
ipWhitelist: ''
})
// 邮件设置
const emailSettings = reactive({
smtpHost: '',
smtpPort: 587,
smtpSecurity: 'tls',
smtpUsername: '',
smtpPassword: '',
fromName: '',
fromEmail: ''
})
// 文件设置
const fileSettings = reactive({
maxFileSize: 10,
allowedFileTypes: ['jpg', 'png', 'pdf', 'doc', 'docx'],
uploadPath: '/uploads',
urlPrefix: '/files',
thumbnailEnabled: true,
thumbnailSize: 200
})
// 上传配置
const uploadUrl = computed(() => '/api/upload')
const uploadHeaders = computed(() => ({
'Authorization': `Bearer ${localStorage.getItem('token')}`
}))
</script>// 加载系统设置
const loadSystemSettings = async () => {
try {
const result = await getSystemSettings()
if (result.status) {
const settings = result.data
// 分配到各个设置对象
Object.assign(basicSettings, settings.basic || {})
Object.assign(systemSettings, settings.system || {})
Object.assign(securitySettings, settings.security || {})
Object.assign(emailSettings, settings.email || {})
Object.assign(fileSettings, settings.file || {})
}
} catch (error) {
console.error('加载系统设置失败:', error)
ElMessage.error('加载设置失败')
}
}// 保存所有设置
const saveAllSettings = async () => {
try {
// 验证所有表单
const forms = [
basicFormRef.value,
systemFormRef.value,
securityFormRef.value,
emailFormRef.value,
fileFormRef.value
]
const validationPromises = forms.map(form => {
return form ? form.validate().catch(() => false) : Promise.resolve(true)
})
const validationResults = await Promise.all(validationPromises)
const isValid = validationResults.every(result => result === true)
if (!isValid) {
ElMessage.error('请检查表单输入')
return
}
saveLoading.value = true
const settingsData = {
basic: basicSettings,
system: systemSettings,
security: securitySettings,
email: emailSettings,
file: fileSettings
}
const result = await updateSystemSettings(settingsData)
if (result.status) {
ElMessage.success('设置保存成功')
} else {
ElMessage.error(result.msg || '保存失败')
}
} catch (error) {
console.error('保存设置失败:', error)
ElMessage.error('保存失败')
} finally {
saveLoading.value = false
}
}
// 重置设置
const resetSettings = () => {
ElMessageBox.confirm(
'确定要重置所有设置为默认值吗?此操作不可恢复。',
'重置确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(async () => {
try {
const result = await resetSystemSettings()
if (result.status) {
ElMessage.success('重置成功')
loadSystemSettings()
} else {
ElMessage.error(result.msg || '重置失败')
}
} catch (error) {
console.error('重置设置失败:', error)
ElMessage.error('重置失败')
}
}).catch(() => {
// 用户取消
})
}// Logo上传成功处理
const handleLogoSuccess = (response) => {
if (response.status) {
basicSettings.siteLogo = response.data.url
ElMessage.success('Logo上传成功')
} else {
ElMessage.error(response.msg || 'Logo上传失败')
}
}
// Logo上传前检查
const beforeLogoUpload = (file) => {
const isImage = file.type.startsWith('image/')
const isLt2M = file.size / 1024 / 1024 < 2
if (!isImage) {
ElMessage.error('只能上传图片文件')
return false
}
if (!isLt2M) {
ElMessage.error('图片大小不能超过2MB')
return false
}
return true
}
// Favicon上传成功处理
const handleFaviconSuccess = (response) => {
if (response.status) {
basicSettings.siteFavicon = response.data.url
ElMessage.success('图标上传成功')
} else {
ElMessage.error(response.msg || '图标上传失败')
}
}
// Favicon上传前检查
const beforeFaviconUpload = (file) => {
const isIcon = file.type === 'image/x-icon' || file.type === 'image/vnd.microsoft.icon'
const isLt1M = file.size / 1024 / 1024 < 1
if (!isIcon) {
ElMessage.error('只能上传ICO格式的图标文件')
return false
}
if (!isLt1M) {
ElMessage.error('图标大小不能超过1MB')
return false
}
return true
}// 测试邮件连接
const testEmailConnection = async () => {
if (!emailFormRef.value) return
try {
await emailFormRef.value.validate()
testingEmail.value = true
const result = await testEmailConnectionApi(emailSettings)
if (result.status) {
ElMessage.success('邮件连接测试成功')
} else {
ElMessage.error(result.msg || '邮件连接测试失败')
}
} catch (error) {
if (error.fields) {
ElMessage.error('请先完善邮件配置')
} else {
console.error('测试邮件连接失败:', error)
ElMessage.error('连接测试失败')
}
} finally {
testingEmail.value = false
}
}// 基础设置验证规则
const basicRules = {
siteName: [
{ required: true, message: '请输入网站名称', trigger: 'blur' }
],
siteTitle: [
{ required: true, message: '请输入网站标题', trigger: 'blur' }
],
contactEmail: [
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
]
}
// 系统设置验证规则
const systemRules = {
timezone: [
{ required: true, message: '请选择时区', trigger: 'change' }
],
defaultLanguage: [
{ required: true, message: '请选择默认语言', trigger: 'change' }
],
pageSize: [
{ required: true, message: '请输入分页大小', trigger: 'blur' },
{ type: 'number', min: 10, max: 100, message: '分页大小必须在10-100之间', trigger: 'blur' }
],
sessionTimeout: [
{ required: true, message: '请输入会话超时时间', trigger: 'blur' },
{ type: 'number', min: 30, max: 1440, message: '会话超时时间必须在30-1440分钟之间', trigger: 'blur' }
]
}
// 安全设置验证规则
const securityRules = {
passwordMinLength: [
{ required: true, message: '请输入密码最小长度', trigger: 'blur' },
{ type: 'number', min: 6, max: 20, message: '密码长度必须在6-20之间', trigger: 'blur' }
],
maxLoginAttempts: [
{ required: true, message: '请输入最大登录失败次数', trigger: 'blur' },
{ type: 'number', min: 3, max: 10, message: '最大登录失败次数必须在3-10之间', trigger: 'blur' }
],
lockoutDuration: [
{ required: true, message: '请输入锁定时间', trigger: 'blur' },
{ type: 'number', min: 5, max: 60, message: '锁定时间必须在5-60分钟之间', trigger: 'blur' }
]
}
// 邮件设置验证规则
const emailRules = {
smtpHost: [
{ required: true, message: '请输入SMTP服务器地址', trigger: 'blur' }
],
smtpPort: [
{ required: true, message: '请输入SMTP端口', trigger: 'blur' },
{ type: 'number', min: 1, max: 65535, message: '端口必须在1-65535之间', trigger: 'blur' }
],
smtpUsername: [
{ required: true, message: '请输入SMTP用户名', trigger: 'blur' }
],
smtpPassword: [
{ required: true, message: '请输入SMTP密码', trigger: 'blur' }
],
fromEmail: [
{ required: true, message: '请输入发件人邮箱', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
]
}
// 文件设置验证规则
const fileRules = {
maxFileSize: [
{ required: true, message: '请输入最大文件大小', trigger: 'blur' },
{ type: 'number', min: 1, max: 100, message: '文件大小必须在1-100MB之间', trigger: 'blur' }
],
uploadPath: [
{ required: true, message: '请输入存储路径', trigger: 'blur' }
],
urlPrefix: [
{ required: true, message: '请输入URL前缀', trigger: 'blur' }
]
}.setting-container {
padding: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid var(--el-border-color);
}
.header-title h2 {
margin: 0 0 8px 0;
color: var(--el-text-color-primary);
}
.header-title p {
margin: 0;
color: var(--el-text-color-regular);
font-size: 14px;
}
.header-actions {
display: flex;
gap: 12px;
}.setting-tabs {
.el-tabs__header {
margin-bottom: 20px;
}
.el-tabs__content {
padding: 0;
}
}
.setting-panel {
.el-card {
border-radius: 8px;
.el-card__header {
background: var(--el-fill-color-lighter);
font-weight: 600;
}
}
}.logo-uploader {
.logo {
width: 120px;
height: 60px;
object-fit: contain;
border: 1px solid var(--el-border-color);
border-radius: 6px;
}
.logo-uploader-icon {
font-size: 28px;
color: var(--el-text-color-secondary);
width: 120px;
height: 60px;
line-height: 60px;
text-align: center;
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
transition: border-color 0.2s ease;
&:hover {
border-color: var(--el-color-primary);
}
}
}
.favicon-uploader {
.favicon {
width: 32px;
height: 32px;
object-fit: contain;
border: 1px solid var(--el-border-color);
border-radius: 4px;
}
.favicon-uploader-icon {
font-size: 16px;
color: var(--el-text-color-secondary);
width: 32px;
height: 32px;
line-height: 32px;
text-align: center;
border: 1px dashed var(--el-border-color);
border-radius: 4px;
cursor: pointer;
transition: border-color 0.2s ease;
&:hover {
border-color: var(--el-color-primary);
}
}
}onMounted(() => {
loadSystemSettings()
}){
path: 'setting',
name: 'Setting',
component: () => import('@/views/sys/setting.vue'),
meta: {
title: '系统设置',
icon: 'Setting',
requiresAuth: true,
permissions: ['setting:manage']
}
}// 获取系统设置
const result = await getSystemSettings()
// 更新系统设置
const updateResult = await updateSystemSettings({
basic: { siteName: '新网站名称' },
system: { pageSize: 30 }
})- 权限控制: 只有超级管理员才能修改系统设置
- 数据验证: 严格验证所有配置参数的有效性
- 安全配置: 密码策略等安全配置要谨慎设置
- 备份恢复: 重要配置修改前应备份原设置
- 实时生效: 某些配置可能需要重启服务才能生效
最后更新时间:2025-09-19