Windows 补 .env.production 与 endpoints 兜底;Android 默认 host/端口改为内网,启动后重写 HttpUtil/ApiService,Urls 改为 getter。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
115 lines
3.1 KiB
TypeScript
115 lines
3.1 KiB
TypeScript
import { Button, Form, Input } from "antd";
|
|
import axios from "axios";
|
|
import { t } from "i18next";
|
|
import { useState } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
|
|
import { ACCOUNT_URL } from "@/config/endpoints";
|
|
import { setIMProfile } from "@/utils/storage";
|
|
|
|
interface LoginResult {
|
|
code: number;
|
|
msg: string;
|
|
data?: {
|
|
userID: string;
|
|
nickname: string;
|
|
imToken: string;
|
|
expireTimeSeconds?: number;
|
|
};
|
|
}
|
|
|
|
type LoginFormProps = {
|
|
onBanner: (text: string) => void;
|
|
};
|
|
|
|
const LoginForm = ({ onBanner }: LoginFormProps) => {
|
|
const navigate = useNavigate();
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const onFinish = async (values: { staffNo: string; password: string }) => {
|
|
setLoading(true);
|
|
try {
|
|
const { data: res } = await axios.post<LoginResult>(
|
|
`${ACCOUNT_URL}/api/login`,
|
|
{
|
|
staffNo: values.staffNo.trim(),
|
|
password: values.password,
|
|
platformID: window.electronAPI?.getPlatform() ?? 5,
|
|
},
|
|
);
|
|
if (res.code !== 0 || !res.data) {
|
|
onBanner(t("toast.staffOrPasswordWrong"));
|
|
return;
|
|
}
|
|
const { userID, imToken } = res.data;
|
|
setIMProfile({ chatToken: imToken, imToken, userID });
|
|
navigate("/chat");
|
|
} catch {
|
|
onBanner(t("toast.networkRetry"));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className="text-center text-xl font-bold text-[var(--base-black)]">
|
|
{t("placeholder.title")}
|
|
</div>
|
|
<div className="mt-1 text-center text-sm text-[var(--sub-text)]">
|
|
{t("placeholder.internalComms")}
|
|
</div>
|
|
<Form
|
|
className="mt-8"
|
|
layout="vertical"
|
|
onFinish={onFinish}
|
|
autoComplete="off"
|
|
validateTrigger={["onSubmit"]}
|
|
>
|
|
<Form.Item
|
|
label={t("placeholder.staffNo")}
|
|
name="staffNo"
|
|
rules={[{ required: true, message: t("toast.inputStaffNo") }]}
|
|
>
|
|
<Input
|
|
allowClear
|
|
placeholder={t("toast.inputStaffNo")}
|
|
style={{ height: 40, borderRadius: 8, backgroundColor: "var(--search-bg)" }}
|
|
disabled={loading}
|
|
/>
|
|
</Form.Item>
|
|
|
|
<Form.Item
|
|
label={t("placeholder.password")}
|
|
name="password"
|
|
rules={[{ required: true, message: t("toast.inputPassword") }]}
|
|
>
|
|
<Input.Password
|
|
allowClear
|
|
placeholder={t("toast.inputPassword")}
|
|
style={{ height: 40, borderRadius: 8, backgroundColor: "var(--search-bg)" }}
|
|
disabled={loading}
|
|
/>
|
|
</Form.Item>
|
|
|
|
<Form.Item className="mb-2 mt-8">
|
|
<Button
|
|
type="primary"
|
|
htmlType="submit"
|
|
block
|
|
loading={loading}
|
|
style={{ height: 40, borderRadius: 8 }}
|
|
>
|
|
{loading ? t("placeholder.loggingIn") : t("placeholder.login")}
|
|
</Button>
|
|
</Form.Item>
|
|
<div className="text-center text-xs text-[var(--sub-text)]">
|
|
{t("placeholder.agreeInternal")}
|
|
</div>
|
|
</Form>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default LoginForm;
|