【调整】更新API表单输入,新增密码类型和显示密码功能

【调整】登录页表单校验调整
【调整】新增阿里云dcdn部署类型
This commit is contained in:
chenzhihua
2025-07-14 10:26:51 +08:00
parent 8bdf9d6b46
commit 2a50e9c4ad
65 changed files with 382 additions and 50 deletions

View File

@@ -618,6 +618,8 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
]),
(param.value.config as SshAccessConfig)?.mode === 'password'
? useFormInput($t('t_48_1745289355714'), 'config.password', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
})
: useFormTextarea($t('t_1_1746667588689'), 'config.key', {
@@ -655,6 +657,8 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
allowInput: noSideSpace,
}),
useFormInput($t('t_55_1745289355715'), 'config.api_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
useFormSwitch(
@@ -676,6 +680,8 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
param.value.type === 'safeline' ? $t('t_1_1747617105179') : $t('t_55_1745289355715'),
param.value.type === 'safeline' ? 'config.api_token' : 'config.api_key',
{
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
},
),
@@ -690,13 +696,21 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
case 'aliyun':
items.push(
useFormInput('AccessKeyId', 'config.access_key_id', { allowInput: noSideSpace }),
useFormInput('AccessKeySecret', 'config.access_key_secret', { allowInput: noSideSpace }),
useFormInput('AccessKeySecret', 'config.access_key_secret', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'tencentcloud':
items.push(
useFormInput('SecretId', 'config.secret_id', { allowInput: noSideSpace }),
useFormInput('SecretKey', 'config.secret_key', { allowInput: noSideSpace }),
useFormInput('SecretKey', 'config.secret_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'huaweicloud':
@@ -705,13 +719,21 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
case 'doge':
items.push(
useFormInput('AccessKey', 'config.access_key', { allowInput: noSideSpace }),
useFormInput('SecretKey', 'config.secret_key', { allowInput: noSideSpace }),
useFormInput('SecretKey', 'config.secret_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'cloudflare':
items.push(
useFormInput('邮箱', 'config.email', { allowInput: noSideSpace }, { showRequireMark: false }),
useFormInput('APIKey', 'config.api_key', { allowInput: noSideSpace }),
useFormInput('APIKey', 'config.api_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
useFormCustom(() => {
return (
<NAlert type="error" class="mt-[1.2rem] whitespace-nowrap" showIcon={false}>
@@ -724,76 +746,144 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
case 'westcn':
items.push(
useFormInput('Username', 'config.username', { allowInput: noSideSpace }),
useFormInput('Password', 'config.password', { allowInput: noSideSpace }),
useFormInput('Password', 'config.password', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'godaddy':
items.push(
useFormInput('API Key', 'config.api_key', { allowInput: noSideSpace }),
useFormInput('API Secret', 'config.api_secret', { allowInput: noSideSpace }),
useFormInput('API Key', 'config.api_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
useFormInput('API Secret', 'config.api_secret', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'qiniu':
items.push(
useFormInput('AccessKey', 'config.access_key', { allowInput: noSideSpace }),
useFormInput('AccessSecret', 'config.access_secret', { allowInput: noSideSpace }),
useFormInput('AccessSecret', 'config.access_secret', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'namecheap':
items.push(
useFormInput('API User', 'config.api_user', { allowInput: noSideSpace }),
useFormInput('API Key', 'config.api_key', { allowInput: noSideSpace }),
useFormInput('API Key', 'config.api_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'ns1':
items.push(useFormInput('API Key', 'config.api_key', { allowInput: noSideSpace }))
items.push(
useFormInput('API Key', 'config.api_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'cloudns':
items.push(
useFormInput('Auth ID', 'config.auth_id', { allowInput: noSideSpace }),
useFormInput('Auth Password', 'config.auth_password', { allowInput: noSideSpace }),
useFormInput('Auth Password', 'config.auth_password', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'aws':
items.push(
useFormInput('Access Key ID', 'config.access_key_id', { allowInput: noSideSpace }),
useFormInput('Secret Access Key', 'config.secret_access_key', { allowInput: noSideSpace }),
useFormInput('Secret Access Key', 'config.secret_access_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'azure':
items.push(
useFormInput('Tenant ID', 'config.tenant_id', { allowInput: noSideSpace }),
useFormInput('Client ID', 'config.client_id', { allowInput: noSideSpace }),
useFormInput('Client Secret', 'config.client_secret', { allowInput: noSideSpace }),
useFormInput('Client Secret', 'config.client_secret', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
useFormInput('Environment', 'config.environment', { allowInput: noSideSpace, placeholder: 'public' }),
)
break
case 'namesilo':
items.push(useFormInput('API Key', 'config.api_key', { allowInput: noSideSpace }))
items.push(
useFormInput('API Key', 'config.api_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'namedotcom':
items.push(
useFormInput('Username', 'config.username', { allowInput: noSideSpace }),
useFormInput('API Token', 'config.api_token', { allowInput: noSideSpace }),
useFormInput('API Token', 'config.api_token', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'bunny':
items.push(useFormInput('API Key', 'config.api_key', { allowInput: noSideSpace }))
items.push(
useFormInput('API Key', 'config.api_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'gcore':
items.push(useFormInput('API Token', 'config.api_token', { allowInput: noSideSpace }))
items.push(
useFormInput('API Token', 'config.api_token', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'jdcloud':
items.push(
useFormInput('Access Key ID', 'config.access_key_id', { allowInput: noSideSpace }),
useFormInput('Secret Access Key', 'config.secret_access_key', { allowInput: noSideSpace }),
useFormInput('Secret Access Key', 'config.secret_access_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'lecdn':
items.push(
useFormInput('URL', 'config.url', { allowInput: noSideSpace }),
useFormInput('Username', 'config.username', { allowInput: noSideSpace }),
useFormInput('Password', 'config.password', { allowInput: noSideSpace }),
useFormInput('Password', 'config.password', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
useFormSwitch(
$t('t_3_1746667592270'),
'config.ignore_ssl',
@@ -804,8 +894,16 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
break
case 'constellix':
items.push(
useFormInput('API Key', 'config.api_key', { allowInput: noSideSpace }),
useFormInput('Secret Key', 'config.secret_key', { allowInput: noSideSpace }),
useFormInput('API Key', 'config.api_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
useFormInput('Secret Key', 'config.secret_key', {
type: 'password',
showPasswordOn: 'click',
allowInput: noSideSpace,
}),
)
break
case 'plugin':

View File

@@ -205,6 +205,7 @@ export default defineComponent({
case 'tencentcloud-waf':
case 'tencentcloud-teo':
case 'aliyun-cdn':
case 'aliyun-dcdn':
case 'baidu-cdn':
case 'qiniu-cdn':
case 'qiniu-oss':

View File

@@ -12,6 +12,7 @@ import {
} from '@baota/naive-ui/hooks'
import { useError } from '@baota/hooks/error'
import { $t } from '@locales/index'
import { getDaysDiff } from '@baota/utils/date'
import { useStore } from './useStore'
@@ -22,6 +23,41 @@ const { useFormTextarea } = useFormHooks()
const { fetchCertList, downloadExistingCert, deleteExistingCert, uploadNewCert, uploadForm, resetUploadForm } =
useStore()
const { confirm } = useModalHooks()
/**
* 计算证书剩余天数
* @param cert 证书项
* @returns 剩余天数,如果无法计算则返回 null
*/
const calculateRemainingDays = (cert: CertItem): number | null => {
// 首先尝试使用后端提供的 end_day 字段
const endDay = Number(cert.end_day)
if (!isNaN(endDay) && endDay !== 0) {
return endDay
}
// 如果 end_day 无效,则根据 end_time 计算
if (cert.end_time) {
try {
const endTime = new Date(cert.end_time)
const currentTime = new Date()
// 检查日期是否有效
if (isNaN(endTime.getTime())) {
return null
}
// 计算剩余天数
const endDay = getDaysDiff(currentTime, endTime)
return endDay
} catch (error) {
console.warn('计算证书剩余天数失败:', error)
return null
}
}
return null
}
/**
* useController
* @description 证书管理业务逻辑控制器
@@ -60,13 +96,27 @@ export const useController = () => {
key: 'end_day',
width: 100,
render: (row: CertItem) => {
const endDay = Number(row.end_day)
const endDay = calculateRemainingDays(row)
// 如果无法计算剩余天数,显示获取失败
if (endDay === null) {
return (
<NTag type="error" size="small">
</NTag>
)
}
// 根据剩余天数确定显示样式和文本
const config = [
[endDay <= 0, 'error', $t('t_0_1746001199409')],
[endDay < 30, 'warning', $t('t_1_1745999036289', { days: row.end_day })],
[endDay > 30, 'success', $t('t_0_1745999035681', { days: row.end_day })],
[endDay < 30, 'warning', $t('t_1_1745999036289', { days: endDay })],
[endDay >= 30, 'success', $t('t_0_1745999035681', { days: endDay })],
] as [boolean, 'success' | 'error' | 'warning' | 'default' | 'info' | 'primary', string][]
const [_, type, text] = config.find((item) => item[0]) ?? ['default', 'error', '获取失败']
const matchedConfig = config.find((item) => item[0])
const [, type, text] = matchedConfig ?? ['default', 'error', '获取失败']
return (
<NTag type={type} size="small">
{text}
@@ -116,7 +166,13 @@ export const useController = () => {
* - 空字符串:其他情况。
*/
const getRowClassName = (row: CertItem): string => {
const endDay = Number(row.end_day)
const endDay = calculateRemainingDays(row)
// 如果无法计算剩余天数,不应用特殊样式
if (endDay === null) {
return ''
}
if (endDay <= 0) {
return 'bg-red-500/10' // Tailwind class for light red background
}

View File

@@ -15,7 +15,7 @@ import styles from './index.module.css'
export default defineComponent({
name: 'LoginView',
setup() {
const { loading, error, rememberMe, handleSubmit, handleKeyup, loginData, handleGetCode, codeImg, mustCode } =
const { loading, error, rememberMe, handleSubmit, handleKeyup, loginData, handleGetCode, codeImg, mustCode, formRef } =
useController()
const { isDark } = useTheme()
const cssVar = useThemeCssVar(['textColor2', 'actionColor', 'errorColor', 'primaryColor', 'primaryColorSuppl'])
@@ -39,7 +39,7 @@ export default defineComponent({
<div class={styles.rightSection}>
<div class={styles.formContainer}>
<h1 class={styles.title}>{$t('t_2_1744164839713')}</h1>
<NForm onSubmit={handleSubmit} class={styles.formWrapper}>
<NForm ref={formRef} model={loginData.value} onSubmit={handleSubmit} class={styles.formWrapper}>
<div class={styles.formContent}>
<div class={styles.formInputs}>
<NFormItem
@@ -84,7 +84,11 @@ export default defineComponent({
<NFormItem
show-label={false}
path="code"
rule={{ required: true, message: $t('t_25_1745289355721'), trigger: ['input', 'blur'] }}
rule={{
required: mustCode.value,
message: $t('t_25_1745289355721'),
trigger: ['input', 'blur']
}}
>
<NInput
onKeyup={handleKeyup}

View File

@@ -1,12 +1,12 @@
// External Libraries
import md5 from 'crypto-js/md5'
import type { FormInst } from 'naive-ui'
// Type Imports
import type { LoginParams } from '@/types/login'
// Absolute Internal Imports
import { useError } from '@baota/hooks/error'
import { $t } from '@locales/index'
// Relative Internal Imports
import { useStore } from './useStore'
@@ -51,6 +51,7 @@ const setRememberData = (username: string, password: string): void => {
*/
interface LoginControllerExposes extends ReturnType<typeof useStore> {
// 继承自 useStore 的返回类型
formRef: Ref<FormInst | null>
handleSubmit: (event: Event) => Promise<void>
handleKeyup: (event: KeyboardEvent) => void
handleLogin: (params: LoginParams) => Promise<void> // 覆盖 store 中的 handleLogin
@@ -69,25 +70,14 @@ export const useController = (): LoginControllerExposes => {
// 从 store 中解构需要的状态和方法
const { error, loginData, handleLogin: storeHandleLogin, rememberMe, checkMustCode, mustCode, handleGetCode } = store
// 表单引用
const formRef = ref<FormInst | null>(null)
/**
* @description 处理登录业务逻辑,包括表单验证和密码加密
* @param params - 登录参数 (用户名、密码等)
*/
const handleLoginBusiness = async (params: LoginParams): Promise<void> => {
// 表单验证
if (!params.username.trim()) {
error.value = $t('t_3_1744164839524') // 请输入用户名
return
}
if (!params.password.trim()) {
error.value = $t('t_4_1744164840458') // 请输入密码
return
}
if (mustCode.value && !params.code?.trim()) {
error.value = $t('t_25_1745289355721') // 请输入验证码
return
}
try {
const encryptedPassword = encryptPassword(params.password)
await storeHandleLogin({ ...params, password: encryptedPassword }) // 调用 store 中的登录方法
@@ -117,7 +107,18 @@ export const useController = (): LoginControllerExposes => {
*/
const handleSubmit = async (event: Event): Promise<void> => {
event.preventDefault()
await handleLoginBusiness(loginData.value)
// 使用 NForm 的校验机制
if (!formRef.value) return
try {
await formRef.value.validate()
// 校验通过,执行登录逻辑
await handleLoginBusiness(loginData.value)
} catch (validationErrors) {
// 校验失败NForm 会自动显示错误信息
console.log('表单校验失败:', validationErrors)
}
}
/**
@@ -163,6 +164,7 @@ export const useController = (): LoginControllerExposes => {
// ==================== 返回值 ====================
return {
...store, // 暴露 store 中的所有属性和方法
formRef,
handleSubmit,
handleKeyup,
handleLogin: handleLoginBusiness, // 控制器封装的登录逻辑