1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-02-07 17:10:07 +08:00

feat: 面板设置增加表单校验

This commit is contained in:
ssongliu 2022-09-16 16:00:49 +08:00 committed by ssongliu
parent 9f5e9d26f5
commit fc3318c8e9
20 changed files with 266 additions and 133 deletions

View File

@ -4,14 +4,14 @@ type SettingInfo struct {
UserName string `json:"userName"` UserName string `json:"userName"`
Email string `json:"email"` Email string `json:"email"`
SessionTimeout string `json:"sessionTimeout"` SessionTimeout int `json:"sessionTimeout"`
LocalTime string `json:"localTime"` LocalTime string `json:"localTime"`
PanelName string `json:"panelName"` PanelName string `json:"panelName"`
Theme string `json:"theme"` Theme string `json:"theme"`
Language string `json:"language"` Language string `json:"language"`
ServerPort string `json:"serverPort"` ServerPort int `json:"serverPort"`
SecurityEntrance string `json:"securityEntrance"` SecurityEntrance string `json:"securityEntrance"`
PasswordTimeOut string `json:"passwordTimeOut"` PasswordTimeOut string `json:"passwordTimeOut"`
ComplexityVerification string `json:"complexityVerification"` ComplexityVerification string `json:"complexityVerification"`
@ -19,7 +19,7 @@ type SettingInfo struct {
MFASecret string `json:"mfaSecret"` MFASecret string `json:"mfaSecret"`
MonitorStatus string `json:"monitorStatus"` MonitorStatus string `json:"monitorStatus"`
MonitorStoreDays string `json:"monitorStoreDays"` MonitorStoreDays int `json:"monitorStoreDays"`
MessageType string `json:"messageType"` MessageType string `json:"messageType"`
EmailVars string `json:"emailVars"` EmailVars string `json:"emailVars"`

View File

@ -2,6 +2,7 @@ package service
import ( import (
"encoding/json" "encoding/json"
"strconv"
"time" "time"
"github.com/1Panel-dev/1Panel/app/dto" "github.com/1Panel-dev/1Panel/app/dto"
@ -37,9 +38,10 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := json.Unmarshal(arr, &info); err != nil { _ = json.Unmarshal(arr, &info)
return nil, err info.MonitorStoreDays, _ = strconv.Atoi(settingMap["MonitorStoreDays"])
} info.ServerPort, _ = strconv.Atoi(settingMap["ServerPort"])
info.SessionTimeout, _ = strconv.Atoi(settingMap["SessionTimeout"])
info.LocalTime = time.Now().Format("2006-01-02 15:04:05") info.LocalTime = time.Now().Format("2006-01-02 15:04:05")
return &info, err return &info, err
} }

View File

@ -4,14 +4,14 @@ export namespace Setting {
password: string; password: string;
email: string; email: string;
sessionTimeout: string; sessionTimeout: number;
localTime: string; localTime: string;
panelName: string; panelName: string;
theme: string; theme: string;
language: string; language: string;
serverPort: string; serverPort: number;
securityEntrance: string; securityEntrance: string;
passwordTimeOut: string; passwordTimeOut: string;
complexityVerification: string; complexityVerification: string;
@ -19,7 +19,7 @@ export namespace Setting {
mfaSecret: string; mfaSecret: string;
monitorStatus: string; monitorStatus: string;
monitorStoreDays: string; monitorStoreDays: number;
messageType: string; messageType: string;
emailVars: string; emailVars: string;
@ -33,7 +33,6 @@ export namespace Setting {
export interface PasswordUpdate { export interface PasswordUpdate {
oldPassword: string; oldPassword: string;
newPassword: string; newPassword: string;
retryPassword: string;
} }
export interface MFAInfo { export interface MFAInfo {
secret: string; secret: string;

View File

@ -15,10 +15,37 @@ const checkIp = (rule: any, value: any, callback: any) => {
} }
}; };
const complexityPassword = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.complexityPassword')));
} else {
const reg = /^(?=.*\d)(?=.*[a-zA-Z])(?=.*[~!@#$%^&*.])[\da-zA-Z~!@#$%^&*.]{8,}$/;
if (!reg.test(value) && value !== '') {
callback(new Error(i18n.global.t('commons.rule.complexityPassword')));
} else {
callback();
}
}
};
const checkName = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.commonName')));
} else {
const reg = /^[a-zA-Z0-9\u4e00-\u9fa5]{1}[a-zA-Z0-9_.\u4e00-\u9fa5-]{0,30}$/;
if (!reg.test(value) && value !== '') {
callback(new Error(i18n.global.t('commons.rule.commonName')));
} else {
callback();
}
}
};
interface CommonRule { interface CommonRule {
requiredInput: FormItemRule; requiredInput: FormItemRule;
requiredSelect: FormItemRule; requiredSelect: FormItemRule;
name: FormItemRule; name: FormItemRule;
password: FormItemRule;
email: FormItemRule; email: FormItemRule;
number: FormItemRule; number: FormItemRule;
ip: FormItemRule; ip: FormItemRule;
@ -37,14 +64,15 @@ export const Rules: CommonRule = {
trigger: 'change', trigger: 'change',
}, },
name: { name: {
type: 'regexp', validator: checkName,
min: 1, trigger: 'blur',
max: 30, },
message: i18n.global.t('commons.rule.commonName'), password: {
validator: complexityPassword,
trigger: 'blur', trigger: 'blur',
pattern: '/^[a-zA-Z0-9\u4e00-\u9fa5]{1}[a-zA-Z0-9_.\u4e00-\u9fa5-]{0,30}$/',
}, },
email: { email: {
required: true,
type: 'email', type: 'email',
message: i18n.global.t('commons.rule.email'), message: i18n.global.t('commons.rule.email'),
trigger: 'blur', trigger: 'blur',

View File

@ -68,6 +68,9 @@ export default {
requiredInput: 'Please enter the required fields', requiredInput: 'Please enter the required fields',
requiredSelect: 'Please select the required fields', requiredSelect: 'Please select the required fields',
commonName: 'Support English, Chinese, numbers, .-_, length 1-30', commonName: 'Support English, Chinese, numbers, .-_, length 1-30',
complexityPassword:
'Please enter a password with more than 8 characters and must contain letters, digits, and special symbols',
commonPassword: 'Please enter a password with more than 6 characters',
email: 'Email format error', email: 'Email format error',
ip: 'Please enter the correct IP address', ip: 'Please enter the correct IP address',
port: 'Please enter the correct port', port: 'Please enter the correct port',

View File

@ -77,6 +77,8 @@ export default {
requiredInput: '请填写必填项', requiredInput: '请填写必填项',
requiredSelect: '请选择必选项', requiredSelect: '请选择必选项',
commonName: '支持英文中文数字.-_,长度1-30', commonName: '支持英文中文数字.-_,长度1-30',
complexityPassword: '请输入 8 位以上必须含有字母数字特殊符号的密码',
commonPassword: '请输入 6 位以上长度密码',
email: '请输入正确的邮箱', email: '请输入正确的邮箱',
number: '请输入正确的数字', number: '请输入正确的数字',
ip: '请输入正确的 IP 地址', ip: '请输入正确的 IP 地址',
@ -298,7 +300,7 @@ export default {
passwordTimeout: '密码过期时间', passwordTimeout: '密码过期时间',
timeoutHelper: ' {0} 天后 面板密码即将过期过期后需要重新设置密码', timeoutHelper: ' {0} 天后 面板密码即将过期过期后需要重新设置密码',
complexity: '密码复杂度验证', complexity: '密码复杂度验证',
complexityHelper: '密码必须满足密码长度大于8位且大写字母小写字母数字特殊字符至少3项组合', complexityHelper: '密码必须满足密码长度大于 8 位且包含字母数字及特殊字符',
mfa: '两步验证', mfa: '两步验证',
mfaHelper1: '下载两步验证手机应用 :', mfaHelper1: '下载两步验证手机应用 :',
mfaHelper2: '使用手机应用扫描以下二维码获取 6 位验证码', mfaHelper2: '使用手机应用扫描以下二维码获取 6 位验证码',

View File

@ -41,7 +41,7 @@ import i18n from '@/lang';
import { computed, reactive, ref, toRefs } from 'vue'; import { computed, reactive, ref, toRefs } from 'vue';
import { File } from '@/api/interface/file'; import { File } from '@/api/interface/file';
import { ElMessage, FormInstance, FormRules } from 'element-plus'; import { ElMessage, FormInstance, FormRules } from 'element-plus';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
import { CompressExtention, CompressType } from '@/enums/files'; import { CompressExtention, CompressType } from '@/enums/files';
import { CompressFile } from '@/api/modules/files'; import { CompressFile } from '@/api/modules/files';
import FileList from '@/components/file-list/index.vue'; import FileList from '@/components/file-list/index.vue';

View File

@ -56,7 +56,7 @@ import { ElMessage, FormInstance, FormRules } from 'element-plus';
import { CreateFile } from '@/api/modules/files'; import { CreateFile } from '@/api/modules/files';
import i18n from '@/lang'; import i18n from '@/lang';
import FileRole from '@/components/file-role/index.vue'; import FileRole from '@/components/file-role/index.vue';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
import FileList from '@/components/file-list/index.vue'; import FileList from '@/components/file-list/index.vue';
const fileForm = ref<FormInstance>(); const fileForm = ref<FormInstance>();

View File

@ -33,7 +33,7 @@ import i18n from '@/lang';
import { reactive, ref, toRefs } from 'vue'; import { reactive, ref, toRefs } from 'vue';
import { File } from '@/api/interface/file'; import { File } from '@/api/interface/file';
import { ElMessage, FormInstance, FormRules } from 'element-plus'; import { ElMessage, FormInstance, FormRules } from 'element-plus';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
import { DeCompressFile } from '@/api/modules/files'; import { DeCompressFile } from '@/api/modules/files';
import { Mimetypes } from '@/global/mimetype'; import { Mimetypes } from '@/global/mimetype';
import FileList from '@/components/file-list/index.vue'; import FileList from '@/components/file-list/index.vue';

View File

@ -36,7 +36,7 @@ import { CompressExtention, CompressType } from '@/enums/files';
import { computed, PropType, reactive, ref, toRefs } from 'vue'; import { computed, PropType, reactive, ref, toRefs } from 'vue';
import { DownloadFile } from '@/api/modules/files'; import { DownloadFile } from '@/api/modules/files';
import { File } from '@/api/interface/file'; import { File } from '@/api/interface/file';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
const props = defineProps({ const props = defineProps({
open: { open: {

View File

@ -27,7 +27,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { MoveFile } from '@/api/modules/files'; import { MoveFile } from '@/api/modules/files';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElMessage, FormInstance, FormRules } from 'element-plus'; import { ElMessage, FormInstance, FormRules } from 'element-plus';
import { toRefs, ref, reactive, PropType, computed } from 'vue'; import { toRefs, ref, reactive, PropType, computed } from 'vue';

View File

@ -24,7 +24,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { RenameRile } from '@/api/modules/files'; import { RenameRile } from '@/api/modules/files';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
import { ElMessage, FormInstance, FormRules } from 'element-plus'; import { ElMessage, FormInstance, FormRules } from 'element-plus';
import { reactive, ref, toRefs } from 'vue'; import { reactive, ref, toRefs } from 'vue';
import { File } from '@/api/interface/file'; import { File } from '@/api/interface/file';

View File

@ -33,7 +33,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { WgetFile } from '@/api/modules/files'; import { WgetFile } from '@/api/modules/files';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElMessage, FormInstance, FormRules } from 'element-plus'; import { ElMessage, FormInstance, FormRules } from 'element-plus';
import { reactive, ref, toRefs } from 'vue'; import { reactive, ref, toRefs } from 'vue';

View File

@ -41,7 +41,7 @@ import { addCommand, editCommand, deleteCommand, getCommandPage } from '@/api/mo
import { reactive, ref } from '@vue/runtime-core'; import { reactive, ref } from '@vue/runtime-core';
import { useDeleteData } from '@/hooks/use-delete-data'; import { useDeleteData } from '@/hooks/use-delete-data';
import type { ElForm } from 'element-plus'; import type { ElForm } from 'element-plus';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';

View File

@ -119,7 +119,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import type { ElForm } from 'element-plus'; import type { ElForm } from 'element-plus';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
import { Host } from '@/api/interface/host'; import { Host } from '@/api/interface/host';
import { Group } from '@/api/interface/group'; import { Group } from '@/api/interface/group';
import { testConn, getHostTree, getHostInfo, addHost, editHost, deleteHost } from '@/api/modules/host'; import { testConn, getHostTree, getHostInfo, addHost, editHost, deleteHost } from '@/api/modules/host';

View File

@ -173,7 +173,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, onBeforeMount, ref, watch, reactive, getCurrentInstance } from 'vue'; import { onMounted, onBeforeMount, ref, watch, reactive, getCurrentInstance } from 'vue';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
import { testConn, getHostTree, addHost } from '@/api/modules/host'; import { testConn, getHostTree, addHost } from '@/api/modules/host';
import { getCommandList } from '@/api/modules/command'; import { getCommandList } from '@/api/modules/command';
import i18n from '@/lang'; import i18n from '@/lang';

View File

@ -7,45 +7,51 @@
<el-radio-button class="topButton" size="large" label="safe">安全</el-radio-button> <el-radio-button class="topButton" size="large" label="safe">安全</el-radio-button>
<el-radio-button class="topButton" size="large" label="backup">备份</el-radio-button> <el-radio-button class="topButton" size="large" label="backup">备份</el-radio-button>
<el-radio-button class="topButton" size="large" label="monitor">监控</el-radio-button> <el-radio-button class="topButton" size="large" label="monitor">监控</el-radio-button>
<el-radio-button class="topButton" size="large" label="message">通知</el-radio-button>
<el-radio-button class="topButton" size="large" label="about">关于</el-radio-button> <el-radio-button class="topButton" size="large" label="about">关于</el-radio-button>
</el-radio-group> </el-radio-group>
</el-card> </el-card>
<Panel v-if="activeNames === 'all' || activeNames === 'panel'" :settingInfo="form" /> <Panel v-if="activeNames === 'all' || activeNames === 'panel'" :settingInfo="form" @on-save="SaveSetting" />
<Safe v-if="activeNames === 'all' || activeNames === 'safe'" :settingInfo="form" /> <Safe v-if="activeNames === 'all' || activeNames === 'safe'" :settingInfo="form" @on-save="SaveSetting" />
<Backup v-if="activeNames === 'all' || activeNames === 'backup'" :settingInfo="form" /> <Backup v-if="activeNames === 'all' || activeNames === 'backup'" :settingInfo="form" @on-save="SaveSetting" />
<Monitor v-if="activeNames === 'all' || activeNames === 'monitor'" :settingInfo="form" /> <Monitor v-if="activeNames === 'all' || activeNames === 'monitor'" :settingInfo="form" @on-save="SaveSetting" />
<Message v-if="activeNames === 'all' || activeNames === 'message'" :settingInfo="form" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted } from 'vue'; import { ref, onMounted, computed } from 'vue';
import { getSettingInfo } from '@/api/modules/setting'; import { getSettingInfo, updateSetting } from '@/api/modules/setting';
import { Setting } from '@/api/interface/setting'; import { Setting } from '@/api/interface/setting';
import Panel from '@/views/setting/tabs/panel.vue'; import Panel from '@/views/setting/tabs/panel.vue';
import Safe from '@/views/setting/tabs/safe.vue'; import Safe from '@/views/setting/tabs/safe.vue';
import Backup from '@/views/setting/tabs/backup.vue'; import Backup from '@/views/setting/tabs/backup.vue';
import Monitor from '@/views/setting/tabs/monitor.vue'; import Monitor from '@/views/setting/tabs/monitor.vue';
import Message from '@/views/setting/tabs/message.vue'; import { GlobalStore } from '@/store';
import { useTheme } from '@/hooks/use-theme';
import { useI18n } from 'vue-i18n';
import { ElMessage, FormInstance } from 'element-plus';
const i18n = useI18n();
const globalStore = GlobalStore();
const themeConfig = computed(() => globalStore.themeConfig);
const activeNames = ref('all'); const activeNames = ref('all');
let form = ref<Setting.SettingInfo>({ let form = ref<Setting.SettingInfo>({
userName: '', userName: '',
password: '', password: '',
email: '', email: '',
sessionTimeout: '', sessionTimeout: 86400,
localTime: '', localTime: '',
panelName: '', panelName: '',
theme: '', theme: '',
language: '', language: '',
serverPort: '', serverPort: 8888,
securityEntrance: '', securityEntrance: '',
passwordTimeOut: '', passwordTimeOut: '',
complexityVerification: '', complexityVerification: '',
mfaStatus: '', mfaStatus: '',
mfaSecret: '',
monitorStatus: '', monitorStatus: '',
monitorStoreDays: '', monitorStoreDays: 30,
messageType: '', messageType: '',
emailVars: '', emailVars: '',
weChatVars: '', weChatVars: '',
@ -58,6 +64,52 @@ const search = async () => {
form.value.password = '******'; form.value.password = '******';
}; };
const { switchDark } = useTheme();
const SaveSetting = async (formEl: FormInstance | undefined, key: string, val: any) => {
if (!formEl) return;
const result = await formEl.validateField('settingInfo.' + key.replace(key[0], key[0].toLowerCase()), callback);
if (!result) {
return;
}
if (val === '') {
return;
}
switch (key) {
case 'Language':
i18n.locale.value = val;
globalStore.updateLanguage(val);
break;
case 'Theme':
globalStore.setThemeConfig({ ...themeConfig.value, theme: val });
switchDark();
break;
case 'PanelName':
globalStore.setThemeConfig({ ...themeConfig.value, panelName: val });
break;
case 'SessionTimeout':
case 'MonitorStoreDays':
case 'ServerPort':
val = val + '';
break;
}
let param = {
key: key,
value: val,
};
await updateSetting(param);
ElMessage.success(i18n.t('commons.msg.operationSuccess'));
search();
};
function callback(error: any) {
if (error) {
return error.message;
} else {
return;
}
}
onMounted(() => { onMounted(() => {
search(); search();
}); });

View File

@ -1,5 +1,5 @@
<template> <template>
<el-form :model="form" label-position="left" label-width="160px"> <el-form :model="form" ref="panelFormRef" label-position="left" label-width="160px">
<el-card style="margin-top: 10px"> <el-card style="margin-top: 10px">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
@ -9,20 +9,28 @@
<el-row> <el-row>
<el-col :span="1"><br /></el-col> <el-col :span="1"><br /></el-col>
<el-col :span="10"> <el-col :span="10">
<el-form-item :label="$t('setting.enableMonitor')"> <el-form-item
:label="$t('setting.enableMonitor')"
:rules="Rules.requiredInput"
prop="settingInfo.monitorStatus"
>
<el-radio-group <el-radio-group
@change="SaveSetting('MonitorStatus', form.settingInfo.monitorStatus)" @change="onSave(panelFormRef, 'MonitorStatus', form.settingInfo.monitorStatus)"
v-model="form.settingInfo.monitorStatus" v-model="form.settingInfo.monitorStatus"
> >
<el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button> <el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button>
<el-radio-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button> <el-radio-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.storeDays')"> <el-form-item
<el-input clearable v-model="form.settingInfo.monitorStoreDays"> :label="$t('setting.storeDays')"
:rules="Rules.number"
prop="settingInfo.monitorStoreDays"
>
<el-input clearable v-model.number="form.settingInfo.monitorStoreDays">
<template #append> <template #append>
<el-button <el-button
@click="SaveSetting('MonitorStoreDays', form.settingInfo.monitorStoreDays)" @click="onSave(panelFormRef, 'MonitorStoreDays', form.settingInfo.monitorStoreDays)"
icon="Collection" icon="Collection"
> >
{{ $t('commons.button.save') }} {{ $t('commons.button.save') }}
@ -40,10 +48,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ElMessage } from 'element-plus'; import { ref } from 'vue';
import { updateSetting, cleanMonitors } from '@/api/modules/setting'; import { FormInstance } from 'element-plus';
import i18n from '@/lang'; import { cleanMonitors } from '@/api/modules/setting';
import { useDeleteData } from '@/hooks/use-delete-data'; import { useDeleteData } from '@/hooks/use-delete-data';
import { Rules } from '@/global/form-rules';
const emit = defineEmits<{ (e: 'on-save', formEl: FormInstance | undefined, key: string, val: any): void }>();
interface Props { interface Props {
settingInfo: any; settingInfo: any;
@ -51,18 +62,14 @@ interface Props {
const form = withDefaults(defineProps<Props>(), { const form = withDefaults(defineProps<Props>(), {
settingInfo: { settingInfo: {
monitorStatus: '', monitorStatus: '',
monitorStoreDays: '', monitorStoreDays: 30,
}, },
}); });
const panelFormRef = ref<FormInstance>();
const SaveSetting = async (key: string, val: string) => { function onSave(formEl: FormInstance | undefined, key: string, val: any) {
let param = { emit('on-save', formEl, key, val);
key: key, }
value: val,
};
await updateSetting(param);
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
};
const onClean = async () => { const onClean = async () => {
await useDeleteData(cleanMonitors, {}, 'commons.msg.delete', true); await useDeleteData(cleanMonitors, {}, 'commons.msg.delete', true);

View File

@ -1,5 +1,5 @@
<template> <template>
<el-form :model="form" label-position="left" label-width="160px"> <el-form :model="form" ref="panelFormRef" label-position="left" label-width="160px">
<el-card style="margin-top: 20px"> <el-card style="margin-top: 20px">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
@ -9,11 +9,11 @@
<el-row> <el-row>
<el-col :span="1"><br /></el-col> <el-col :span="1"><br /></el-col>
<el-col :span="10"> <el-col :span="10">
<el-form-item :label="$t('auth.username')" prop="settingInfo.userName"> <el-form-item :label="$t('auth.username')" :rules="Rules.requiredInput" prop="settingInfo.userName">
<el-input clearable v-model="form.settingInfo.userName"> <el-input clearable v-model="form.settingInfo.userName">
<template #append> <template #append>
<el-button <el-button
@click="SaveSetting('UserName', form.settingInfo.userName)" @click="onSave(panelFormRef, 'UserName', form.settingInfo.userName)"
icon="Collection" icon="Collection"
> >
{{ $t('commons.button.save') }} {{ $t('commons.button.save') }}
@ -21,7 +21,7 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item :label="$t('auth.password')" prop="settingInfo.password"> <el-form-item :label="$t('auth.password')" :rules="Rules.requiredInput" prop="settingInfo.password">
<el-input type="password" clearable disabled v-model="form.settingInfo.password"> <el-input type="password" clearable disabled v-model="form.settingInfo.password">
<template #append> <template #append>
<el-button icon="Setting" @click="passwordVisiable = true"> <el-button icon="Setting" @click="passwordVisiable = true">
@ -30,10 +30,13 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item :label="$t('auth.email')" prop="settingInfo.email"> <el-form-item :label="$t('auth.email')" :rules="Rules.email" prop="settingInfo.email">
<el-input clearable v-model="form.settingInfo.email"> <el-input clearable v-model="form.settingInfo.email">
<template #append> <template #append>
<el-button @click="SaveSetting('Email', form.settingInfo.email)" icon="Collection"> <el-button
@click="onSave(panelFormRef, 'Email', form.settingInfo.email)"
icon="Collection"
>
{{ $t('commons.button.save') }} {{ $t('commons.button.save') }}
</el-button> </el-button>
</template> </template>
@ -42,11 +45,15 @@
<span class="input-help">{{ $t('setting.emailHelper') }}</span> <span class="input-help">{{ $t('setting.emailHelper') }}</span>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.title')" prop="settingInfo.panelName"> <el-form-item
:label="$t('setting.title')"
:rules="Rules.requiredInput"
prop="settingInfo.panelName"
>
<el-input clearable v-model="form.settingInfo.panelName"> <el-input clearable v-model="form.settingInfo.panelName">
<template #append> <template #append>
<el-button <el-button
@click="SaveSetting('PanelName', form.settingInfo.panelName)" @click="onSave(panelFormRef, 'PanelName', form.settingInfo.panelName)"
icon="Collection" icon="Collection"
> >
{{ $t('commons.button.save') }} {{ $t('commons.button.save') }}
@ -54,9 +61,9 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.theme')" prop="settingInfo.theme"> <el-form-item :label="$t('setting.theme')" :rules="Rules.requiredSelect" prop="settingInfo.theme">
<el-radio-group <el-radio-group
@change="SaveSetting('Theme', form.settingInfo.theme)" @change="onSave(panelFormRef, 'Theme', form.settingInfo.theme)"
v-model="form.settingInfo.theme" v-model="form.settingInfo.theme"
> >
<el-radio-button label="dark"> <el-radio-button label="dark">
@ -69,9 +76,13 @@
</el-radio-button> </el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.language')" prop="settingInfo.language"> <el-form-item
:label="$t('setting.language')"
:rules="Rules.requiredSelect"
prop="settingInfo.language"
>
<el-radio-group <el-radio-group
@change="SaveSetting('Language', form.settingInfo.language)" @change="onSave(panelFormRef, 'Language', form.settingInfo.language)"
v-model="form.settingInfo.language" v-model="form.settingInfo.language"
> >
<el-radio-button label="zh">中文</el-radio-button> <el-radio-button label="zh">中文</el-radio-button>
@ -83,11 +94,15 @@
</span> </span>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.sessionTimeout')" prop="settingInfo.sessionTimeout"> <el-form-item
:label="$t('setting.sessionTimeout')"
:rules="Rules.number"
prop="settingInfo.sessionTimeout"
>
<el-input v-model.number="form.settingInfo.sessionTimeout"> <el-input v-model.number="form.settingInfo.sessionTimeout">
<template #append> <template #append>
<el-button <el-button
@click="SaveSetting('SessionTimeout', form.settingInfo.sessionTimeout)" @click="onSave(panelFormRef, 'SessionTimeout', form.settingInfo.sessionTimeout)"
icon="Collection" icon="Collection"
> >
{{ $t('commons.button.save') }} {{ $t('commons.button.save') }}
@ -118,9 +133,20 @@
<el-form-item :label="$t('setting.oldPassword')" prop="oldPassword"> <el-form-item :label="$t('setting.oldPassword')" prop="oldPassword">
<el-input type="password" show-password clearable v-model="passForm.oldPassword" /> <el-input type="password" show-password clearable v-model="passForm.oldPassword" />
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.newPassword')" prop="newPassword"> <el-form-item
v-if="form.settingInfo.complexityVerification === 'disable'"
:label="$t('setting.newPassword')"
prop="newPassword"
>
<el-input type="password" show-password clearable v-model="passForm.newPassword" /> <el-input type="password" show-password clearable v-model="passForm.newPassword" />
</el-form-item> </el-form-item>
<el-form-item
v-if="form.settingInfo.complexityVerification === 'enable'"
:label="$t('setting.newPassword')"
prop="newPasswordComplexity"
>
<el-input type="password" show-password clearable v-model="passForm.newPasswordComplexity" />
</el-form-item>
<el-form-item :label="$t('setting.retryPassword')" prop="retryPassword"> <el-form-item :label="$t('setting.retryPassword')" prop="retryPassword">
<el-input type="password" show-password clearable v-model="passForm.retryPassword" /> <el-input type="password" show-password clearable v-model="passForm.retryPassword" />
</el-form-item> </el-form-item>
@ -137,31 +163,30 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, computed } from 'vue'; import { ref, reactive } from 'vue';
import { ElMessage, ElForm } from 'element-plus'; import { ElMessage, ElForm } from 'element-plus';
import { updateSetting, updatePassword, syncTime } from '@/api/modules/setting'; import { updatePassword, syncTime } from '@/api/modules/setting';
import { Setting } from '@/api/interface/setting';
import { useI18n } from 'vue-i18n';
import { GlobalStore } from '@/store';
import { useTheme } from '@/hooks/use-theme';
import router from '@/routers/router'; import router from '@/routers/router';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { GlobalStore } from '@/store';
const i18n = useI18n();
const globalStore = GlobalStore(); const globalStore = GlobalStore();
const themeConfig = computed(() => globalStore.themeConfig); const emit = defineEmits<{ (e: 'on-save', formEl: FormInstance | undefined, key: string, val: any): void }>();
type FormInstance = InstanceType<typeof ElForm>; type FormInstance = InstanceType<typeof ElForm>;
const passFormRef = ref<FormInstance>(); const passFormRef = ref<FormInstance>();
const passRules = reactive({ const passRules = reactive({
oldPassword: [Rules.requiredInput], oldPassword: [Rules.requiredInput],
newPassword: [Rules.requiredInput], newPassword: [Rules.requiredInput, { min: 6, message: i18n.global.t('commons.rule.passwordLen'), trigger: 'blur' }],
newPasswordComplexity: [Rules.password],
retryPassword: [Rules.requiredInput, { validator: checkPassword, trigger: 'blur' }], retryPassword: [Rules.requiredInput, { validator: checkPassword, trigger: 'blur' }],
}); });
const passwordVisiable = ref<boolean>(false); const passwordVisiable = ref<boolean>(false);
const passForm = reactive<Setting.PasswordUpdate>({ const passForm = reactive({
oldPassword: '', oldPassword: '',
newPassword: '', newPassword: '',
newPasswordComplexity: '',
retryPassword: '', retryPassword: '',
}); });
@ -173,61 +198,49 @@ const form = withDefaults(defineProps<Props>(), {
userName: '', userName: '',
password: '', password: '',
email: '', email: '',
sessionTimeout: '', sessionTimeout: 0,
localTime: '', localTime: '',
panelName: '', panelName: '',
theme: '', theme: '',
language: '', language: '',
complexityVerification: '',
}, },
}); });
const { switchDark } = useTheme(); const panelFormRef = ref<FormInstance>();
const SaveSetting = async (key: string, val: string) => { function onSave(formEl: FormInstance | undefined, key: string, val: any) {
if (val === '') { emit('on-save', formEl, key, val);
return; }
}
switch (key) {
case 'Language':
i18n.locale.value = val;
globalStore.updateLanguage(val);
break;
case 'Theme':
globalStore.setThemeConfig({ ...themeConfig.value, theme: val });
switchDark();
break;
case 'PanelName':
globalStore.setThemeConfig({ ...themeConfig.value, panelName: val });
break;
}
let param = {
key: key,
value: val,
};
await updateSetting(param);
ElMessage.success(i18n.t('commons.msg.operationSuccess'));
};
function checkPassword(rule: any, value: any, callback: any) { function checkPassword(rule: any, value: any, callback: any) {
if (passForm.newPassword !== passForm.retryPassword) { let password =
return callback(new Error(i18n.t('commons.rule.rePassword'))); form.settingInfo.complexityVerification === 'disable' ? passForm.newPassword : passForm.newPasswordComplexity;
if (password !== passForm.retryPassword) {
return callback(new Error(i18n.global.t('commons.rule.rePassword')));
} }
callback(); callback();
} }
const submitChangePassword = async (formEl: FormInstance | undefined) => { const submitChangePassword = async (formEl: FormInstance | undefined) => {
if (!formEl) return; if (!formEl) return;
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
if (!valid) return; if (!valid) return;
await updatePassword(passForm); let password =
form.settingInfo.complexityVerification === 'disable'
? passForm.newPassword
: passForm.newPasswordComplexity;
await updatePassword({ oldPassword: passForm.oldPassword, newPassword: password });
passwordVisiable.value = false; passwordVisiable.value = false;
ElMessage.success(i18n.t('commons.msg.operationSuccess')); ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
router.push({ name: 'login', params: { code: '' } }); router.push({ name: 'login', params: { code: '' } });
globalStore.setLogStatus(false); globalStore.setLogStatus(false);
}); });
}; };
const onSyncTime = async () => { const onSyncTime = async () => {
const res = await syncTime(); const res = await syncTime();
form.settingInfo.localTime = res.data; form.settingInfo.localTime = res.data;
ElMessage.success(i18n.t('commons.msg.operationSuccess')); ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
}; };
</script> </script>

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<el-form :model="form" label-position="left" label-width="160px"> <el-form :model="form" ref="panelFormRef" label-position="left" label-width="160px">
<el-card style="margin-top: 10px"> <el-card style="margin-top: 10px">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
@ -10,11 +10,15 @@
<el-row> <el-row>
<el-col :span="1"><br /></el-col> <el-col :span="1"><br /></el-col>
<el-col :span="10"> <el-col :span="10">
<el-form-item :label="$t('setting.panelPort')"> <el-form-item
:label="$t('setting.panelPort')"
prop="settingInfo.serverPort"
:rules="Rules.number"
>
<el-input clearable v-model="form.settingInfo.serverPort"> <el-input clearable v-model="form.settingInfo.serverPort">
<template #append> <template #append>
<el-button <el-button
@click="SaveSetting('ServerPort', form.settingInfo.serverPort)" @click="onSave(panelFormRef, 'ServerPort', form.settingInfo.serverPort)"
icon="Collection" icon="Collection"
> >
{{ $t('commons.button.save') }} {{ $t('commons.button.save') }}
@ -35,11 +39,17 @@
</span> </span>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.safeEntrance')"> <el-form-item
:label="$t('setting.safeEntrance')"
prop="settingInfo.securityEntrance"
:rules="Rules.requiredInput"
>
<el-input clearable v-model="form.settingInfo.securityEntrance"> <el-input clearable v-model="form.settingInfo.securityEntrance">
<template #append> <template #append>
<el-button <el-button
@click="SaveSetting('SecurityEntrance', form.settingInfo.securityEntrance)" @click="
onSave(panelFormRef, 'SecurityEntrance', form.settingInfo.securityEntrance)
"
icon="Collection" icon="Collection"
> >
{{ $t('commons.button.save') }} {{ $t('commons.button.save') }}
@ -52,8 +62,12 @@
</span> </span>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.passwordTimeout')"> <el-form-item
<el-input clearable v-model="form.settingInfo.passwordTimeOut"> :label="$t('setting.passwordTimeout')"
prop="settingInfo.passwordTimeOut"
:rules="Rules.requiredInput"
>
<el-input disabled v-model="form.settingInfo.passwordTimeOut">
<template #append> <template #append>
<el-button @click="timeoutVisiable = true" icon="Collection"> <el-button @click="timeoutVisiable = true" icon="Collection">
{{ $t('commons.button.set') }} {{ $t('commons.button.set') }}
@ -66,9 +80,19 @@
</span> </span>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.complexity')"> <el-form-item
:label="$t('setting.complexity')"
prop="settingInfo.complexityVerification"
:rules="Rules.requiredSelect"
>
<el-radio-group <el-radio-group
@change="SaveSetting('ComplexityVerification', form.settingInfo.complexityVerification)" @change="
onSave(
panelFormRef,
'ComplexityVerification',
form.settingInfo.complexityVerification,
)
"
v-model="form.settingInfo.complexityVerification" v-model="form.settingInfo.complexityVerification"
> >
<el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button> <el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button>
@ -80,7 +104,11 @@
</span> </span>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.mfa')"> <el-form-item
:label="$t('setting.mfa')"
prop="settingInfo.securityEntrance"
:rules="Rules.requiredSelect"
>
<el-radio-group @change="handleMFA()" v-model="form.settingInfo.mfaStatus"> <el-radio-group @change="handleMFA()" v-model="form.settingInfo.mfaStatus">
<el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button> <el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button>
<el-radio-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button> <el-radio-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button>
@ -143,9 +171,12 @@ import { ElMessage, ElForm } from 'element-plus';
import { Setting } from '@/api/interface/setting'; import { Setting } from '@/api/interface/setting';
import { updateSetting, getMFA, bindMFA } from '@/api/modules/setting'; import { updateSetting, getMFA, bindMFA } from '@/api/modules/setting';
import i18n from '@/lang'; import i18n from '@/lang';
import { Rules } from '@/global/form-rues'; import { Rules } from '@/global/form-rules';
import { dateFromat } from '@/utils/util'; import { dateFromat } from '@/utils/util';
// const emit = defineEmits(['on-save']);
const emit = defineEmits<{ (e: 'on-save', formEl: FormInstance | undefined, key: string, val: any): void }>();
interface Props { interface Props {
settingInfo: any; settingInfo: any;
} }
@ -172,15 +203,11 @@ const otp = reactive<Setting.MFAInfo>({
qrImage: '', qrImage: '',
}); });
const mfaCode = ref(); const mfaCode = ref();
const panelFormRef = ref<FormInstance>();
const SaveSetting = async (key: string, val: string) => { function onSave(formEl: FormInstance | undefined, key: string, val: any) {
let param = { emit('on-save', formEl, key, val);
key: key, }
value: val,
};
await updateSetting(param);
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
};
const handleMFA = async () => { const handleMFA = async () => {
if (form.settingInfo.mfaStatus === 'enable') { if (form.settingInfo.mfaStatus === 'enable') {
@ -205,7 +232,7 @@ const submitTimeout = async (formEl: FormInstance | undefined) => {
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
if (!valid) return; if (!valid) return;
let time = new Date(new Date().getTime() + 3600 * 1000 * 24 * timeoutForm.days); let time = new Date(new Date().getTime() + 3600 * 1000 * 24 * timeoutForm.days);
SaveSetting('PasswordTimeOut', dateFromat(0, 0, time)); await updateSetting({ key: 'PasswordTimeOut', value: dateFromat(0, 0, time) });
form.settingInfo.passwordTimeOut = dateFromat(0, 0, time); form.settingInfo.passwordTimeOut = dateFromat(0, 0, time);
timeoutVisiable.value = false; timeoutVisiable.value = false;
}); });