新增 pc-client:畅联 PC 端工程(Electron+React,按确认效果图改造,B-59)
- 基于 openim-electron-demo 改造:工号+密码登录(自研账号服务)、会话列表/聊天窗/通讯录界面重做
- 文字/语音/文件/图片消息、文件拖拽发送、表情面板、一对一语音通话(LiveKit)
- RTC 令牌对接账号服务带鉴权版 /api/rtc_token(Bearer imToken + {room,identity})
- account-service: CORS Allow-Headers 补 authorization(浏览器/渲染进程跨域调 rtc_token 需要)
- 附本地 mock 账号服务(scripts/mock-account-server.js)与截图联调脚本
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
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 { feedbackToast } from "@/utils/common";
|
||||
import { setIMProfile } from "@/utils/storage";
|
||||
|
||||
interface LoginResult {
|
||||
code: number;
|
||||
msg: string;
|
||||
data?: {
|
||||
userID: string;
|
||||
nickname: string;
|
||||
imToken: string;
|
||||
expireTimeSeconds?: number;
|
||||
};
|
||||
}
|
||||
|
||||
const LoginForm = () => {
|
||||
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>(
|
||||
`${import.meta.env.VITE_ACCOUNT_URL}/api/login`,
|
||||
{
|
||||
staffNo: values.staffNo.trim(),
|
||||
password: values.password,
|
||||
platformID: window.electronAPI?.getPlatform() ?? 5,
|
||||
},
|
||||
);
|
||||
if (res.code !== 0 || !res.data) {
|
||||
feedbackToast({
|
||||
msg: res.msg || t("toast.accessFailed"),
|
||||
error: res.msg || "login failed",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { userID, imToken } = res.data;
|
||||
setIMProfile({ chatToken: imToken, imToken, userID });
|
||||
navigate("/chat");
|
||||
} catch (error) {
|
||||
feedbackToast({ msg: "登录失败,请检查网络后重试", error });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<div className="text-xl font-medium">{t("placeholder.welcome")}</div>
|
||||
</div>
|
||||
<Form
|
||||
className="mt-8"
|
||||
layout="vertical"
|
||||
onFinish={onFinish}
|
||||
autoComplete="off"
|
||||
labelCol={{ prefixCls: "custom-form-item" }}
|
||||
>
|
||||
<Form.Item
|
||||
label={t("placeholder.staffNo")}
|
||||
name="staffNo"
|
||||
rules={[{ required: true, message: t("toast.inputStaffNo") }]}
|
||||
>
|
||||
<Input allowClear placeholder={t("toast.inputStaffNo")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t("placeholder.password")}
|
||||
name="password"
|
||||
rules={[{ required: true, message: t("toast.inputPassword") }]}
|
||||
>
|
||||
<Input.Password allowClear placeholder={t("toast.inputPassword")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item className="mb-4 mt-10">
|
||||
<Button type="primary" htmlType="submit" block loading={loading}>
|
||||
{t("placeholder.login")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginForm;
|
||||
Reference in New Issue
Block a user