0fca5f025e
Co-authored-by: Cursor <cursoragent@cursor.com>
101 lines
3.2 KiB
TypeScript
101 lines
3.2 KiB
TypeScript
import { FormEvent, useState } from "react";
|
||
import { changeCredentials, getUsername, setSession } from "../api/client";
|
||
|
||
export default function SettingsPage() {
|
||
const [newUsername, setNewUsername] = useState(getUsername() || "admin");
|
||
const [currentPassword, setCurrentPassword] = useState("");
|
||
const [newPassword, setNewPassword] = useState("");
|
||
const [confirmPassword, setConfirmPassword] = useState("");
|
||
const [err, setErr] = useState("");
|
||
const [ok, setOk] = useState("");
|
||
const [loading, setLoading] = useState(false);
|
||
|
||
async function onSave(e: FormEvent) {
|
||
e.preventDefault();
|
||
setErr("");
|
||
setOk("");
|
||
if (newPassword !== confirmPassword) {
|
||
setErr("两次输入的新密码不一致");
|
||
return;
|
||
}
|
||
if (newPassword.length < 4) {
|
||
setErr("新密码至少 4 位");
|
||
return;
|
||
}
|
||
setLoading(true);
|
||
try {
|
||
const res = await changeCredentials({
|
||
current_password: currentPassword,
|
||
new_username: newUsername.trim(),
|
||
new_password: newPassword,
|
||
});
|
||
setSession(res.token, res.username);
|
||
setCurrentPassword("");
|
||
setNewPassword("");
|
||
setConfirmPassword("");
|
||
setOk("用户名/密码已更新");
|
||
} catch (ex) {
|
||
setErr(ex instanceof Error ? ex.message : String(ex));
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="card" style={{ maxWidth: 560 }}>
|
||
<h2 style={{ marginTop: 0 }}>系统设置</h2>
|
||
<p style={{ color: "var(--muted)" }}>修改登录用户名与密码(写入服务器 .env,立即生效)。</p>
|
||
{err ? <div className="err">{err}</div> : null}
|
||
{ok ? <div style={{ color: "var(--up)", marginBottom: 12 }}>{ok}</div> : null}
|
||
<form onSubmit={onSave}>
|
||
<div className="field">
|
||
<label htmlFor="user">新用户名</label>
|
||
<input
|
||
id="user"
|
||
value={newUsername}
|
||
onChange={(e) => setNewUsername(e.target.value)}
|
||
autoComplete="username"
|
||
required
|
||
/>
|
||
</div>
|
||
<div className="field">
|
||
<label htmlFor="cur">当前密码</label>
|
||
<input
|
||
id="cur"
|
||
type="password"
|
||
value={currentPassword}
|
||
onChange={(e) => setCurrentPassword(e.target.value)}
|
||
autoComplete="current-password"
|
||
required
|
||
/>
|
||
</div>
|
||
<div className="field">
|
||
<label htmlFor="np">新密码</label>
|
||
<input
|
||
id="np"
|
||
type="password"
|
||
value={newPassword}
|
||
onChange={(e) => setNewPassword(e.target.value)}
|
||
autoComplete="new-password"
|
||
required
|
||
/>
|
||
</div>
|
||
<div className="field">
|
||
<label htmlFor="cp">确认新密码</label>
|
||
<input
|
||
id="cp"
|
||
type="password"
|
||
value={confirmPassword}
|
||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||
autoComplete="new-password"
|
||
required
|
||
/>
|
||
</div>
|
||
<button className="btn" type="submit" disabled={loading}>
|
||
{loading ? "保存中…" : "保存"}
|
||
</button>
|
||
</form>
|
||
</div>
|
||
);
|
||
}
|