mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-10-31 10:57:10 +08:00 
			
		
		
		
	| @@ -5,6 +5,7 @@ export * from './dept' | ||||
| export * from './notice' | ||||
| export * from './dict' | ||||
| export * from './file' | ||||
| export * from './storage' | ||||
| export * from './option' | ||||
| export * from './user-center' | ||||
| export * from './message' | ||||
|   | ||||
							
								
								
									
										31
									
								
								src/apis/system/storage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/apis/system/storage.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import type * as T from './type' | ||||
| import http from '@/utils/http' | ||||
|  | ||||
| export type * from './type' | ||||
|  | ||||
| const BASE_URL = '/system/storage' | ||||
|  | ||||
| /** @desc 查询存储列表 */ | ||||
| export function listStorage(query: T.StoragePageQuery) { | ||||
|   return http.get<PageRes<T.StorageResp[]>>(`${BASE_URL}`, query) | ||||
| } | ||||
|  | ||||
| /** @desc 查询存储详情 */ | ||||
| export function getStorage(id: string) { | ||||
|   return http.get<T.StorageResp>(`${BASE_URL}/${id}`) | ||||
| } | ||||
|  | ||||
| /** @desc 新增存储 */ | ||||
| export function addStorage(data: any) { | ||||
|   return http.post(`${BASE_URL}`, data) | ||||
| } | ||||
|  | ||||
| /** @desc 修改存储 */ | ||||
| export function updateStorage(data: any, id: string) { | ||||
|   return http.put(`${BASE_URL}/${id}`, data) | ||||
| } | ||||
|  | ||||
| /** @desc 删除存储 */ | ||||
| export function deleteStorage(id: string) { | ||||
|   return http.del(`${BASE_URL}/${id}`) | ||||
| } | ||||
| @@ -231,6 +231,34 @@ export interface FileQuery { | ||||
| export interface FilePageQuery extends FileQuery, PageQuery { | ||||
| } | ||||
|  | ||||
| /** 存储类型 */ | ||||
| export interface StorageResp { | ||||
|   id: string | ||||
|   name: string | ||||
|   code: string | ||||
|   type: number | ||||
|   accessKey: string | ||||
|   secretKey: string | ||||
|   endpoint: string | ||||
|   bucketName: string | ||||
|   domain: string | ||||
|   description: string | ||||
|   isDefault: boolean | ||||
|   sort: number | ||||
|   status: number | ||||
|   createUserString: string | ||||
|   createTime: string | ||||
|   updateUserString: string | ||||
|   updateTime: string | ||||
| } | ||||
| export interface StorageQuery { | ||||
|   description?: string | ||||
|   status?: number | ||||
|   sort: Array<string> | ||||
| } | ||||
| export interface StoragePageQuery extends StorageQuery, PageQuery { | ||||
| } | ||||
|  | ||||
| /** 客户端类型 */ | ||||
| export interface ClientResp { | ||||
|   id: string | ||||
| @@ -306,7 +334,6 @@ export interface SiteConfig { | ||||
|   SITE_TITLE: OptionResp | ||||
|   SITE_DESCRIPTION: OptionResp | ||||
|   SITE_COPYRIGHT: OptionResp | ||||
|   SITE_DOMAIN: OptionResp | ||||
|   SITE_BEIAN: OptionResp | ||||
| } | ||||
|  | ||||
| @@ -333,18 +360,6 @@ export interface MailConfig { | ||||
|   MAIL_SSL_PORT: OptionResp | ||||
| } | ||||
|  | ||||
| /** 存储配置类型 */ | ||||
| export interface StorageConfig { | ||||
|   STORAGE_DEFAULT: OptionResp | ||||
|   STORAGE_LOCAL_BUCKET: OptionResp | ||||
|   STORAGE_LOCAL_ENDPOINT: OptionResp | ||||
|   STORAGE_OSS_ACCESS_KEY: OptionResp | ||||
|   STORAGE_OSS_SECRET_KEY: OptionResp | ||||
|   STORAGE_OSS_BUCKET: OptionResp | ||||
|   STORAGE_OSS_ENDPOINT: OptionResp | ||||
|   STORAGE_OSS_REGION: OptionResp | ||||
| } | ||||
|  | ||||
| /** 登录配置类型 */ | ||||
| export interface LoginConfig { | ||||
|   LOGIN_CAPTCHA_ENABLED: OptionResp | ||||
|   | ||||
| @@ -72,26 +72,24 @@ | ||||
|           </a-upload> | ||||
|         </template> | ||||
|       </a-form-item> | ||||
|       <a-form-item class="input-item" field="SITE_TITLE" :label="siteConfig.SITE_TITLE.name" :help="siteConfig.SITE_TITLE.description"> | ||||
|         <a-input v-model.trim="form.SITE_TITLE" placeholder="请输入网站名称" :max-length="18" show-word-limit /> | ||||
|       <a-form-item class="input-item" field="SITE_TITLE" :label="siteConfig.SITE_TITLE.name"> | ||||
|         <a-input v-model.trim="form.SITE_TITLE" class="input-width" placeholder="请输入系统标题" :max-length="18" show-word-limit /> | ||||
|       </a-form-item> | ||||
|       <a-form-item class="input-item" field="SITE_DESCRIPTION" :label="siteConfig.SITE_DESCRIPTION.name" :help="siteConfig.SITE_DESCRIPTION.description"> | ||||
|       <a-form-item class="input-item" field="SITE_DESCRIPTION" :label="siteConfig.SITE_DESCRIPTION.name"> | ||||
|         <a-textarea | ||||
|           v-model.trim="form.SITE_DESCRIPTION" | ||||
|           placeholder="请输入网站描述" | ||||
|           class="input-width" | ||||
|           placeholder="请输入系统描述" | ||||
|           :auto-size="{ minRows: 1, maxRows: 3 }" | ||||
|         /> | ||||
|       </a-form-item> | ||||
|       <a-form-item class="input-item" field="SITE_COPYRIGHT" :label="siteConfig.SITE_COPYRIGHT.name" :help="siteConfig.SITE_COPYRIGHT.description"> | ||||
|         <a-input v-model.trim="form.SITE_COPYRIGHT" placeholder="请输入版权声明" /> | ||||
|       <a-form-item class="input-item" field="SITE_COPYRIGHT" :label="siteConfig.SITE_COPYRIGHT.name"> | ||||
|         <a-input v-model.trim="form.SITE_COPYRIGHT" class="input-width" placeholder="请输入版权信息" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item class="input-item" field="SITE_DOMAIN" :label="siteConfig.SITE_DOMAIN.name" :help="siteConfig.SITE_DOMAIN.description"> | ||||
|         <a-input v-model.trim="form.SITE_DOMAIN" placeholder="请输入网站域名" /> | ||||
|       <a-form-item field="SITE_BEIAN" :label="siteConfig.SITE_BEIAN.name"> | ||||
|         <a-input v-model.trim="form.SITE_BEIAN" class="input-width" placeholder="请输入备案号" :max-length="30" show-word-limit /> | ||||
|       </a-form-item> | ||||
|       <a-form-item field="SITE_BEIAN" :label="siteConfig.SITE_BEIAN.name" :help="siteConfig.SITE_BEIAN.description"> | ||||
|         <a-input v-model.trim="form.SITE_BEIAN" placeholder="请输入网站备案号" :max-length="30" show-word-limit /> | ||||
|       </a-form-item> | ||||
|       <a-space style="margin-top: 16px"> | ||||
|       <a-space style="margin-bottom: 16px"> | ||||
|         <a-button v-if="!isUpdate" v-permission="['system:config:update']" type="primary" @click="onUpdate"> | ||||
|           <template #icon> | ||||
|             <icon-edit /> | ||||
| @@ -146,9 +144,9 @@ const [form] = useResetReactive({ | ||||
|   SITE_COPYRIGHT: '', | ||||
| }) | ||||
| const rules: FormInstance['rules'] = { | ||||
|   SITE_TITLE: [{ required: true, message: '请输入网站名称' }], | ||||
|   SITE_DESCRIPTION: [{ required: true, message: '请输入网站描述' }], | ||||
|   SITE_COPYRIGHT: [{ required: true, message: '请输入版权声明' }], | ||||
|   SITE_TITLE: [{ required: true, message: '请输入系统标题' }], | ||||
|   SITE_DESCRIPTION: [{ required: true, message: '请输入系统描述' }], | ||||
|   SITE_COPYRIGHT: [{ required: true, message: '请输入版权信息' }], | ||||
| } | ||||
| 
 | ||||
| const siteConfig = ref<SiteConfig>({ | ||||
| @@ -157,7 +155,6 @@ const siteConfig = ref<SiteConfig>({ | ||||
|   SITE_TITLE: {}, | ||||
|   SITE_DESCRIPTION: {}, | ||||
|   SITE_COPYRIGHT: {}, | ||||
|   SITE_DOMAIN: {}, | ||||
|   SITE_BEIAN: {}, | ||||
| }) | ||||
| const faviconFile = ref<FileItem>({ uid: '-1' }) | ||||
| @@ -169,7 +166,6 @@ const reset = () => { | ||||
|   form.SITE_LOGO = siteConfig.value.SITE_LOGO.value || '' | ||||
|   form.SITE_TITLE = siteConfig.value.SITE_TITLE.value || '' | ||||
|   form.SITE_DESCRIPTION = siteConfig.value.SITE_DESCRIPTION.value || '' | ||||
|   form.SITE_DOMAIN = siteConfig.value.SITE_DOMAIN.value || '' | ||||
|   form.SITE_COPYRIGHT = siteConfig.value.SITE_COPYRIGHT.value || '' | ||||
|   form.SITE_BEIAN = siteConfig.value.SITE_BEIAN.value || '' | ||||
|   faviconFile.value.url = siteConfig.value.SITE_FAVICON.value | ||||
| @@ -317,8 +313,7 @@ onMounted(() => { | ||||
|   line-height: 46px; | ||||
| } | ||||
| 
 | ||||
| :deep(.form .arco-input-wrapper), | ||||
| :deep(.form .arco-textarea-wrapper) { | ||||
| .input-width { | ||||
|   width: 500px; | ||||
| } | ||||
| 
 | ||||
| @@ -332,8 +327,9 @@ onMounted(() => { | ||||
| } | ||||
| 
 | ||||
| // responsive | ||||
| :deep(.mobile .form .arco-input-wrapper), | ||||
| :deep(.mobile .form .arco-textarea-wrapper) { | ||||
|   width: 100%; | ||||
| .mobile { | ||||
|   .input-width { | ||||
|     width: 100%; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -9,56 +9,25 @@ | ||||
|       :layout="width >= 500 ? 'horizontal' : 'vertical'" | ||||
|       :disabled="!isUpdate" | ||||
|       scroll-to-first-error | ||||
|       class="form" | ||||
|     > | ||||
|       <a-form-item | ||||
|         field="MAIL_PROTOCOL" | ||||
|         :label="mailConfig.MAIL_PROTOCOL.name" | ||||
|         :help="mailConfig.MAIL_PROTOCOL.description" | ||||
|         hide-asterisk | ||||
|       > | ||||
|       <a-form-item field="MAIL_PROTOCOL" :label="mailConfig.MAIL_PROTOCOL.name" hide-asterisk> | ||||
|         <a-select v-model.trim="form.MAIL_PROTOCOL"> | ||||
|           <a-option label="SMTP" value="smtp" /> | ||||
|         </a-select> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         field="MAIL_HOST" | ||||
|         :label="mailConfig.MAIL_HOST.name" | ||||
|         :help="mailConfig.MAIL_HOST.description" | ||||
|         hide-asterisk | ||||
|       > | ||||
|         <a-input v-model.trim="form.MAIL_HOST" /> | ||||
|       <a-form-item field="MAIL_HOST" :label="mailConfig.MAIL_HOST.name" hide-asterisk> | ||||
|         <a-input v-model.trim="form.MAIL_HOST" class="input-width" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         field="MAIL_PORT" | ||||
|         :label="mailConfig.MAIL_PORT.name" | ||||
|         :help="mailConfig.MAIL_PORT.description" | ||||
|         hide-asterisk | ||||
|       > | ||||
|         <a-input-number v-model="form.MAIL_PORT" :min="0" /> | ||||
|       <a-form-item field="MAIL_PORT" :label="mailConfig.MAIL_PORT.name" hide-asterisk> | ||||
|         <a-input-number v-model="form.MAIL_PORT" class="input-width" :min="0" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         field="MAIL_USERNAME" | ||||
|         :label="mailConfig.MAIL_USERNAME.name" | ||||
|         :help="mailConfig.MAIL_USERNAME.description" | ||||
|         hide-asterisk | ||||
|       > | ||||
|         <a-input v-model.trim="form.MAIL_USERNAME" /> | ||||
|       <a-form-item field="MAIL_USERNAME" :label="mailConfig.MAIL_USERNAME.name" hide-asterisk> | ||||
|         <a-input v-model.trim="form.MAIL_USERNAME" class="input-width" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         field="MAIL_PASSWORD" | ||||
|         :label="mailConfig.MAIL_PASSWORD?.name" | ||||
|         :help="mailConfig.MAIL_PASSWORD.description" | ||||
|         hide-asterisk | ||||
|       > | ||||
|         <a-input-password v-model.trim="form.MAIL_PASSWORD" /> | ||||
|       <a-form-item field="MAIL_PASSWORD" :label="mailConfig.MAIL_PASSWORD?.name" hide-asterisk> | ||||
|         <a-input-password v-model.trim="form.MAIL_PASSWORD" class="input-width" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         field="MAIL_SSL_ENABLED" | ||||
|         :label="mailConfig.MAIL_SSL_ENABLED?.name" | ||||
|         :help="mailConfig.MAIL_SSL_ENABLED.description" | ||||
|         hide-asterisk | ||||
|       > | ||||
|       <a-form-item field="MAIL_SSL_ENABLED" :label="mailConfig.MAIL_SSL_ENABLED?.name" hide-asterisk> | ||||
|         <a-switch | ||||
|           v-model="form.MAIL_SSL_ENABLED" | ||||
|           type="round" | ||||
| @@ -70,13 +39,10 @@ | ||||
|         </a-switch> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         v-if="form.MAIL_SSL_ENABLED === '1'" | ||||
|         field="MAIL_SSL_PORT" | ||||
|         :label="mailConfig.MAIL_SSL_PORT.name" | ||||
|         :help="mailConfig.MAIL_SSL_PORT.description" | ||||
|         v-if="form.MAIL_SSL_ENABLED === '1'" field="MAIL_SSL_PORT" :label="mailConfig.MAIL_SSL_PORT.name" | ||||
|         hide-asterisk | ||||
|       > | ||||
|         <a-input-number v-model="form.MAIL_SSL_PORT" :min="0" /> | ||||
|         <a-input-number v-model="form.MAIL_SSL_PORT" class="input-width" :min="0" /> | ||||
|       </a-form-item> | ||||
|       <a-space style="margin-bottom: 16px"> | ||||
|         <a-button v-if="!isUpdate" v-permission="['system:config:update']" type="primary" @click="onUpdate"> | ||||
| @@ -220,8 +186,7 @@ onMounted(() => { | ||||
|   margin-bottom: 5px; | ||||
| } | ||||
|  | ||||
| :deep(.form .arco-input-wrapper), | ||||
| :deep(.arco-select-view-single) { | ||||
| .input-width, :deep(.arco-select-view-single) { | ||||
|   width: 220px; | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -9,49 +9,35 @@ | ||||
|       :layout="width >= 500 ? 'horizontal' : 'vertical'" | ||||
|       :disabled="!isUpdate" | ||||
|       scroll-to-first-error | ||||
|       class="form" | ||||
|     > | ||||
|       <a-form-item | ||||
|         field="PASSWORD_ERROR_LOCK_COUNT" | ||||
|         :label="securityConfig.PASSWORD_ERROR_LOCK_COUNT.name" | ||||
|         :help="securityConfig.PASSWORD_ERROR_LOCK_COUNT.description" | ||||
|         hide-asterisk | ||||
|         field="PASSWORD_ERROR_LOCK_COUNT" :label="securityConfig.PASSWORD_ERROR_LOCK_COUNT.name" | ||||
|         :help="securityConfig.PASSWORD_ERROR_LOCK_COUNT.description" hide-asterisk | ||||
|       > | ||||
|         <a-input-number | ||||
|           v-model="form.PASSWORD_ERROR_LOCK_COUNT" | ||||
|           :default-value="0" | ||||
|           :precision="0" | ||||
|           :min="0" | ||||
|           :max="10" | ||||
|           v-model="form.PASSWORD_ERROR_LOCK_COUNT" class="input-width" :default-value="0" :precision="0" | ||||
|           :min="0" :max="10" | ||||
|         > | ||||
|           <template #append>次</template> | ||||
|         </a-input-number> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         field="PASSWORD_ERROR_LOCK_MINUTES" | ||||
|         :label="securityConfig.PASSWORD_ERROR_LOCK_MINUTES.name" | ||||
|         :help="securityConfig.PASSWORD_ERROR_LOCK_MINUTES.description" | ||||
|         hide-asterisk | ||||
|         field="PASSWORD_ERROR_LOCK_MINUTES" :label="securityConfig.PASSWORD_ERROR_LOCK_MINUTES.name" | ||||
|         :help="securityConfig.PASSWORD_ERROR_LOCK_MINUTES.description" hide-asterisk | ||||
|       > | ||||
|         <a-input-number | ||||
|           v-model="form.PASSWORD_ERROR_LOCK_MINUTES" | ||||
|           :precision="0" | ||||
|           :min="1" | ||||
|           v-model="form.PASSWORD_ERROR_LOCK_MINUTES" class="input-width" :precision="0" :min="1" | ||||
|           :max="1440" | ||||
|         > | ||||
|           <template #append>分钟</template> | ||||
|         </a-input-number> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         field="PASSWORD_EXPIRATION_DAYS" | ||||
|         :label="securityConfig.PASSWORD_EXPIRATION_DAYS.name" | ||||
|         :help="securityConfig.PASSWORD_EXPIRATION_DAYS.description" | ||||
|         hide-asterisk | ||||
|         field="PASSWORD_EXPIRATION_DAYS" :label="securityConfig.PASSWORD_EXPIRATION_DAYS.name" | ||||
|         :help="securityConfig.PASSWORD_EXPIRATION_DAYS.description" hide-asterisk | ||||
|       > | ||||
|         <a-input-number | ||||
|           v-model="form.PASSWORD_EXPIRATION_DAYS" | ||||
|           :precision="0" | ||||
|           :min="0" | ||||
|           v-model="form.PASSWORD_EXPIRATION_DAYS" class="input-width" :precision="0" :min="0" | ||||
|           :max="999" | ||||
|         > | ||||
|           <template #append>天</template> | ||||
| @@ -59,62 +45,43 @@ | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         :label="securityConfig.PASSWORD_EXPIRATION_WARNING_DAYS.name" | ||||
|         field="PASSWORD_EXPIRATION_WARNING_DAYS" | ||||
|         :help="securityConfig.PASSWORD_EXPIRATION_WARNING_DAYS.description" | ||||
|         field="PASSWORD_EXPIRATION_WARNING_DAYS" :help="securityConfig.PASSWORD_EXPIRATION_WARNING_DAYS.description" | ||||
|         hide-asterisk | ||||
|       > | ||||
|         <a-input-number | ||||
|           v-model="form.PASSWORD_EXPIRATION_WARNING_DAYS" | ||||
|           :precision="0" | ||||
|           :min="0" | ||||
|           v-model="form.PASSWORD_EXPIRATION_WARNING_DAYS" class="input-width" :precision="0" :min="0" | ||||
|           :max="998" | ||||
|         > | ||||
|           <template #append>天</template> | ||||
|         </a-input-number> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         field="PASSWORD_REPETITION_TIMES" | ||||
|         :label="securityConfig.PASSWORD_REPETITION_TIMES.name" | ||||
|         :help="securityConfig.PASSWORD_REPETITION_TIMES.description" | ||||
|         hide-asterisk | ||||
|         field="PASSWORD_REPETITION_TIMES" :label="securityConfig.PASSWORD_REPETITION_TIMES.name" | ||||
|         :help="securityConfig.PASSWORD_REPETITION_TIMES.description" hide-asterisk | ||||
|       > | ||||
|         <a-input-number | ||||
|           v-model="form.PASSWORD_REPETITION_TIMES" | ||||
|           :precision="0" | ||||
|           :min="3" | ||||
|           v-model="form.PASSWORD_REPETITION_TIMES" class="input-width" :precision="0" :min="3" | ||||
|           :max="32" | ||||
|         > | ||||
|           <template #append>次</template> | ||||
|         </a-input-number> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         field="PASSWORD_MIN_LENGTH" | ||||
|         :label="securityConfig.PASSWORD_MIN_LENGTH.name" | ||||
|         :help="securityConfig.PASSWORD_MIN_LENGTH.description" | ||||
|         hide-asterisk | ||||
|         field="PASSWORD_MIN_LENGTH" :label="securityConfig.PASSWORD_MIN_LENGTH.name" | ||||
|         :help="securityConfig.PASSWORD_MIN_LENGTH.description" hide-asterisk | ||||
|       > | ||||
|         <a-input-number | ||||
|           v-model="form.PASSWORD_MIN_LENGTH" | ||||
|           :precision="0" | ||||
|           :min="8" | ||||
|           :max="32" | ||||
|         /> | ||||
|         <a-input-number v-model="form.PASSWORD_MIN_LENGTH" class="input-width" :precision="0" :min="8" :max="32" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         field="PASSWORD_ALLOW_CONTAIN_USERNAME" | ||||
|         :label="securityConfig.PASSWORD_ALLOW_CONTAIN_USERNAME.name" | ||||
|         :help="securityConfig.PASSWORD_ALLOW_CONTAIN_USERNAME.description" | ||||
|       > | ||||
|         <a-switch v-model="form.PASSWORD_ALLOW_CONTAIN_USERNAME" type="round" :checked-value="1" :unchecked-value="0"> | ||||
|           <template #checked>是</template> | ||||
|           <template #unchecked>否</template> | ||||
|         </a-switch> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         field="PASSWORD_REQUIRE_SYMBOLS" | ||||
|         :label="securityConfig.PASSWORD_REQUIRE_SYMBOLS.name" | ||||
|         :help="securityConfig.PASSWORD_REQUIRE_SYMBOLS.description" | ||||
|       > | ||||
|       <a-form-item field="PASSWORD_REQUIRE_SYMBOLS" :label="securityConfig.PASSWORD_REQUIRE_SYMBOLS.name"> | ||||
|         <a-switch v-model="form.PASSWORD_REQUIRE_SYMBOLS" type="round" :checked-value="1" :unchecked-value="0"> | ||||
|           <template #checked>是</template> | ||||
|           <template #unchecked>否</template> | ||||
| @@ -171,7 +138,7 @@ const rules: FormInstance['rules'] = { | ||||
|     { | ||||
|       validator: (value, callback) => { | ||||
|         if (form.PASSWORD_EXPIRATION_DAYS > 0 && value >= form.PASSWORD_EXPIRATION_DAYS) { | ||||
|           callback('密码到期提醒时间应小于密码有效期') | ||||
|           callback('密码到期前的提示时间应小于密码有效期') | ||||
|         } else { | ||||
|           callback() | ||||
|         } | ||||
| @@ -271,7 +238,7 @@ onMounted(() => { | ||||
|   margin-bottom: 5px; | ||||
| } | ||||
|  | ||||
| :deep(.form .arco-input-wrapper) { | ||||
| .input-width { | ||||
|   width: 200px; | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -1,261 +0,0 @@ | ||||
| <template> | ||||
|   <a-spin :loading="loading"> | ||||
|     <a-form | ||||
|       ref="formRef" | ||||
|       :model="form" | ||||
|       :rules="form.STORAGE_DEFAULT === 'LOCAL' ? localRules : ossRules" | ||||
|       auto-label-width | ||||
|       label-align="left" | ||||
|       :layout="width >= 500 ? 'horizontal' : 'vertical'" | ||||
|       :disabled="!isUpdate" | ||||
|       scroll-to-first-error | ||||
|       class="form" | ||||
|     > | ||||
|       <!-- 默认存储 --> | ||||
|       <a-form-item | ||||
|         field="STORAGE_DEFAULT" | ||||
|         :label="storageConfig.STORAGE_DEFAULT.name" | ||||
|       > | ||||
|         <a-radio-group v-model="form.STORAGE_DEFAULT"> | ||||
|           <a-radio value="LOCAL">本地存储</a-radio> | ||||
|           <a-radio value="OSS">对象存储</a-radio> | ||||
|         </a-radio-group> | ||||
|       </a-form-item> | ||||
|  | ||||
|       <fieldset> | ||||
|         <legend>本地存储配置</legend> | ||||
|         <a-form-item | ||||
|           field="STORAGE_LOCAL_BUCKET" | ||||
|           :label="storageConfig.STORAGE_LOCAL_BUCKET.name" | ||||
|           :help="storageConfig.STORAGE_LOCAL_BUCKET.description" | ||||
|           hide-asterisk | ||||
|         > | ||||
|           <a-input v-model="form.STORAGE_LOCAL_BUCKET" /> | ||||
|         </a-form-item> | ||||
|         <a-form-item | ||||
|           field="STORAGE_LOCAL_ENDPOINT" | ||||
|           :label="storageConfig.STORAGE_LOCAL_ENDPOINT.name" | ||||
|           :help="storageConfig.STORAGE_LOCAL_ENDPOINT.description" | ||||
|           hide-asterisk | ||||
|         > | ||||
|           <a-input v-model="form.STORAGE_LOCAL_ENDPOINT" /> | ||||
|         </a-form-item> | ||||
|       </fieldset> | ||||
|  | ||||
|       <fieldset> | ||||
|         <legend>对象存储配置</legend> | ||||
|         <a-form-item | ||||
|           field="STORAGE_OSS_ACCESS_KEY" | ||||
|           :label="storageConfig.STORAGE_OSS_ACCESS_KEY.name" | ||||
|           :help="storageConfig.STORAGE_OSS_ACCESS_KEY.description" | ||||
|           hide-asterisk | ||||
|         > | ||||
|           <a-input v-model="form.STORAGE_OSS_ACCESS_KEY" /> | ||||
|         </a-form-item> | ||||
|         <a-form-item | ||||
|           field="STORAGE_OSS_SECRET_KEY" | ||||
|           :label="storageConfig.STORAGE_OSS_SECRET_KEY.name" | ||||
|           :help="storageConfig.STORAGE_OSS_SECRET_KEY.description" | ||||
|           hide-asterisk | ||||
|         > | ||||
|           <a-input-password v-model="form.STORAGE_OSS_SECRET_KEY" /> | ||||
|         </a-form-item> | ||||
|         <a-form-item | ||||
|           field="STORAGE_OSS_BUCKET" | ||||
|           :label="storageConfig.STORAGE_OSS_BUCKET.name" | ||||
|           :help="storageConfig.STORAGE_OSS_BUCKET.description" | ||||
|           hide-asterisk | ||||
|         > | ||||
|           <a-input v-model="form.STORAGE_OSS_BUCKET" /> | ||||
|         </a-form-item> | ||||
|         <a-form-item | ||||
|           field="STORAGE_OSS_ENDPOINT" | ||||
|           :label="storageConfig.STORAGE_OSS_ENDPOINT.name" | ||||
|           :help="storageConfig.STORAGE_OSS_ENDPOINT.description" | ||||
|           hide-asterisk | ||||
|         > | ||||
|           <a-input v-model="form.STORAGE_OSS_ENDPOINT" /> | ||||
|         </a-form-item> | ||||
|         <a-form-item | ||||
|           field="STORAGE_OSS_REGION" | ||||
|           :label="storageConfig.STORAGE_OSS_REGION.name" | ||||
|           :help="storageConfig.STORAGE_OSS_REGION.description" | ||||
|           hide-asterisk | ||||
|         > | ||||
|           <a-input v-model="form.STORAGE_OSS_REGION" /> | ||||
|         </a-form-item> | ||||
|       </fieldset> | ||||
|  | ||||
|       <!-- 操作按钮 --> | ||||
|       <a-space style="margin-bottom: 16px"> | ||||
|         <a-button v-if="!isUpdate" v-permission="['system:config:update']" type="primary" @click="onUpdate"> | ||||
|           <template #icon> | ||||
|             <icon-edit /> | ||||
|           </template> | ||||
|           修改 | ||||
|         </a-button> | ||||
|         <a-button v-if="!isUpdate" v-permission="['system:config:reset']" @click="onResetValue"> | ||||
|           <template #icon> | ||||
|             <icon-undo /> | ||||
|           </template> | ||||
|           恢复默认 | ||||
|         </a-button> | ||||
|         <a-button v-if="isUpdate" type="primary" @click="handleSave"> | ||||
|           <template #icon> | ||||
|             <icon-save /> | ||||
|           </template> | ||||
|           保存 | ||||
|         </a-button> | ||||
|         <a-button v-if="isUpdate" @click="reset"> | ||||
|           <template #icon> | ||||
|             <icon-refresh /> | ||||
|           </template> | ||||
|           重置 | ||||
|         </a-button> | ||||
|         <a-button v-if="isUpdate" @click="handleCancel"> | ||||
|           <template #icon> | ||||
|             <icon-undo /> | ||||
|           </template> | ||||
|           取消 | ||||
|         </a-button> | ||||
|       </a-space> | ||||
|     </a-form> | ||||
|   </a-spin> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { type FormInstance, Message, Modal } from '@arco-design/web-vue' | ||||
| import { | ||||
|   type OptionResp, | ||||
|   type SecurityConfig, | ||||
|   type StorageConfig, | ||||
|   listOption, | ||||
|   resetOptionValue, | ||||
|   updateOption, | ||||
| } from '@/apis/system' | ||||
| import { useResetReactive } from '@/hooks' | ||||
|  | ||||
| defineOptions({ name: 'StorageSetting' }) | ||||
| const { width } = useWindowSize() | ||||
|  | ||||
| const loading = ref<boolean>(false) | ||||
| const formRef = ref<FormInstance>() | ||||
|  | ||||
| const [form] = useResetReactive({ | ||||
|   STORAGE_DEFAULT: 'LOCAL', // 默认选中本地存储 | ||||
|   STORAGE_LOCAL_BUCKET: '', | ||||
|   STORAGE_LOCAL_ENDPOINT: '', | ||||
|   STORAGE_OSS_ACCESS_KEY: '', | ||||
|   STORAGE_OSS_SECRET_KEY: '', | ||||
|   STORAGE_OSS_BUCKET: '', | ||||
|   STORAGE_OSS_ENDPOINT: '', | ||||
|   STORAGE_OSS_REGION: '', | ||||
| }) | ||||
|  | ||||
| const localRules: FormInstance['rules'] = { | ||||
|   STORAGE_LOCAL_BUCKET: [{ required: true, message: '请输入本地存储路径' }], | ||||
|   STORAGE_LOCAL_ENDPOINT: [{ required: true, message: '请输入本地资源访问地址' }], | ||||
| } | ||||
|  | ||||
| const ossRules: FormInstance['rules'] = { | ||||
|   STORAGE_OSS_ACCESS_KEY: [{ required: true, message: '请输入Access Key' }], | ||||
|   STORAGE_OSS_SECRET_KEY: [{ required: true, message: '请输入Secret Key' }], | ||||
|   STORAGE_OSS_BUCKET: [{ required: true, message: '请输入对象存储桶名称' }], | ||||
|   STORAGE_OSS_ENDPOINT: [{ required: true, message: '请输入对象存储终端节点' }], | ||||
| } | ||||
|  | ||||
| const storageConfig = ref<StorageConfig>({ | ||||
|   STORAGE_DEFAULT: {}, | ||||
|   STORAGE_LOCAL_BUCKET: {}, | ||||
|   STORAGE_LOCAL_ENDPOINT: {}, | ||||
|   STORAGE_OSS_ACCESS_KEY: {}, | ||||
|   STORAGE_OSS_SECRET_KEY: {}, | ||||
|   STORAGE_OSS_BUCKET: {}, | ||||
|   STORAGE_OSS_ENDPOINT: {}, | ||||
|   STORAGE_OSS_REGION: {}, | ||||
| }) | ||||
|  | ||||
| const reset = () => { | ||||
|   formRef.value?.resetFields() | ||||
|   Object.keys(form).forEach((key) => { | ||||
|     form[key] = storageConfig.value[key]?.value || form[key] // 兜底设置默认值 | ||||
|   }) | ||||
| } | ||||
|  | ||||
| const isUpdate = ref(false) | ||||
|  | ||||
| const onUpdate = () => { | ||||
|   isUpdate.value = true | ||||
| } | ||||
|  | ||||
| const handleCancel = () => { | ||||
|   reset() | ||||
|   isUpdate.value = false | ||||
| } | ||||
|  | ||||
| const queryForm = { category: 'STORAGE' } | ||||
|  | ||||
| const getDataList = async () => { | ||||
|   loading.value = true | ||||
|   const { data } = await listOption(queryForm) | ||||
|   storageConfig.value = data.reduce((obj: SecurityConfig, option: OptionResp) => { | ||||
|     obj[option.code] = { ...option, value: option.value } | ||||
|     return obj | ||||
|   }, {}) | ||||
|   handleCancel() | ||||
|   loading.value = false | ||||
| } | ||||
|  | ||||
| const handleSave = async () => { | ||||
|   const isInvalid = await formRef.value?.validate() | ||||
|   if (isInvalid) return | ||||
|   await updateOption( | ||||
|     Object.entries(form).map(([key, value]) => ({ id: storageConfig.value[key].id, code: key, value })), | ||||
|   ) | ||||
|   await getDataList() | ||||
|   Message.success('保存成功') | ||||
| } | ||||
|  | ||||
| const handleResetValue = async () => { | ||||
|   await resetOptionValue(queryForm) | ||||
|   Message.success('恢复成功') | ||||
|   await getDataList() | ||||
| } | ||||
|  | ||||
| const onResetValue = () => { | ||||
|   Modal.warning({ | ||||
|     title: '警告', | ||||
|     content: '确认恢复存储配置为默认值吗?', | ||||
|     hideCancel: false, | ||||
|     maskClosable: false, | ||||
|     onOk: handleResetValue, | ||||
|   }) | ||||
| } | ||||
|  | ||||
| onMounted(() => { | ||||
|   getDataList() | ||||
| }) | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| :deep(.form .arco-input-wrapper) { | ||||
|   width: 400px; | ||||
| } | ||||
|  | ||||
| fieldset { | ||||
|   display: inline-block; | ||||
|   width: fit-content; | ||||
|   min-width: 0; | ||||
|   padding: 15px; | ||||
|   margin-bottom: 15px; | ||||
|   border: 1px solid var(--color-neutral-3); | ||||
|   border-radius: 3px; | ||||
| } | ||||
| fieldset legend { | ||||
|   color: rgb(var(--gray-10)); | ||||
|   padding: 2px 5px 2px 5px; | ||||
|   border: 1px solid var(--color-neutral-3); | ||||
|   border-radius: 3px; | ||||
| } | ||||
| </style> | ||||
| @@ -6,19 +6,16 @@ | ||||
|       </a-space> | ||||
|     </a-row> --> | ||||
|     <a-tabs v-model:active-key="activeKey" type="card-gutter" size="large" @change="change"> | ||||
|       <a-tab-pane key="site"> | ||||
|         <template #title><icon-apps /> 网站配置</template> | ||||
|       <a-tab-pane key="1"> | ||||
|         <template #title><icon-settings /> 基础配置</template> | ||||
|       </a-tab-pane> | ||||
|       <a-tab-pane key="security"> | ||||
|       <a-tab-pane key="2"> | ||||
|         <template #title><icon-safe /> 安全配置</template> | ||||
|       </a-tab-pane> | ||||
|       <a-tab-pane key="mail"> | ||||
|       <a-tab-pane key="3"> | ||||
|         <template #title><icon-email /> 邮件配置</template> | ||||
|       </a-tab-pane> | ||||
|       <a-tab-pane key="storage"> | ||||
|         <template #title><icon-storage /> 存储配置</template> | ||||
|       </a-tab-pane> | ||||
|       <a-tab-pane key="login"> | ||||
|       <a-tab-pane key="4"> | ||||
|         <template #title><icon-lock /> 登录配置</template> | ||||
|       </a-tab-pane> | ||||
|     </a-tabs> | ||||
| @@ -30,37 +27,35 @@ | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { useRoute, useRouter } from 'vue-router' | ||||
| import SiteSetting from './components/SiteSetting.vue' | ||||
| import BasicSetting from './components/BasicSetting.vue' | ||||
| import SecuritySetting from './components/SecuritySetting.vue' | ||||
| import MailSetting from './components/MailSetting.vue' | ||||
| import LoginSetting from './components/LoginSetting.vue' | ||||
| import StorageSetting from './components/StorageSetting.vue' | ||||
|  | ||||
| defineOptions({ name: 'SystemConfig' }) | ||||
|  | ||||
| const PanMap: Record<string, Component> = { | ||||
|   site: SiteSetting, | ||||
|   security: SecuritySetting, | ||||
|   mail: MailSetting, | ||||
|   storage: StorageSetting, | ||||
|   login: LoginSetting, | ||||
|   1: BasicSetting, | ||||
|   2: SecuritySetting, | ||||
|   3: MailSetting, | ||||
|   4: LoginSetting, | ||||
| } | ||||
|  | ||||
| const route = useRoute() | ||||
| const router = useRouter() | ||||
| const activeKey = ref('site') | ||||
| const activeKey = ref('1') | ||||
| watch( | ||||
|   () => route.query, | ||||
|   () => { | ||||
|     if (route.query.tab) { | ||||
|       activeKey.value = String(route.query.tab) | ||||
|     if (route.query.tabKey) { | ||||
|       activeKey.value = String(route.query.tabKey) | ||||
|     } | ||||
|   }, | ||||
|   { immediate: true }, | ||||
| ) | ||||
| const change = (key: string | number) => { | ||||
|   activeKey.value = key as string | ||||
|   router.replace({ path: route.path, query: { tab: key } }) | ||||
|   router.replace({ path: route.path, query: { tabKey: key } }) | ||||
| } | ||||
| </script> | ||||
|  | ||||
|   | ||||
							
								
								
									
										198
									
								
								src/views/system/storage/StorageAddDrawer.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								src/views/system/storage/StorageAddDrawer.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | ||||
| <template> | ||||
|   <a-drawer | ||||
|     v-model:visible="visible" | ||||
|     :title="title" | ||||
|     :mask-closable="false" | ||||
|     :esc-to-close="false" | ||||
|     :width="width >= 600 ? 600 : '100%'" | ||||
|     @before-ok="save" | ||||
|     @close="reset" | ||||
|   > | ||||
|     <a-form ref="formRef" :model="form" :rules="rules" size="large" auto-label-width> | ||||
|       <a-form-item label="名称" field="name"> | ||||
|         <a-input v-model.trim="form.name" placeholder="请输入名称" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="编码" field="code"> | ||||
|         <a-input v-model.trim="form.code" placeholder="请输入编码" :disabled="isUpdate" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="类型" field="type"> | ||||
|         <a-select v-model.trim="form.type" :options="storage_type_enum" placeholder="请选择类型" :disabled="isUpdate" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item v-if="form.type === 1" label="访问密钥" field="accessKey"> | ||||
|         <a-input v-model.trim="form.accessKey" placeholder="请输入访问密钥" :max-length="255" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item v-if="form.type === 1" label="私有密钥" field="secretKey"> | ||||
|         <a-input | ||||
|           v-model.trim="form.secretKey" | ||||
|           placeholder="请输入私有密钥" | ||||
|           :max-length="255" | ||||
|         /> | ||||
|       </a-form-item> | ||||
|       <a-form-item v-if="form.type === 1" label="终端节点" field="endpoint"> | ||||
|         <a-input v-model.trim="form.endpoint" placeholder="请输入终端节点" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="桶名称" field="bucketName"> | ||||
|         <a-input v-model.trim="form.bucketName" placeholder="请输入桶名称" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item v-if="form.type === 1" label="域名" field="domain"> | ||||
|         <a-input v-model.trim="form.domain" placeholder="请输入域名" /> | ||||
|         <template #extra> | ||||
|           <div v-if="defaultDomain"> | ||||
|             <span>留空默认域名:{{ defaultDomain }}</span> | ||||
|           </div> | ||||
|         </template> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         v-if="form.type === 2" | ||||
|         label="域名" | ||||
|         field="domain" | ||||
|         :rules="[ | ||||
|           { | ||||
|             required: true, | ||||
|             message: '请输入域名', | ||||
|           }, | ||||
|         ]" | ||||
|       > | ||||
|         <a-input v-model.trim="form.domain" placeholder="请输入域名" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="排序" field="sort"> | ||||
|         <a-input-number v-model="form.sort" placeholder="请输入排序" :min="1" mode="button" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="描述" field="description"> | ||||
|         <a-textarea | ||||
|           v-model.trim="form.description" | ||||
|           placeholder="请输入描述" | ||||
|           show-word-limit | ||||
|           :max-length="200" | ||||
|           :auto-size="{ minRows: 3, maxRows: 5 }" | ||||
|         /> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="默认存储" field="isDefault"> | ||||
|         <a-switch | ||||
|           v-model="form.isDefault" | ||||
|           type="round" | ||||
|           :checked-value="true" | ||||
|           :unchecked-value="false" | ||||
|           checked-text="是" | ||||
|           unchecked-text="否" | ||||
|         /> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="状态" field="status"> | ||||
|         <a-switch | ||||
|           v-model="form.status" | ||||
|           type="round" | ||||
|           :checked-value="1" | ||||
|           :unchecked-value="2" | ||||
|           checked-text="启用" | ||||
|           unchecked-text="禁用" | ||||
|         /> | ||||
|       </a-form-item> | ||||
|     </a-form> | ||||
|   </a-drawer> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { type FormInstance, Message } from '@arco-design/web-vue' | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { addStorage, getStorage, updateStorage } from '@/apis/system/storage' | ||||
| import { useResetReactive } from '@/hooks' | ||||
| import { useDict } from '@/hooks/app' | ||||
| import { encryptByRsa } from '@/utils/encrypt' | ||||
| import { isIPv4 } from '@/utils/validate' | ||||
|  | ||||
| const emit = defineEmits<{ | ||||
|   (e: 'save-success'): void | ||||
| }>() | ||||
|  | ||||
| const { width } = useWindowSize() | ||||
|  | ||||
| const dataId = ref('') | ||||
| const visible = ref(false) | ||||
| const isUpdate = computed(() => !!dataId.value) | ||||
| const title = computed(() => (isUpdate.value ? '修改存储' : '新增存储')) | ||||
| const formRef = ref<FormInstance>() | ||||
| const { storage_type_enum } = useDict('storage_type_enum') | ||||
|  | ||||
| const rules: FormInstance['rules'] = { | ||||
|   name: [{ required: true, message: '请输入名称' }], | ||||
|   code: [{ required: true, message: '请输入编码' }], | ||||
|   type: [{ required: true, message: '请选择类型' }], | ||||
|   accessKey: [{ required: true, message: '请输入访问密钥' }], | ||||
|   secretKey: [{ required: true, message: '请输入私有密钥' }], | ||||
|   endpoint: [{ required: true, message: '请输入终端节点' }], | ||||
|   bucketName: [{ required: true, message: '请输入桶名称' }], | ||||
| } | ||||
|  | ||||
| const [form, resetForm] = useResetReactive({ | ||||
|   type: 2, | ||||
|   isDefault: false, | ||||
|   sort: 999, | ||||
|   status: 1, | ||||
| }) | ||||
| /** 获取url的protocol和endpoint */ | ||||
| const stripProtocol = (url: string): { endpoint: string, protocol: string } => { | ||||
|   if (url.startsWith('http://')) { | ||||
|     return { endpoint: url.substring(7), protocol: 'http://' } | ||||
|   } else if (url.startsWith('https://')) { | ||||
|     return { endpoint: url.substring(8), protocol: 'https://' } | ||||
|   } | ||||
|   return { endpoint: url, protocol: 'http://' } | ||||
| } | ||||
| /** 按规则拼接当前默认domain */ | ||||
| const defaultDomain = computed(() => { | ||||
|   const { endpoint: initialEndpoint, bucketName, domain, type } = form | ||||
|   if (domain || type !== 1 || !initialEndpoint || !bucketName) { | ||||
|     return | ||||
|   } | ||||
|   const { endpoint, protocol } = stripProtocol(initialEndpoint) | ||||
|   return isIPv4(endpoint) ? `${protocol}${endpoint}/${bucketName}/` : `${protocol}${bucketName}.${endpoint}/` | ||||
| }) | ||||
|  | ||||
| // 重置 | ||||
| const reset = () => { | ||||
|   formRef.value?.resetFields() | ||||
|   resetForm() | ||||
| } | ||||
|  | ||||
| // 保存 | ||||
| const save = async () => { | ||||
|   try { | ||||
|     const isInvalid = await formRef.value?.validate() | ||||
|     if (isInvalid) return false | ||||
|     const data = { | ||||
|       ...form, | ||||
|       secretKey: form.type === 1 && !form.secretKey.includes('*') ? encryptByRsa(form.secretKey) : null, | ||||
|       domain: form.domain || defaultDomain.value, | ||||
|     } | ||||
|     if (isUpdate.value) { | ||||
|       await updateStorage(data, dataId.value) | ||||
|       Message.success('修改成功') | ||||
|     } else { | ||||
|       await addStorage(data) | ||||
|       Message.success('新增成功') | ||||
|     } | ||||
|     emit('save-success') | ||||
|     return true | ||||
|   } catch (error) { | ||||
|     return false | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 新增 | ||||
| const onAdd = () => { | ||||
|   reset() | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const { data } = await getStorage(id) | ||||
|   Object.assign(form, data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| defineExpose({ onAdd, onUpdate }) | ||||
| </script> | ||||
							
								
								
									
										153
									
								
								src/views/system/storage/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/views/system/storage/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| <template> | ||||
|   <div class="gi_table_page"> | ||||
|     <GiTable | ||||
|       title="" | ||||
|       row-key="id" | ||||
|       :data="dataList" | ||||
|       :columns="columns" | ||||
|       :loading="loading" | ||||
|       :scroll="{ x: '100%', y: '100%', minWidth: 1300 }" | ||||
|       :pagination="pagination" | ||||
|       :disabled-tools="['size']" | ||||
|       :disabled-column-keys="['name']" | ||||
|       @refresh="search" | ||||
|     > | ||||
|       <template #toolbar-left> | ||||
|         <a-input-search v-model="queryForm.description" placeholder="搜索名称/编码/描述" allow-clear @search="search" /> | ||||
|         <a-select | ||||
|           v-model="queryForm.status" | ||||
|           :options="DisEnableStatusList" | ||||
|           placeholder="请选择状态" | ||||
|           allow-clear | ||||
|           style="width: 150px" | ||||
|           @change="search" | ||||
|         /> | ||||
|         <a-button @click="reset"> | ||||
|           <template #icon><icon-refresh /></template> | ||||
|           <template #default>重置</template> | ||||
|         </a-button> | ||||
|       </template> | ||||
|       <template #toolbar-right> | ||||
|         <a-button v-permission="['system:storage:add']" type="primary" @click="onAdd"> | ||||
|           <template #icon><icon-plus /></template> | ||||
|           <template #default>新增</template> | ||||
|         </a-button> | ||||
|       </template> | ||||
|       <template #name="{ record }"> | ||||
|         <a-space fill> | ||||
|           <span>{{ record.name }}</span> | ||||
|           <a-tag v-if="record.isDefault" color="arcoblue" size="small" class="gi_round"> | ||||
|             <template #default>默认</template> | ||||
|           </a-tag> | ||||
|         </a-space> | ||||
|       </template> | ||||
|       <template #type="{ record }"> | ||||
|         <GiCellTag :value="record.type" :dict="storage_type_enum" /> | ||||
|       </template> | ||||
|       <template #status="{ record }"> | ||||
|         <GiCellStatus :status="record.status" /> | ||||
|       </template> | ||||
|       <template #action="{ record }"> | ||||
|         <a-space> | ||||
|           <a-link v-permission="['system:storage:update']" title="修改" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link | ||||
|             v-permission="['system:storage:delete']" | ||||
|             status="danger" | ||||
|             :disabled="record.isDefault" | ||||
|             :title="record.isDefault ? '默认存储不能删除' : '删除'" | ||||
|             @click="onDelete(record)" | ||||
|           > | ||||
|             删除 | ||||
|           </a-link> | ||||
|         </a-space> | ||||
|       </template> | ||||
|     </GiTable> | ||||
|  | ||||
|     <StorageAddDrawer ref="StorageAddDrawerRef" @save-success="search" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import StorageAddDrawer from './StorageAddDrawer.vue' | ||||
| import { type StorageQuery, type StorageResp, deleteStorage, listStorage } from '@/apis/system/storage' | ||||
| import type { TableInstanceColumns } from '@/components/GiTable/type' | ||||
| import { DisEnableStatusList } from '@/constant/common' | ||||
| import { useTable } from '@/hooks' | ||||
| import { useDict } from '@/hooks/app' | ||||
| import { isMobile } from '@/utils' | ||||
| import has from '@/utils/has' | ||||
|  | ||||
| defineOptions({ name: 'SystemStorage' }) | ||||
|  | ||||
| const { storage_type_enum } = useDict('storage_type_enum') | ||||
|  | ||||
| const queryForm = reactive<StorageQuery>({ | ||||
|   sort: ['createTime,desc'], | ||||
| }) | ||||
|  | ||||
| const { | ||||
|   tableData: dataList, | ||||
|   loading, | ||||
|   pagination, | ||||
|   search, | ||||
|   handleDelete, | ||||
| } = useTable((page) => listStorage({ ...queryForm, ...page }), { immediate: true }) | ||||
| const columns: TableInstanceColumns[] = [ | ||||
|   { | ||||
|     title: '序号', | ||||
|     width: 66, | ||||
|     align: 'center', | ||||
|     render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize), | ||||
|   }, | ||||
|   { title: '名称', dataIndex: 'name', slotName: 'name', width: 140, ellipsis: true, tooltip: true }, | ||||
|   { title: '编码', dataIndex: 'code', ellipsis: true, tooltip: true }, | ||||
|   { title: '状态', dataIndex: 'status', slotName: 'status', align: 'center' }, | ||||
|   { title: '类型', dataIndex: 'type', slotName: 'type', align: 'center', ellipsis: true, tooltip: true }, | ||||
|   { title: '访问密钥', dataIndex: 'accessKey', ellipsis: true, tooltip: true }, | ||||
|   { title: '终端节点', dataIndex: 'endpoint', ellipsis: true, tooltip: true }, | ||||
|   { title: '桶名称', dataIndex: 'bucketName', ellipsis: true, tooltip: true }, | ||||
|   { title: '域名', dataIndex: 'domain', ellipsis: true, tooltip: true }, | ||||
|   { title: '描述', dataIndex: 'description', ellipsis: true, tooltip: true }, | ||||
|   { title: '创建人', dataIndex: 'createUserString', ellipsis: true, tooltip: true, show: false }, | ||||
|   { title: '创建时间', dataIndex: 'createTime', width: 180 }, | ||||
|   { title: '修改人', dataIndex: 'updateUserString', ellipsis: true, tooltip: true, show: false }, | ||||
|   { title: '修改时间', dataIndex: 'updateTime', width: 180, show: false }, | ||||
|   { | ||||
|     title: '操作', | ||||
|     dataIndex: 'action', | ||||
|     slotName: 'action', | ||||
|     width: 130, | ||||
|     align: 'center', | ||||
|     fixed: !isMobile() ? 'right' : undefined, | ||||
|     show: has.hasPermOr(['system:storage:update', 'system:storage:delete']), | ||||
|   }, | ||||
| ] | ||||
|  | ||||
| // 重置 | ||||
| const reset = () => { | ||||
|   queryForm.description = undefined | ||||
|   queryForm.status = undefined | ||||
|   search() | ||||
| } | ||||
|  | ||||
| // 删除 | ||||
| const onDelete = (record: StorageResp) => { | ||||
|   return handleDelete(() => deleteStorage(record.id), { | ||||
|     content: `是否确定删除存储「${record.name}」?`, | ||||
|     showModal: true, | ||||
|   }) | ||||
| } | ||||
|  | ||||
| const StorageAddDrawerRef = ref<InstanceType<typeof StorageAddDrawer>>() | ||||
| // 新增 | ||||
| const onAdd = () => { | ||||
|   StorageAddDrawerRef.value?.onAdd() | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = (record: StorageResp) => { | ||||
|   StorageAddDrawerRef.value?.onUpdate(record.id) | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"></style> | ||||
							
								
								
									
										14
									
								
								src/views/system/storage/type.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/views/system/storage/type.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| export interface StorageReq { | ||||
|   name: string | ||||
|   code: string | ||||
|   type: number | ||||
|   accessKey: string | ||||
|   secretKey: string | ||||
|   endpoint: string | ||||
|   bucketName: string | ||||
|   domain: string | ||||
|   sort: number | ||||
|   description: string | ||||
|   isDefault: boolean | ||||
|   status: 1 | 2 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user