mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-07 23:31:10 +08:00
【调整】更新API表单输入,新增密码类型和显示密码功能
【调整】登录页表单校验调整 【调整】新增阿里云dcdn部署类型
This commit is contained in:
@@ -11370,5 +11370,39 @@
|
||||
"arDZ": "صيغة اسم المجال خاطئة"
|
||||
},
|
||||
"timestamp": "2025-06-21T01:26:22.154Z"
|
||||
},
|
||||
"阿里云DCDN": {
|
||||
"text": "阿里云DCDN",
|
||||
"key": "t_0_1752230148946",
|
||||
"translations": {
|
||||
"zhCN": "阿里云DCDN",
|
||||
"zhTW": "阿里雲DCDN",
|
||||
"enUS": "Alibaba Cloud DCDN",
|
||||
"jaJP": "アリババクラウドDCDN",
|
||||
"koKR": "알리바바 클라우드 DCDN",
|
||||
"ruRU": "Alibaba Cloud DCDN",
|
||||
"ptBR": "Alibaba Cloud DCDN",
|
||||
"frFR": "Alibaba Cloud DCDN",
|
||||
"esAR": "Alibaba Cloud DCDN",
|
||||
"arDZ": "Alibaba Cloud DCDN"
|
||||
},
|
||||
"timestamp": "2025-07-11T10:35:48.946Z"
|
||||
},
|
||||
"阿里云ESA": {
|
||||
"text": "阿里云ESA",
|
||||
"key": "t_1_1752230146379",
|
||||
"translations": {
|
||||
"zhCN": "阿里云ESA",
|
||||
"zhTW": "阿里雲ESA",
|
||||
"enUS": "Aliyun ESA",
|
||||
"jaJP": "アリババクラウドESA",
|
||||
"koKR": "알리바바 클라우드 ESA",
|
||||
"ruRU": "Алибаба Клауд ESA",
|
||||
"ptBR": "Aliyun ESA",
|
||||
"frFR": "Aliyun ESA",
|
||||
"esAR": "Aliyun ESA",
|
||||
"arDZ": "علي بابا كلاود ESA"
|
||||
},
|
||||
"timestamp": "2025-07-11T10:35:48.948Z"
|
||||
}
|
||||
}
|
||||
@@ -244,6 +244,7 @@ export interface DeployConfig<
|
||||
| 'tencentcloud-waf'
|
||||
| 'tencentcloud-teo'
|
||||
| 'aliyun-cdn'
|
||||
| 'aliyun-dcdn'
|
||||
| 'aliyun-oss'
|
||||
| 'aliyun-waf'
|
||||
| 'aliyun-esa'
|
||||
|
||||
@@ -85,9 +85,10 @@ export const ApiProjectConfig: Record<string, ApiProjectType> = {
|
||||
type: ['host', 'dns'],
|
||||
hostRelated: {
|
||||
cdn: { name: $t('t_16_1745735766712') },
|
||||
dcdn: { name: $t('t_0_1752230148946') },
|
||||
oss: { name: $t('t_2_1746697487164') },
|
||||
waf: { name: $t('t_10_1744958860078') },
|
||||
esa: { name: '阿里云ESA' },
|
||||
esa: { name: $t('t_1_1752230146379') },
|
||||
},
|
||||
sort: 6,
|
||||
},
|
||||
@@ -255,5 +256,3 @@ export const ApiProjectConfig: Record<string, ApiProjectType> = {
|
||||
sort: 29,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"t_0_1752230148946": "Alibaba Cloud DCDN",
|
||||
"t_1_1752230146379": "علي بابا كلاود ESA",
|
||||
"t_0_1744098811152": "تحذير: لقد دخلتم منطقة غير معروفة، الصفحة التي تحاول زيارتها غير موجودة، يرجى الضغط على الزر للعودة إلى الصفحة الرئيسية.",
|
||||
"t_1_1744098801860": "رجوع إلى الصفحة الرئيسية",
|
||||
"t_2_1744098804908": "نصيحة أمنية: إذا كنت تعتقد أن هذا خطأ، يرجى الاتصال بالمدير على الفور",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"t_0_1752230148946": "Alibaba Cloud DCDN",
|
||||
"t_1_1752230146379": "Aliyun ESA",
|
||||
"t_0_1744098811152": "Warning: You have entered an unknown area, the page you are visiting does not exist, please click the button to return to the homepage.",
|
||||
"t_1_1744098801860": "Return Home",
|
||||
"t_2_1744098804908": "Safety Tip: If you think this is an error, please contact the administrator immediately",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"t_0_1752230148946": "Alibaba Cloud DCDN",
|
||||
"t_1_1752230146379": "Aliyun ESA",
|
||||
"t_0_1744098811152": "Advertencia: Ha ingresado a una zona desconocida, la página que intenta visitar no existe, por favor, haga clic en el botón para regresar a la página de inicio.",
|
||||
"t_1_1744098801860": "Volver al inicio",
|
||||
"t_2_1744098804908": "Consejo de seguridad: Si piensa que es un error, póngase en contacto con el administrador inmediatamente",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"t_0_1752230148946": "Alibaba Cloud DCDN",
|
||||
"t_1_1752230146379": "Aliyun ESA",
|
||||
"t_0_1744098811152": "Avertissement : Vous avez entré dans une zone inconnue, la page que vous visitez n'existe pas, veuillez cliquer sur le bouton pour revenir à la page d'accueil.",
|
||||
"t_1_1744098801860": "Retour à l'accueil",
|
||||
"t_2_1744098804908": "Avis de sécurité : Si vous pensez que c'est une erreur, veuillez contacter l'administrateur immédiatement",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"t_0_1752230148946": "アリババクラウドDCDN",
|
||||
"t_1_1752230146379": "アリババクラウドESA",
|
||||
"t_0_1744098811152": "警告:未知のエリアに進入しました。アクセスしようとしたページは存在しません。ボタンをクリックしてホームページに戻ってください。",
|
||||
"t_1_1744098801860": "ホームに戻る",
|
||||
"t_2_1744098804908": "安全注意:これが誤りだと思われる場合は、すぐに管理者に連絡してください",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"t_0_1752230148946": "알리바바 클라우드 DCDN",
|
||||
"t_1_1752230146379": "알리바바 클라우드 ESA",
|
||||
"t_0_1744098811152": "경고: 알 수 없는 영역에 진입했습니다. 방문하려는 페이지가 존재하지 않습니다. 버튼을 클릭하여 홈페이지로 돌아가세요。",
|
||||
"t_1_1744098801860": "홈으로 돌아가기",
|
||||
"t_2_1744098804908": "안전 유의사항: 이가 오류라면 즉시 관리자에게 연락하십시오",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"t_0_1752230148946": "Alibaba Cloud DCDN",
|
||||
"t_1_1752230146379": "Aliyun ESA",
|
||||
"t_0_1744098811152": "Aviso: Você entrou em uma área desconhecida, a página que você está visitando não existe, por favor, clique no botão para voltar para a página inicial.",
|
||||
"t_1_1744098801860": "Voltar para a homepage",
|
||||
"t_2_1744098804908": "Dica de Segurança: Se você acha que isso é um erro, entre em contato com o administrador imediatamente",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"t_0_1752230148946": "Alibaba Cloud DCDN",
|
||||
"t_1_1752230146379": "Алибаба Клауд ESA",
|
||||
"t_0_1744098811152": "Предупреждение: Вы вошли в неизвестную зону, посещаемая страница не существует, пожалуйста, нажмите кнопку, чтобы вернуться на главную страницу.",
|
||||
"t_1_1744098801860": "Вернуться на главную",
|
||||
"t_2_1744098804908": "Совет по безопасности: Если вы считаете, что это ошибка, немедленно свяжитесь с администратором",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"t_0_1752230148946": "阿里云DCDN",
|
||||
"t_1_1752230146379": "阿里云ESA",
|
||||
"t_0_1744098811152": "警告:您已进入未知区域,所访问的页面不存在,请点击按钮返回首页。",
|
||||
"t_1_1744098801860": "返回首页",
|
||||
"t_2_1744098804908": "安全提示:如果您认为这是个错误,请立即联系管理员",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"t_0_1752230148946": "阿里雲DCDN",
|
||||
"t_1_1752230146379": "阿里雲ESA",
|
||||
"t_0_1744098811152": "警告:您已進入未知區域,所訪問的頁面不存在,請點擊按鈕返回首頁。",
|
||||
"t_1_1744098801860": "返回首頁",
|
||||
"t_2_1744098804908": "安全提示:如果您認為這是個錯誤,請立即聯繫管理員",
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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, // 控制器封装的登录逻辑
|
||||
|
||||
@@ -96,6 +96,10 @@ export default function useTable<T = Record<string, any>, Z extends Record<strin
|
||||
const { page, pageSize } = alias
|
||||
const pageSizeOptionsRef = ref([10, 20, 50, 100, 200]) // 分页选项
|
||||
|
||||
// 防重复请求相关状态
|
||||
const lastDirectRequestTime = ref(0) // 记录最后一次直接请求的时间
|
||||
const REQUEST_DEBOUNCE_DELAY = 100 // 防抖延迟时间(毫秒)
|
||||
|
||||
// 初始化分页参数
|
||||
if ((param.value as Record<string, unknown>)[page]) {
|
||||
;(param.value as Record<string, unknown>)[page] = 1 // 当前页码
|
||||
@@ -111,6 +115,8 @@ export default function useTable<T = Record<string, any>, Z extends Record<strin
|
||||
* @param currentPage 当前页码
|
||||
*/
|
||||
const handlePageChange = (currentPage: number) => {
|
||||
// 记录直接请求时间,防止 watch 重复触发
|
||||
lastDirectRequestTime.value = Date.now()
|
||||
;(param.value as Record<string, unknown>)[page] = currentPage
|
||||
fetchData()
|
||||
}
|
||||
@@ -120,6 +126,8 @@ export default function useTable<T = Record<string, any>, Z extends Record<strin
|
||||
* @param size 每页条数
|
||||
*/
|
||||
const handlePageSizeChange = (size: number) => {
|
||||
// 记录直接请求时间,防止 watch 重复触发
|
||||
lastDirectRequestTime.value = Date.now()
|
||||
// 保存到本地存储
|
||||
savePageSizeToStorage(storage, size)
|
||||
;(param.value as Record<string, unknown>)[page] = 1 // 重置页码为1
|
||||
@@ -211,7 +219,20 @@ export default function useTable<T = Record<string, any>, Z extends Record<strin
|
||||
if (Array.isArray(watchValue)) {
|
||||
// 只监听指定的字段
|
||||
const source = computed(() => watchValue.map((key) => param.value[key]))
|
||||
watch(source, (value) => fetchData(), { deep: true })
|
||||
watch(
|
||||
source,
|
||||
() => {
|
||||
// 检查是否刚刚有直接请求,如果是则跳过此次 watch 触发的请求
|
||||
const timeSinceLastDirectRequest = Date.now() - lastDirectRequestTime.value
|
||||
if (timeSinceLastDirectRequest < REQUEST_DEBOUNCE_DELAY) {
|
||||
console.log('跳过 watch 触发的重复请求,距离上次直接请求:', timeSinceLastDirectRequest, 'ms')
|
||||
return
|
||||
}
|
||||
console.log('watch 触发请求,距离上次直接请求:', timeSinceLastDirectRequest, 'ms')
|
||||
fetchData()
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
Reference in New Issue
Block a user