feat(web): call the login name the account ID and list sign-up requirements

This commit is contained in:
justme committed 2026-10-16 17:54:31 +08:00
1 parent 700be2ebae
commit 79bfd560cb
6 files changed
+49 -10

No files matched your search

@@ -150,6 +150,34 @@
grid-column: 1 / -1;
}
.rules {
display: grid;
gap: 2px;
margin: 0;
padding: 0;
list-style: none;
color: var(--text-dim);
font-size: 12px;
line-height: 1.6;
}
.rules li {
display: flex;
gap: var(--space-2);
}
.rules li[data-met='true'] {
color: var(--text);
}
.srOnly {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.fieldError {
color: var(--danger);
font-size: 12px;
+15 -4
View File
@@ -43,6 +43,14 @@ export function AccountPanel({ variant = 'default' }: { variant?: 'default' | 'a
}, [account]);
const creating = register && !unlockUsername;
const mismatch = creating && confirm !== '' && password !== confirm;
// 与服务端一致:ID 3–32 位,仅字母、数字、_、-
const rules = [
{ text: 'ID 为 3–32 个字符', met: username.length >= 3 && username.length <= 32 },
{ text: 'ID 只能包含字母、数字、下划线 _ 和连字符 -', met: username !== '' && /^[A-Za-z0-9_-]+$/.test(username) },
{ text: '密码至少 12 个字符', met: password.length >= 12 },
{ text: '两次输入的密码一致', met: confirm !== '' && password === confirm },
];
const unmet = creating && rules.some((rule) => !rule.met);
async function clearSaved() {
edited.current = true;
try { await clearSavedLogin(); setSaved(false); setRemember(false); setUsername(''); setPassword(''); setMessage(account ? '本机保存已清除,当前页面仍可使用;刷新后需要重新登录。' : '已清除本机保存的账号和密码。'); }
@@ -81,14 +89,17 @@ export function AccountPanel({ variant = 'default' }: { variant?: 'default' | 'a
<button type="button" aria-pressed={register} onClick={() => { edited.current = true; setRegister(true); setMessage(''); }}>创建账号</button>
</div> : null}
{unlockUsername ? <p className={styles.note}>已用Passkey确认账号,请输入账号密码解锁资源。</p> : null}
<label>用户名<input name="username" autoComplete="username" minLength={3} maxLength={32} value={unlockUsername ?? username} disabled={Boolean(unlockUsername)} onChange={(event) => { edited.current = true; setUsername(event.target.value); }} required /></label>
<label>账号 ID<input name="username" autoComplete="username" minLength={3} maxLength={32} value={unlockUsername ?? username} disabled={Boolean(unlockUsername)} onChange={(event) => { edited.current = true; setUsername(event.target.value); }} required /></label>
<label>密码<input name="password" autoComplete={register ? 'new-password' : 'current-password'} type="password" minLength={register ? 12 : undefined} value={password} onChange={(event) => { edited.current = true; setPassword(event.target.value); }} required /></label>
{creating ? <label>确认密码<input name="confirm-password" autoComplete="new-password" type="password" minLength={12} value={confirm} aria-invalid={mismatch} onChange={(event) => { edited.current = true; setConfirm(event.target.value); }} required />
{mismatch ? <span className={styles.fieldError} role="alert">两次输入的密码不一致</span> : null}</label> : null}
{creating ? <p className={styles.note}>密码至少 12 个字符。密码用于在本机加密你的资源保险库,<strong>忘记后无法找回</strong>,请妥善保管。</p> : null}
</label> : null}
{creating ? <ul className={styles.rules} aria-label="注册要求">
{rules.map((rule) => <li key={rule.text} data-met={rule.met}><span aria-hidden="true">{rule.met ? '✓' : '○'}</span>{rule.text}<span className={styles.srOnly}>{rule.met ? ',已满足' : ',未满足'}</span></li>)}
</ul> : null}
{creating ? <p className={styles.note}>账号 ID 是登录用的唯一标识,不是显示名称;显示名称可在登录后的个人资料里设置。密码用于在本机加密你的资源保险库,<strong>忘记后无法找回</strong>,请妥善保管。</p> : null}
<label className={styles.remember}><input type="checkbox" checked={remember} onChange={(event) => setRemember(event.target.checked)} />记住账号和密码(本机)</label>
<div className={styles.actions}>
<Button type="submit" variant="primary" disabled={busy || !channelsReady || !sharesReady || mismatch}>{busy ? '处理中…' : !channelsReady || !sharesReady ? '正在读取本机资源…' : unlockUsername ? '解锁资源' : register ? '创建账号' : '登录并同步'}</Button>
<Button type="submit" variant="primary" disabled={busy || !channelsReady || !sharesReady || unmet}>{busy ? '处理中…' : !channelsReady || !sharesReady ? '正在读取本机资源…' : unlockUsername ? '解锁资源' : register ? '创建账号' : '登录并同步'}</Button>
{variant !== 'auth' ? <Button variant="ghost" onClick={() => { edited.current = true; setRegister((value) => !value); setMessage(''); }}>{register ? '已有账号' : '创建账号'}</Button> : null}
{saved ? <Button variant="ghost" disabled={busy} onClick={() => { void clearSaved(); }}>清除本机保存</Button> : null}
{!register && !unlockUsername ? <Button variant="ghost" disabled={busy || !username || !passkeysAvailable()} onClick={() => {