Remove API URL UI; add username/password change in settings.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-24 16:46:53 +08:00
parent e24b4f297f
commit 0fca5f025e
6 changed files with 199 additions and 52 deletions
+3 -22
View File
@@ -1,26 +1,19 @@
import { FormEvent, useMemo, useState } from "react";
import { FormEvent, useState } from "react";
import { useNavigate } from "react-router-dom";
import { getApiBase, login, setApiBase, setSession } from "../api/client";
import { login, setSession } from "../api/client";
export default function LoginPage() {
const nav = useNavigate();
const [apiBase, setApi] = useState(getApiBase());
const [username, setUsername] = useState("admin");
const [password, setPassword] = useState("");
const [err, setErr] = useState("");
const [loading, setLoading] = useState(false);
const hint = useMemo(
() => "默认同域 API。测试环境反代:https://dc.hyf2.cc",
[],
);
async function onSubmit(e: FormEvent) {
e.preventDefault();
setErr("");
setLoading(true);
try {
setApiBase(apiBase);
const res = await login(username.trim(), password);
setSession(res.token, res.username);
nav("/plan", { replace: true });
@@ -35,19 +28,8 @@ export default function LoginPage() {
<div className="login-wrap">
<form className="login-box" onSubmit={onSubmit}>
<h1>eth_hedge_sim</h1>
<p> · API </p>
<p></p>
{err ? <div className="err">{err}</div> : null}
<div className="field">
<label htmlFor="api">API </label>
<input
id="api"
className="mono"
value={apiBase}
onChange={(e) => setApi(e.target.value)}
placeholder="https://dc.hyf2.cc"
autoComplete="url"
/>
</div>
<div className="field">
<label htmlFor="user"></label>
<input
@@ -72,7 +54,6 @@ export default function LoginPage() {
<button className="btn block" type="submit" disabled={loading}>
{loading ? "登录中…" : "登录"}
</button>
<div className="hint">{hint}</div>
</form>
</div>
);
+80 -18
View File
@@ -1,37 +1,99 @@
import { FormEvent, useState } from "react";
import { getApiBase, setApiBase } from "../api/client";
import { changeCredentials, getUsername, setSession } from "../api/client";
export default function SettingsPage() {
const [apiBase, setApi] = useState(getApiBase());
const [saved, setSaved] = useState(false);
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);
function onSave(e: FormEvent) {
async function onSave(e: FormEvent) {
e.preventDefault();
setApiBase(apiBase);
setSaved(true);
window.setTimeout(() => setSaved(false), 1500);
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)" }}>
API https://dc.hyf2.cc(同源可留空当前域名)。
</p>
<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="api">API </label>
<label htmlFor="user"></label>
<input
id="api"
className="mono"
value={apiBase}
onChange={(e) => setApi(e.target.value)}
id="user"
value={newUsername}
onChange={(e) => setNewUsername(e.target.value)}
autoComplete="username"
required
/>
</div>
<button className="btn" type="submit">
<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>
{saved ? <span style={{ marginLeft: 10, color: "var(--up)" }}></span> : null}
</form>
</div>
);