mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-10-31 22:57:15 +08:00 
			
		
		
		
	refactor: 优化密码策略处理
This commit is contained in:
		| @@ -276,21 +276,23 @@ export interface OptionQuery { | |||||||
| } | } | ||||||
|  |  | ||||||
| /** 基础配置类型 */ | /** 基础配置类型 */ | ||||||
| export interface BasicConfigResp { | export interface BasicConfig { | ||||||
|   site_favicon: string |   SITE_FAVICON: string | ||||||
|   site_logo: string |   SITE_LOGO: string | ||||||
|   site_title: string |   SITE_TITLE: string | ||||||
|   site_copyright: string |   SITE_COPYRIGHT: string | ||||||
| } | } | ||||||
|  |  | ||||||
| /** 安全配置类型 */ | /** 安全配置类型 */ | ||||||
| export interface SecurityConfigResp { | export interface SecurityConfig { | ||||||
|   password_contain_name: OptionResp |   PASSWORD_ERROR_LOCK_COUNT: OptionResp | ||||||
|   password_error_count: OptionResp |   PASSWORD_ERROR_LOCK_MINUTES: OptionResp | ||||||
|   password_lock_minutes: OptionResp |   PASSWORD_EXPIRATION_WARNING_DAYS: OptionResp | ||||||
|   password_min_length: OptionResp |   PASSWORD_EXPIRATION_DAYS: OptionResp | ||||||
|   password_special_char: OptionResp |   PASSWORD_REUSE_POLICY: OptionResp | ||||||
|   password_update_interval: OptionResp |   PASSWORD_MIN_LENGTH: OptionResp | ||||||
|  |   PASSWORD_ALLOW_CONTAIN_USERNAME: OptionResp | ||||||
|  |   PASSWORD_CONTAIN_SPECIAL_CHARACTERS: OptionResp | ||||||
| } | } | ||||||
|  |  | ||||||
| /** 绑定三方账号信息 */ | /** 绑定三方账号信息 */ | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| import { defineStore } from 'pinia' | import { defineStore } from 'pinia' | ||||||
| import { computed, reactive, toRefs } from 'vue' | import { computed, reactive, toRefs } from 'vue' | ||||||
| import { generate, getRgbStr } from '@arco-design/color' | import { generate, getRgbStr } from '@arco-design/color' | ||||||
| import { type BasicConfigResp, listOptionDict } from '@/apis' | import { type BasicConfig, listOptionDict } from '@/apis' | ||||||
| import defaultSettings from '@/config/setting.json' | import defaultSettings from '@/config/setting.json' | ||||||
|  |  | ||||||
| const storeSetup = () => { | const storeSetup = () => { | ||||||
| @@ -55,48 +55,48 @@ const storeSetup = () => { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // 系统配置配置 |   // 系统配置配置 | ||||||
|   const siteConfig = reactive({}) as BasicConfigResp |   const siteConfig = reactive({}) as BasicConfig | ||||||
|   // 初始化系统配置 |   // 初始化系统配置 | ||||||
|   const initSiteConfig = () => { |   const initSiteConfig = () => { | ||||||
|     listOptionDict({ |     listOptionDict({ | ||||||
|       code: ['site_favicon', 'site_logo', 'site_title', 'site_copyright'] |       code: ['SITE_FAVICON', 'SITE_LOGO', 'SITE_TITLE', 'SITE_COPYRIGHT'] | ||||||
|     }).then((res) => { |     }).then((res) => { | ||||||
|       const resMap = new Map() |       const resMap = new Map() | ||||||
|       res.data.forEach((item) => { |       res.data.forEach((item) => { | ||||||
|         resMap.set(item.label, item.value) |         resMap.set(item.label, item.value) | ||||||
|       }) |       }) | ||||||
|       siteConfig.site_logo = resMap.get('site_logo') |       siteConfig.SITE_FAVICON = resMap.get('SITE_FAVICON') | ||||||
|       siteConfig.site_favicon = resMap.get('site_favicon') |       siteConfig.SITE_LOGO = resMap.get('SITE_LOGO') | ||||||
|       siteConfig.site_title = resMap.get('site_title') |       siteConfig.SITE_TITLE = resMap.get('SITE_TITLE') | ||||||
|       siteConfig.site_copyright = resMap.get('site_copyright') |       siteConfig.SITE_COPYRIGHT = resMap.get('SITE_COPYRIGHT') | ||||||
|       document.title = resMap.get('site_title') |       document.title = resMap.get('SITE_TITLE') | ||||||
|       document |       document | ||||||
|         .querySelector('link[rel="shortcut icon"]') |         .querySelector('link[rel="shortcut icon"]') | ||||||
|         ?.setAttribute('href', resMap.get('site_favicon') || '/favicon.ico') |         ?.setAttribute('href', resMap.get('SITE_FAVICON') || '/favicon.ico') | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // 设置系统配置 |   // 设置系统配置 | ||||||
|   const setSiteConfig = (config: BasicConfigResp) => { |   const setSiteConfig = (config: BasicConfig) => { | ||||||
|     Object.assign(siteConfig, config) |     Object.assign(siteConfig, config) | ||||||
|     document.title = config.site_title || '' |     document.title = config.SITE_TITLE || '' | ||||||
|     document.querySelector('link[rel="shortcut icon"]')?.setAttribute('href', config.site_favicon || '/favicon.ico') |     document.querySelector('link[rel="shortcut icon"]')?.setAttribute('href', config.SITE_FAVICON || '/favicon.ico') | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const getFavicon = () => { |   const getFavicon = () => { | ||||||
|     return siteConfig.site_favicon |     return siteConfig.SITE_FAVICON | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const getLogo = () => { |   const getLogo = () => { | ||||||
|     return siteConfig.site_logo |     return siteConfig.SITE_LOGO | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const getTitle = () => { |   const getTitle = () => { | ||||||
|     return siteConfig.site_title |     return siteConfig.SITE_TITLE | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const getCopyright = () => { |   const getCopyright = () => { | ||||||
|     return siteConfig.site_copyright |     return siteConfig.SITE_COPYRIGHT | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return { |   return { | ||||||
|   | |||||||
| @@ -56,7 +56,7 @@ | |||||||
|     <div class="footer">注册于 {{ userInfo.registrationDate }}</div> |     <div class="footer">注册于 {{ userInfo.registrationDate }}</div> | ||||||
|   </a-card> |   </a-card> | ||||||
|  |  | ||||||
|   <a-modal v-model:visible="visible" title="上传头像" :width="400" :footer="false" @close="reset"> |   <a-modal v-model:visible="visible" title="上传头像" :width="width >= 400 ? 400 : '100%'" :footer="false" @close="reset"> | ||||||
|     <a-row> |     <a-row> | ||||||
|       <a-col :span="14" style="width: 200px; height: 200px"> |       <a-col :span="14" style="width: 200px; height: 200px"> | ||||||
|         <VueCropper |         <VueCropper | ||||||
| @@ -95,6 +95,7 @@ | |||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|  | import { useWindowSize } from '@vueuse/core' | ||||||
| import { type FileItem, Message } from '@arco-design/web-vue' | import { type FileItem, Message } from '@arco-design/web-vue' | ||||||
| import { VueCropper } from 'vue-cropper' | import { VueCropper } from 'vue-cropper' | ||||||
| import BasicInfoUpdateModal from './BasicInfoUpdateModal.vue' | import BasicInfoUpdateModal from './BasicInfoUpdateModal.vue' | ||||||
| @@ -103,6 +104,7 @@ import 'vue-cropper/dist/index.css' | |||||||
| import { useUserStore } from '@/stores' | import { useUserStore } from '@/stores' | ||||||
| import getAvatar from '@/utils/avatar' | import getAvatar from '@/utils/avatar' | ||||||
|  |  | ||||||
|  | const { width } = useWindowSize() | ||||||
| const userStore = useUserStore() | const userStore = useUserStore() | ||||||
| const userInfo = computed(() => userStore.userInfo) | const userInfo = computed(() => userStore.userInfo) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ | |||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import type { ModeItem } from '../type' | import type { ModeItem } from '../type' | ||||||
| import VerifyModel from '../components/VerifyModel.vue' | import VerifyModel from '../components/VerifyModel.vue' | ||||||
| import { type OptionResp, type SecurityConfigResp, listOption } from '@/apis' | import { type OptionResp, type SecurityConfig, listOption } from '@/apis' | ||||||
| import { useUserStore } from '@/stores' | import { useUserStore } from '@/stores' | ||||||
|  |  | ||||||
| const userStore = useUserStore() | const userStore = useUserStore() | ||||||
| @@ -82,20 +82,21 @@ const onUpdate = (type: string) => { | |||||||
|   verifyModelRef.value?.open(type) |   verifyModelRef.value?.open(type) | ||||||
| } | } | ||||||
|  |  | ||||||
| const securityConfig = ref<SecurityConfigResp>({ | const securityConfig = ref<SecurityConfig>({ | ||||||
|   password_contain_name: {}, |   PASSWORD_ERROR_LOCK_COUNT: {}, | ||||||
|   password_error_count: {}, |   PASSWORD_ERROR_LOCK_MINUTES: {}, | ||||||
|   password_expiration_days: {}, |   PASSWORD_EXPIRATION_WARNING_DAYS: {}, | ||||||
|   password_lock_minutes: {}, |   PASSWORD_EXPIRATION_DAYS: {}, | ||||||
|   password_min_length: {}, |   PASSWORD_REUSE_POLICY: {}, | ||||||
|   password_special_char: {}, |   PASSWORD_MIN_LENGTH: {}, | ||||||
|   password_update_interval: {} |   PASSWORD_ALLOW_CONTAIN_USERNAME: {}, | ||||||
|  |   PASSWORD_CONTAIN_SPECIAL_CHARACTERS: {} | ||||||
| }) | }) | ||||||
|  |  | ||||||
| // 查询列表数据 | // 查询列表数据 | ||||||
| const getDataList = async () => { | const getDataList = async () => { | ||||||
|   const { data } = await listOption({ code: Object.keys(securityConfig.value) }) |   const { data } = await listOption({ code: Object.keys(securityConfig.value) }) | ||||||
|   securityConfig.value = data.reduce((obj: SecurityConfigResp, option: OptionResp) => { |   securityConfig.value = data.reduce((obj: SecurityConfig, option: OptionResp) => { | ||||||
|     obj[option.code] = { ...option, value: Number.parseInt(option.value) } |     obj[option.code] = { ...option, value: Number.parseInt(option.value) } | ||||||
|     return obj |     return obj | ||||||
|   }, {}) |   }, {}) | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|   <a-form ref="formRef" :model="form" :rules="rules" size="large" layout="vertical" :disabled="!isUpdate"> |   <a-form ref="formRef" :model="form" :rules="rules" size="large" layout="vertical" :disabled="!isUpdate"> | ||||||
|     <a-list class="list-layout" :bordered="false"> |     <a-list class="list-layout" :bordered="false"> | ||||||
|       <a-list-item> |       <a-list-item> | ||||||
|         <a-form-item class="image-item" hide-label field="site_favicon"> |         <a-form-item class="image-item" hide-label field="SITE_FAVICON"> | ||||||
|           {{ siteFavicon?.name }} |           {{ siteFavicon?.name }} | ||||||
|           <template #extra> |           <template #extra> | ||||||
|             {{ siteFavicon?.description }} |             {{ siteFavicon?.description }} | ||||||
| @@ -41,7 +41,7 @@ | |||||||
|         </a-form-item> |         </a-form-item> | ||||||
|       </a-list-item> |       </a-list-item> | ||||||
|       <a-list-item> |       <a-list-item> | ||||||
|         <a-form-item class="image-item" hide-label field="site_logo"> |         <a-form-item class="image-item" hide-label field="SITE_LOGO"> | ||||||
|           {{ siteLogo?.name }} |           {{ siteLogo?.name }} | ||||||
|           <template #extra> |           <template #extra> | ||||||
|             {{ siteLogo?.description }} |             {{ siteLogo?.description }} | ||||||
| @@ -77,12 +77,12 @@ | |||||||
|         </a-form-item> |         </a-form-item> | ||||||
|       </a-list-item> |       </a-list-item> | ||||||
|       <a-list-item style="padding-top: 13px; border: none"> |       <a-list-item style="padding-top: 13px; border: none"> | ||||||
|         <a-form-item class="input-item" :label="siteTitle?.name" field="site_title"> |         <a-form-item class="input-item" :label="siteTitle?.name" field="SITE_TITLE"> | ||||||
|           <a-input v-model.trim="form.site_title" placeholder="请输入网站标题" :max-length="18" /> |           <a-input v-model.trim="form.SITE_TITLE" placeholder="请输入网站标题" :max-length="18" /> | ||||||
|         </a-form-item> |         </a-form-item> | ||||||
|         <a-form-item class="input-item" :label="siteCopyright?.name" field="site_copyright" tooltip="支持HTML标签"> |         <a-form-item class="input-item" :label="siteCopyright?.name" field="SITE_COPYRIGHT" tooltip="支持HTML标签"> | ||||||
|           <a-textarea |           <a-textarea | ||||||
|             v-model.trim="form.site_copyright" |             v-model.trim="form.SITE_COPYRIGHT" | ||||||
|             placeholder="请输入版权信息" |             placeholder="请输入版权信息" | ||||||
|             :auto-size="{ |             :auto-size="{ | ||||||
|               minRows: 3, |               minRows: 3, | ||||||
| @@ -137,15 +137,15 @@ import { useForm } from '@/hooks' | |||||||
|  |  | ||||||
| const formRef = ref<FormInstance>() | const formRef = ref<FormInstance>() | ||||||
| const rules: FormInstance['rules'] = { | const rules: FormInstance['rules'] = { | ||||||
|   site_title: [{ required: true, message: '请输入系统标题' }], |   SITE_TITLE: [{ required: true, message: '请输入系统标题' }], | ||||||
|   site_copyright: [{ required: true, message: '请输入版权信息' }] |   SITE_COPYRIGHT: [{ required: true, message: '请输入版权信息' }] | ||||||
| } | } | ||||||
|  |  | ||||||
| const { form } = useForm({ | const { form } = useForm({ | ||||||
|   site_favicon: '', |   SITE_FAVICON: '', | ||||||
|   site_logo: '', |   SITE_LOGO: '', | ||||||
|   site_title: '', |   SITE_TITLE: '', | ||||||
|   site_copyright: '' |   SITE_COPYRIGHT: '' | ||||||
| }) | }) | ||||||
|  |  | ||||||
| const siteFavicon = ref<OptionResp>() | const siteFavicon = ref<OptionResp>() | ||||||
| @@ -156,10 +156,10 @@ const faviconFile = ref<FileItem>({ uid: '-1' }) | |||||||
| const logoFile = ref<FileItem>({ uid: '-2' }) | const logoFile = ref<FileItem>({ uid: '-2' }) | ||||||
| // 重置 | // 重置 | ||||||
| const reset = () => { | const reset = () => { | ||||||
|   form.site_favicon = siteFavicon.value?.value || '' |   form.SITE_FAVICON = siteFavicon.value?.value || '' | ||||||
|   form.site_logo = siteLogo.value?.value || '' |   form.SITE_LOGO = siteLogo.value?.value || '' | ||||||
|   form.site_title = siteTitle.value?.value || '' |   form.SITE_TITLE = siteTitle.value?.value || '' | ||||||
|   form.site_copyright = siteCopyright.value?.value || '' |   form.SITE_COPYRIGHT = siteCopyright.value?.value || '' | ||||||
|   faviconFile.value.url = siteFavicon.value?.value |   faviconFile.value.url = siteFavicon.value?.value | ||||||
|   logoFile.value.url = siteLogo.value?.value |   logoFile.value.url = siteLogo.value?.value | ||||||
| } | } | ||||||
| @@ -177,16 +177,16 @@ const handleCancel = () => { | |||||||
|  |  | ||||||
| const dataList = ref<OptionResp[]>([]) | const dataList = ref<OptionResp[]>([]) | ||||||
| const queryForm = reactive({ | const queryForm = reactive({ | ||||||
|   code: ['site_title', 'site_copyright', 'site_logo', 'site_favicon'] |   code: ['SITE_TITLE', 'SITE_COPYRIGHT', 'SITE_LOGO', 'SITE_FAVICON'] | ||||||
| }) | }) | ||||||
| // 查询列表数据 | // 查询列表数据 | ||||||
| const getDataList = async () => { | const getDataList = async () => { | ||||||
|   const res = await listOption(queryForm) |   const res = await listOption(queryForm) | ||||||
|   dataList.value = res.data |   dataList.value = res.data | ||||||
|   siteFavicon.value = dataList.value.find((option) => option.code === 'site_favicon') |   siteFavicon.value = dataList.value.find((option) => option.code === 'SITE_FAVICON') | ||||||
|   siteLogo.value = dataList.value.find((option) => option.code === 'site_logo') |   siteLogo.value = dataList.value.find((option) => option.code === 'SITE_LOGO') | ||||||
|   siteTitle.value = dataList.value.find((option) => option.code === 'site_title') |   siteTitle.value = dataList.value.find((option) => option.code === 'SITE_TITLE') | ||||||
|   siteCopyright.value = dataList.value.find((option) => option.code === 'site_copyright') |   siteCopyright.value = dataList.value.find((option) => option.code === 'SITE_COPYRIGHT') | ||||||
|   reset() |   reset() | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -236,7 +236,7 @@ const handleUploadFavicon = (options: RequestOption) => { | |||||||
|     uploadFile(formData) |     uploadFile(formData) | ||||||
|       .then((res) => { |       .then((res) => { | ||||||
|         onSuccess(res) |         onSuccess(res) | ||||||
|         form.site_favicon = res.data.url |         form.SITE_FAVICON = res.data.url | ||||||
|         Message.success('上传成功') |         Message.success('上传成功') | ||||||
|       }) |       }) | ||||||
|       .catch((error) => { |       .catch((error) => { | ||||||
| @@ -266,7 +266,7 @@ const handleUploadLogo = (options: RequestOption) => { | |||||||
|     uploadFile(formData) |     uploadFile(formData) | ||||||
|       .then((res) => { |       .then((res) => { | ||||||
|         onSuccess(res) |         onSuccess(res) | ||||||
|         form.site_logo = res.data.url |         form.SITE_LOGO = res.data.url | ||||||
|         Message.success('上传成功') |         Message.success('上传成功') | ||||||
|       }) |       }) | ||||||
|       .catch((error) => { |       .catch((error) => { | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								src/views/system/config/components/MailSetting.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/views/system/config/components/MailSetting.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | <template> | ||||||
|  |   <div>暂未开放</div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script lang="ts" setup> | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | </style> | ||||||
| @@ -1,52 +1,102 @@ | |||||||
| <template> | <template> | ||||||
|   <a-form ref="formRef" style="margin-top: 20px" :model="form" size="small" label-align="left" :disabled="!isUpdate"> |   <a-form | ||||||
|     <a-list size="small" :bordered="false"> |     ref="formRef" | ||||||
|       <a-list-item style="border: none"> |     :model="form" | ||||||
|         <a-form-item |     size="small" | ||||||
|           :help="form.password_expiration_days.description" |     :auto-label-width="true" | ||||||
|           :label="form.password_expiration_days.name" |     label-align="left" | ||||||
|           field="password_expiration_days" |     :layout="width >= 500 ? 'horizontal' : 'vertical'" | ||||||
|  |     :disabled="!isUpdate" | ||||||
|  |     style="margin-top: 10px" | ||||||
|   > |   > | ||||||
|           <a-input-number v-model="form.password_expiration_days.value" class="input-width" :min="0" :max="999"> |     <a-list size="small" :bordered="false"> | ||||||
|  |       <a-list-item> | ||||||
|  |         <a-form-item | ||||||
|  |           :label="form.PASSWORD_ERROR_LOCK_COUNT.name" | ||||||
|  |           field="PASSWORD_ERROR_LOCK_COUNT" | ||||||
|  |           :help="form.PASSWORD_ERROR_LOCK_COUNT.description" | ||||||
|  |         > | ||||||
|  |           <a-input-number v-model="form.PASSWORD_ERROR_LOCK_COUNT.value" class="input-width" :min="0" :max="10"> | ||||||
|  |             <template #append>次</template> | ||||||
|  |           </a-input-number> | ||||||
|  |         </a-form-item> | ||||||
|  |       </a-list-item> | ||||||
|  |      <a-list-item> | ||||||
|  |         <a-form-item | ||||||
|  |           :label="form.PASSWORD_ERROR_LOCK_MINUTES.name" | ||||||
|  |           field="PASSWORD_ERROR_LOCK_MINUTES" | ||||||
|  |           :help="form.PASSWORD_ERROR_LOCK_MINUTES.description" | ||||||
|  |         > | ||||||
|  |           <a-input-number v-model="form.PASSWORD_ERROR_LOCK_MINUTES.value" class="input-width" :min="1" :max="1440"> | ||||||
|  |             <template #append>分钟</template> | ||||||
|  |           </a-input-number> | ||||||
|  |         </a-form-item> | ||||||
|  |       </a-list-item> | ||||||
|  |       <a-list-item> | ||||||
|  |         <a-form-item | ||||||
|  |           :label="form.PASSWORD_EXPIRATION_WARNING_DAYS.name" | ||||||
|  |           field="PASSWORD_EXPIRATION_WARNING_DAYS" | ||||||
|  |           :help="form.PASSWORD_EXPIRATION_WARNING_DAYS.description" | ||||||
|  |         > | ||||||
|  |           <a-input-number v-model="form.PASSWORD_EXPIRATION_WARNING_DAYS.value" class="input-width" :min="0"> | ||||||
|             <template #append>天</template> |             <template #append>天</template> | ||||||
|           </a-input-number> |           </a-input-number> | ||||||
|         </a-form-item> |         </a-form-item> | ||||||
|       </a-list-item> |       </a-list-item> | ||||||
|       <a-list-item style="border: none"> |       <a-list-item> | ||||||
|         <a-form-item :help="form.password_min_length.description" :label="form.password_min_length.name"> |         <a-form-item | ||||||
|           <a-input-number v-model="form.password_min_length.value" class="input-width" :min="8" :max="32" /> |           :label="form.PASSWORD_EXPIRATION_DAYS.name" | ||||||
|         </a-form-item> |           field="PASSWORD_EXPIRATION_DAYS" | ||||||
|       </a-list-item> |           :help="form.PASSWORD_EXPIRATION_DAYS.description" | ||||||
|       <a-list-item style="border: none"> |         > | ||||||
|         <a-form-item :help="form.password_update_interval.description" :label="form.password_update_interval.name"> |           <a-input-number v-model="form.PASSWORD_EXPIRATION_DAYS.value" class="input-width" :min="0" :max="999"> | ||||||
|           <a-input-number v-model="form.password_update_interval.value" class="input-width" :min="0" :max="9999"> |             <template #append>天</template> | ||||||
|             <template #append>分钟</template> |  | ||||||
|           </a-input-number> |           </a-input-number> | ||||||
|         </a-form-item> |         </a-form-item> | ||||||
|       </a-list-item> |       </a-list-item> | ||||||
|       <a-list-item style="border: none"> |       <a-list-item> | ||||||
|         <a-form-item :help="form.password_error_count.description" :label="form.password_error_count.name"> |         <a-form-item | ||||||
|           <a-input-number v-model="form.password_error_count.value" class="input-width" :min="0" :max="9999" /> |           :label="form.PASSWORD_REUSE_POLICY.name" | ||||||
|         </a-form-item> |           field="PASSWORD_REUSE_POLICY" | ||||||
|       </a-list-item> |           :help="form.PASSWORD_REUSE_POLICY.description" | ||||||
|       <a-list-item style="border: none"> |         > | ||||||
|         <a-form-item :help="form.password_lock_minutes.description" :label="form.password_lock_minutes.name"> |           <a-input-number v-model="form.PASSWORD_REUSE_POLICY.value" class="input-width" :min="3" :max="32"> | ||||||
|           <a-input-number v-model="form.password_lock_minutes.value" class="input-width" :min="0" :max="9999"> |             <template #append>次</template> | ||||||
|             <template #append>分钟</template> |  | ||||||
|           </a-input-number> |           </a-input-number> | ||||||
|         </a-form-item> |         </a-form-item> | ||||||
|       </a-list-item> |       </a-list-item> | ||||||
|       <a-list-item style="border: none"> |       <a-list-item> | ||||||
|         <a-form-item :help="form.password_special_char.description" :label="form.password_special_char.name"> |         <a-form-item | ||||||
|           <a-switch v-model="form.password_special_char.value" type="round" :checked-value="1" :unchecked-value="0" /> |           :label="form.PASSWORD_MIN_LENGTH.name" | ||||||
|  |           field="PASSWORD_MIN_LENGTH" | ||||||
|  |           :help="form.PASSWORD_MIN_LENGTH.description" | ||||||
|  |         > | ||||||
|  |           <a-input-number v-model="form.PASSWORD_MIN_LENGTH.value" class="input-width" :min="8" :max="32" /> | ||||||
|         </a-form-item> |         </a-form-item> | ||||||
|       </a-list-item> |       </a-list-item> | ||||||
|       <a-list-item style="border: none"> |       <a-list-item> | ||||||
|         <a-form-item :help="form.password_contain_name.description" :label="form.password_contain_name.name"> |         <a-form-item | ||||||
|           <a-switch v-model="form.password_contain_name.value" type="round" :checked-value="1" :unchecked-value="0" /> |           :label="form.PASSWORD_ALLOW_CONTAIN_USERNAME.name" | ||||||
|  |           field="PASSWORD_ALLOW_CONTAIN_USERNAME" | ||||||
|  |         > | ||||||
|  |           <a-switch v-model="form.PASSWORD_ALLOW_CONTAIN_USERNAME.value" type="round" :checked-value="1" :unchecked-value="0"> | ||||||
|  |             <template #checked>是</template> | ||||||
|  |             <template #unchecked>否</template> | ||||||
|  |           </a-switch> | ||||||
|         </a-form-item> |         </a-form-item> | ||||||
|       </a-list-item> |       </a-list-item> | ||||||
|       <a-list-item style="padding-top: 13px; border: none"> |       <a-list-item> | ||||||
|  |         <a-form-item | ||||||
|  |           :label="form.PASSWORD_CONTAIN_SPECIAL_CHARACTERS.name" | ||||||
|  |           field="PASSWORD_CONTAIN_SPECIAL_CHARACTERS" | ||||||
|  |         > | ||||||
|  |           <a-switch v-model="form.PASSWORD_CONTAIN_SPECIAL_CHARACTERS.value" type="round" :checked-value="1" :unchecked-value="0"> | ||||||
|  |             <template #checked>是</template> | ||||||
|  |             <template #unchecked>否</template> | ||||||
|  |           </a-switch> | ||||||
|  |         </a-form-item> | ||||||
|  |       </a-list-item> | ||||||
|  |       <a-list-item> | ||||||
|         <a-space> |         <a-space> | ||||||
|           <a-button v-if="!isUpdate" v-permission="['system:config:reset']" @click="onResetValue"> |           <a-button v-if="!isUpdate" v-permission="['system:config:reset']" @click="onResetValue"> | ||||||
|             <template #icon> |             <template #icon> | ||||||
| @@ -85,39 +135,42 @@ | |||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|  | import { useWindowSize } from '@vueuse/core' | ||||||
| import { type FormInstance, Message, Modal } from '@arco-design/web-vue' | import { type FormInstance, Message, Modal } from '@arco-design/web-vue' | ||||||
| import { type OptionResp, type SecurityConfigResp, listOption, resetOptionValue, updateOption } from '@/apis' | import { type OptionResp, type SecurityConfig, listOption, resetOptionValue, updateOption } from '@/apis' | ||||||
|  |  | ||||||
|  | const { width } = useWindowSize() | ||||||
|  |  | ||||||
| const formRef = ref<FormInstance>() | const formRef = ref<FormInstance>() | ||||||
|  | const form = ref<SecurityConfig>({ | ||||||
| const form = ref<SecurityConfigResp>({ |   PASSWORD_ERROR_LOCK_COUNT: {}, | ||||||
|   password_contain_name: {}, |   PASSWORD_ERROR_LOCK_MINUTES: {}, | ||||||
|   password_error_count: {}, |   PASSWORD_EXPIRATION_WARNING_DAYS: {}, | ||||||
|   password_expiration_days: {}, |   PASSWORD_EXPIRATION_DAYS: {}, | ||||||
|   password_lock_minutes: {}, |   PASSWORD_REUSE_POLICY: {}, | ||||||
|   password_min_length: {}, |   PASSWORD_MIN_LENGTH: {}, | ||||||
|   password_special_char: {}, |   PASSWORD_ALLOW_CONTAIN_USERNAME: {}, | ||||||
|   password_update_interval: {} |   PASSWORD_CONTAIN_SPECIAL_CHARACTERS: {} | ||||||
| }) | }) | ||||||
|  |  | ||||||
| const isUpdate = ref(false) |  | ||||||
| // 修改 |  | ||||||
| const onUpdate = () => { |  | ||||||
|   isUpdate.value = true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const queryForm = { | const queryForm = { | ||||||
|   code: Object.keys(form.value) |   code: Object.keys(form.value) | ||||||
| } | } | ||||||
| // 查询列表数据 | // 查询列表数据 | ||||||
| const getDataList = async () => { | const getDataList = async () => { | ||||||
|   const { data } = await listOption(queryForm) |   const { data } = await listOption(queryForm) | ||||||
|   form.value = data.reduce((obj: SecurityConfigResp, option: OptionResp) => { |   form.value = data.reduce((obj: SecurityConfig, option: OptionResp) => { | ||||||
|     obj[option.code] = { ...option, value: Number.parseInt(option.value) } |     obj[option.code] = { ...option, value: Number.parseInt(option.value) } | ||||||
|     return obj |     return obj | ||||||
|   }, {}) |   }, {}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const isUpdate = ref(false) | ||||||
|  | // 修改 | ||||||
|  | const onUpdate = () => { | ||||||
|  |   isUpdate.value = true | ||||||
|  | } | ||||||
|  |  | ||||||
| // 重置 | // 重置 | ||||||
| const reset = () => { | const reset = () => { | ||||||
|   getDataList() |   getDataList() | ||||||
| @@ -155,12 +208,17 @@ const onResetValue = () => { | |||||||
|     onOk: handleResetValue |     onOk: handleResetValue | ||||||
|   }) |   }) | ||||||
| } | } | ||||||
|  |  | ||||||
| onMounted(() => { | onMounted(() => { | ||||||
|   getDataList() |   getDataList() | ||||||
| }) | }) | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
|  | :deep(.arco-list-item:not(:last-child)) { | ||||||
|  |   border-bottom: none; | ||||||
|  | } | ||||||
|  |  | ||||||
| .input-width { | .input-width { | ||||||
|   width: 130px; |   width: 130px; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,7 +5,9 @@ | |||||||
|         <a-tab-pane key="1" title="基础配置"> |         <a-tab-pane key="1" title="基础配置"> | ||||||
|           <BasicSetting /> |           <BasicSetting /> | ||||||
|         </a-tab-pane> |         </a-tab-pane> | ||||||
|         <a-tab-pane key="2" title="邮件配置(暂未开放)" disabled></a-tab-pane> |         <a-tab-pane key="2" title="邮件配置"> | ||||||
|  |           <MailSetting /> | ||||||
|  |         </a-tab-pane> | ||||||
|         <a-tab-pane key="3" title="安全配置"> |         <a-tab-pane key="3" title="安全配置"> | ||||||
|           <SecuritySetting /> |           <SecuritySetting /> | ||||||
|         </a-tab-pane> |         </a-tab-pane> | ||||||
| @@ -16,6 +18,7 @@ | |||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import BasicSetting from './components/BasicSetting.vue' | import BasicSetting from './components/BasicSetting.vue' | ||||||
|  | import MailSetting from './components/MailSetting.vue' | ||||||
| import SecuritySetting from './components/SecuritySetting.vue' | import SecuritySetting from './components/SecuritySetting.vue' | ||||||
|  |  | ||||||
| defineOptions({ name: 'SystemConfig' }) | defineOptions({ name: 'SystemConfig' }) | ||||||
| @@ -25,4 +28,8 @@ defineOptions({ name: 'SystemConfig' }) | |||||||
| :deep(.arco-tabs-content) { | :deep(.arco-tabs-content) { | ||||||
|   padding-top: 5px; |   padding-top: 5px; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | :deep(.arco-tabs-tab) { | ||||||
|  |   background-color: var(--color-fill-2); | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user