fix: make admin SMS login form usable

This commit is contained in:
Codex
2026-07-03 23:14:08 +08:00
parent 9e5bfa0fdb
commit 7eb5ceca62
2 changed files with 96 additions and 11 deletions

View File

@@ -19,6 +19,10 @@ export default function StudentLoginPage() {
const [reason, setReason] = useState(''); const [reason, setReason] = useState('');
const [sending, setSending] = useState(false); const [sending, setSending] = useState(false);
const [verifying, setVerifying] = useState(false); const [verifying, setVerifying] = useState(false);
const phoneValue = phone.trim();
const codeValue = code.trim();
const canSendCode = runtimeReady && /^1\d{10}$/.test(phoneValue) && !sending;
const canSubmit = runtimeReady && /^1\d{10}$/.test(phoneValue) && /^\d{4,8}$/.test(codeValue || debugCode) && !verifying;
useEffect(() => { useEffect(() => {
ensureRuntimeConfigLoaded() ensureRuntimeConfigLoaded()
@@ -71,13 +75,19 @@ export default function StudentLoginPage() {
}; };
async function handleSendCode() { async function handleSendCode() {
if (!/^1\d{10}$/.test(phoneValue)) {
setError('请输入 11 位手机号');
return;
}
setSending(true); setSending(true);
setError(''); setError('');
setMessage(''); setMessage('');
try { try {
const result = await sendSmsCode(phone); await ensureTenantResolved();
const result = await sendSmsCode(phoneValue);
const nextCode = typeof result.debugCode === 'string' ? result.debugCode : ''; const nextCode = typeof result.debugCode === 'string' ? result.debugCode : '';
setDebugCode(nextCode); setDebugCode(nextCode);
setCode('');
setMessage(nextCode ? `验证码已发送,开发环境验证码 ${nextCode}` : '验证码已发送'); setMessage(nextCode ? `验证码已发送,开发环境验证码 ${nextCode}` : '验证码已发送');
} catch (nextError) { } catch (nextError) {
setError(nextError instanceof Error ? nextError.message : '验证码发送失败'); setError(nextError instanceof Error ? nextError.message : '验证码发送失败');
@@ -87,10 +97,18 @@ export default function StudentLoginPage() {
} }
async function handleLogin() { async function handleLogin() {
if (!/^1\d{10}$/.test(phoneValue)) {
setError('请输入 11 位手机号');
return;
}
if (!/^\d{4,8}$/.test(codeValue || debugCode)) {
setError('请输入短信验证码');
return;
}
setVerifying(true); setVerifying(true);
setError(''); setError('');
try { try {
await verifySmsCode(phone, code || debugCode); await verifySmsCode(phoneValue, codeValue || debugCode);
redirectAfterLogin(params.redirect || landingPath()); redirectAfterLogin(params.redirect || landingPath());
} catch (nextError) { } catch (nextError) {
setError(nextError instanceof Error ? nextError.message : '登录失败'); setError(nextError instanceof Error ? nextError.message : '登录失败');
@@ -116,21 +134,25 @@ export default function StudentLoginPage() {
{reason ? <Text className='error-text'>{reason}</Text> : null} {reason ? <Text className='error-text'>{reason}</Text> : null}
<View className='login-field'> <View className='login-field'>
<Text className='login-field-label'></Text> <Text className='login-field-label'></Text>
<Input className='login-input' type='number' maxlength={11} placeholder='请输入手机号' value={phone} onInput={event => setPhone(String(event.detail.value || ''))} /> <View className='login-input-shell'>
<Input className='login-input-native' type='number' maxlength={11} placeholder='请输入手机号' value={phone} onInput={event => setPhone(String(event.detail.value || ''))} />
</View>
</View> </View>
<View className='login-field'> <View className='login-field'>
<Text className='login-field-label'></Text> <Text className='login-field-label'></Text>
<View className='login-code-row'> <View className='login-code-row'>
<Input className='login-input login-code-input' type='number' maxlength={6} placeholder='请输入短信验证码' value={code} onInput={event => setCode(String(event.detail.value || ''))} /> <View className='login-input-shell login-code-shell'>
<Button className='login-send-button secondary-button' loading={sending} onClick={handleSendCode}> <Input className='login-input-native' type='number' maxlength={6} placeholder='请输入验证码' value={code} onInput={event => setCode(String(event.detail.value || ''))} />
</View>
<Button className='login-send-button secondary-button' disabled={!canSendCode} loading={sending} onClick={handleSendCode}>
{message ? '重发' : '发送'}
</Button> </Button>
</View> </View>
<Text className={`login-field-help ${message ? 'success' : ''}`}>{message || '收到短信后在这里填写验证码'}</Text>
</View> </View>
<Button className='primary-button' loading={verifying} onClick={handleLogin}> <Button className='primary-button' disabled={!canSubmit} loading={verifying} onClick={handleLogin}>
</Button> </Button>
{message ? <Text className='success-text'>{message}</Text> : null}
{error ? <Text className='error-text'>{error}</Text> : null} {error ? <Text className='error-text'>{error}</Text> : null}
</View> </View>
</View> </View>

View File

@@ -116,7 +116,8 @@
font-weight: 850; font-weight: 850;
} }
.login-input { .login-input,
.login-input-shell {
box-sizing: border-box; box-sizing: border-box;
display: block; display: block;
width: 100%; width: 100%;
@@ -131,6 +132,27 @@
line-height: 54px; line-height: 54px;
} }
.login-input-shell {
display: flex;
align-items: center;
padding: 0;
overflow: hidden;
}
.login-input-native {
display: block;
width: 100%;
height: 52px;
min-height: 52px;
padding: 0 16px;
border: 0;
outline: 0;
background: transparent;
color: #111827;
font-size: 17px;
line-height: 52px;
}
.login-code-row { .login-code-row {
display: grid; display: grid;
grid-template-columns: minmax(0, 1fr) 104px; grid-template-columns: minmax(0, 1fr) 104px;
@@ -143,6 +165,10 @@
min-width: 0; min-width: 0;
} }
.login-code-shell {
min-width: 0;
}
.login-send-button { .login-send-button {
width: 104px; width: 104px;
min-width: 104px; min-width: 104px;
@@ -151,7 +177,8 @@
white-space: nowrap; white-space: nowrap;
} }
.login-card .login-input { .login-card .login-input,
.login-card .login-input-shell {
display: block; display: block;
width: 100%; width: 100%;
height: 54px; height: 54px;
@@ -165,7 +192,24 @@
line-height: 54px; line-height: 54px;
} }
.login-card .login-code-row .login-code-input { .login-card .login-input-shell {
display: flex;
align-items: center;
padding: 0;
}
.login-card .login-input-native {
width: 100%;
height: 52px;
min-height: 52px;
padding: 0 16px;
color: #111827;
font-size: 16px;
line-height: 52px;
}
.login-card .login-code-row .login-code-input,
.login-card .login-code-row .login-code-shell {
width: 100%; width: 100%;
} }
@@ -181,6 +225,25 @@
line-height: 54px; line-height: 54px;
} }
.login-card .primary-button[disabled],
.login-card .secondary-button[disabled],
.login-card .login-send-button[disabled] {
opacity: 0.55;
}
.login-field-help {
display: block;
min-height: 20px;
color: #64748b;
font-size: 14px;
line-height: 1.45;
}
.login-field-help.success {
color: #0f766e;
font-weight: 750;
}
.login-page-admin { .login-page-admin {
background: background:
linear-gradient(to right, rgba(15, 23, 42, 0.035) 1px, transparent 1px), linear-gradient(to right, rgba(15, 23, 42, 0.035) 1px, transparent 1px),