diff --git a/src/api/loginSms.test.ts b/src/api/loginSms.test.ts
index 2bd4750..5358e93 100644
--- a/src/api/loginSms.test.ts
+++ b/src/api/loginSms.test.ts
@@ -3,6 +3,8 @@ import { describe, it } from "node:test";
import {
buildCaptchaImageUrl,
buildSendMobileCodeRequest,
+ isImageCaptchaLocked,
+ resolveImageCaptchaInteractionState,
resolveLoginErrorMessage,
} from "./loginSms.ts";
@@ -63,4 +65,46 @@ describe("login sms api helpers", () => {
"网络错误",
);
});
+
+ it("locks image captcha while sms countdown is active", () => {
+ assert.equal(isImageCaptchaLocked(60), true);
+ assert.equal(isImageCaptchaLocked(1), true);
+ assert.equal(isImageCaptchaLocked(0), false);
+ });
+
+ it("resolves captcha field and refresh button interaction state", () => {
+ assert.deepEqual(
+ resolveImageCaptchaInteractionState({
+ smsSending: false,
+ smsCountdown: 60,
+ }),
+ {
+ disabled: true,
+ clearable: false,
+ refreshDisabled: true,
+ },
+ );
+ assert.deepEqual(
+ resolveImageCaptchaInteractionState({
+ smsSending: false,
+ smsCountdown: 0,
+ }),
+ {
+ disabled: false,
+ clearable: true,
+ refreshDisabled: false,
+ },
+ );
+ assert.deepEqual(
+ resolveImageCaptchaInteractionState({
+ smsSending: true,
+ smsCountdown: 0,
+ }),
+ {
+ disabled: true,
+ clearable: false,
+ refreshDisabled: true,
+ },
+ );
+ });
});
diff --git a/src/api/loginSms.ts b/src/api/loginSms.ts
index a4a639e..d37e7b2 100644
--- a/src/api/loginSms.ts
+++ b/src/api/loginSms.ts
@@ -10,6 +10,17 @@ export interface CaptchaImageUrlOptions {
cacheBust?: number | string;
}
+export interface ImageCaptchaInteractionParams {
+ smsSending: boolean;
+ smsCountdown: number;
+}
+
+export interface ImageCaptchaInteractionState {
+ disabled: boolean;
+ clearable: boolean;
+ refreshDisabled: boolean;
+}
+
function trimEndSlash(value: string): string {
return value.replace(/\/+$/, "");
}
@@ -39,6 +50,23 @@ export function createCaptchaRandomStr(): string {
return `${Date.now()}${Math.random().toString(36).slice(2, 10)}`;
}
+export function isImageCaptchaLocked(smsCountdown: number): boolean {
+ return smsCountdown > 0;
+}
+
+export function resolveImageCaptchaInteractionState({
+ smsSending,
+ smsCountdown,
+}: ImageCaptchaInteractionParams): ImageCaptchaInteractionState {
+ const disabled = smsSending || isImageCaptchaLocked(smsCountdown);
+
+ return {
+ disabled,
+ clearable: !disabled,
+ refreshDisabled: disabled,
+ };
+}
+
export function buildCaptchaImageUrl(
randomStr: string,
options: CaptchaImageUrlOptions = {},
diff --git a/src/pages/login/index.vue b/src/pages/login/index.vue
index 78fb13c..a0a4a7b 100644
--- a/src/pages/login/index.vue
+++ b/src/pages/login/index.vue
@@ -16,11 +16,14 @@
-