优化登录验证码锁定交互

This commit is contained in:
andy
2026-07-15 14:59:16 +07:00
parent d7b67fdcd5
commit 2a2c222c2f
3 changed files with 99 additions and 3 deletions

View File

@@ -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,
},
);
});
});

View File

@@ -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 = {},