feat: allow admins to bind member phone numbers
Members page previously only displayed phone status; add bind/replace UI and admin member update phone support for login and sync identity.
This commit is contained in:
@@ -38,6 +38,9 @@ export function MembersPage(props: {
|
||||
const [orgs, setOrgs] = useState<OrgUnit[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [phoneOpen, setPhoneOpen] = useState(false);
|
||||
const [phoneTarget, setPhoneTarget] = useState<Member | null>(null);
|
||||
const [phoneValue, setPhoneValue] = useState("");
|
||||
const [form] = Form.useForm<{
|
||||
password?: string;
|
||||
display_name?: string;
|
||||
@@ -120,6 +123,7 @@ export function MembersPage(props: {
|
||||
>
|
||||
<Typography.Paragraph type="secondary" style={{ marginTop: 0 }}>
|
||||
用户名由系统随机生成且全局唯一;初始密码可留空自动生成,登录后可自行「修改密码」。也可走「邀请加入」。
|
||||
管理员可为成员「绑定手机」(登录/同号同步联调用);顶栏「更换手机」只改当前登录账号。
|
||||
{session.role === "超级管理员" || session.role === "platform_admin"
|
||||
? " 超管代管修改时会要求两次确认。"
|
||||
: ""}
|
||||
@@ -134,8 +138,28 @@ export function MembersPage(props: {
|
||||
{
|
||||
title: "手机号",
|
||||
dataIndex: "phone",
|
||||
width: 120,
|
||||
render: (v: string) => v || <Typography.Text type="secondary">未绑定</Typography.Text>,
|
||||
width: 200,
|
||||
render: (v: string, r: Member) => (
|
||||
<Space size={4}>
|
||||
{v ? (
|
||||
<Typography.Text copyable>{v}</Typography.Text>
|
||||
) : (
|
||||
<Typography.Text type="secondary">未绑定</Typography.Text>
|
||||
)}
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
style={{ padding: 0 }}
|
||||
onClick={() => {
|
||||
setPhoneTarget(r);
|
||||
setPhoneValue(v || "");
|
||||
setPhoneOpen(true);
|
||||
}}
|
||||
>
|
||||
{v ? "更换" : "绑定"}
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{ title: "显示名", dataIndex: "display_name", width: 120 },
|
||||
{
|
||||
@@ -204,6 +228,54 @@ export function MembersPage(props: {
|
||||
]}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title={phoneTarget?.phone ? "更换手机号" : "绑定手机号"}
|
||||
open={phoneOpen}
|
||||
onCancel={() => {
|
||||
setPhoneOpen(false);
|
||||
setPhoneTarget(null);
|
||||
}}
|
||||
onOk={async () => {
|
||||
if (!phoneTarget) return;
|
||||
const trimmed = phoneValue.trim();
|
||||
const ok = await confirmCompanyWrite(
|
||||
modal,
|
||||
session,
|
||||
trimmed ? "绑定/更换成员手机号" : "解绑成员手机号",
|
||||
`成员:${phoneTarget.display_name || phoneTarget.username}(#${phoneTarget.user_id})→ ${trimmed || "(空)"}`
|
||||
);
|
||||
if (!ok) return;
|
||||
setBusy(true);
|
||||
try {
|
||||
await updateMember(session, phoneTarget.user_id, { phone: trimmed });
|
||||
setInfo(trimmed ? `已绑定 ${trimmed}` : "已解绑手机号");
|
||||
setPhoneOpen(false);
|
||||
setPhoneTarget(null);
|
||||
await refresh();
|
||||
} catch (e: any) {
|
||||
message.error(e.message || String(e));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}}
|
||||
confirmLoading={busy}
|
||||
destroyOnHidden
|
||||
okText="保存"
|
||||
>
|
||||
<Typography.Paragraph type="secondary">
|
||||
用于手机号登录与松离线同号绑定联调。生产宇信达请绑{" "}
|
||||
<Typography.Text code>13531041944</Typography.Text>
|
||||
;勿绑超管号 13531041945。清空并保存即解绑。
|
||||
</Typography.Paragraph>
|
||||
<Input
|
||||
value={phoneValue}
|
||||
onChange={(e) => setPhoneValue(e.target.value)}
|
||||
placeholder="例:13531041944"
|
||||
maxLength={20}
|
||||
allowClear
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="新建成员"
|
||||
open={open}
|
||||
|
||||
@@ -1557,7 +1557,7 @@ export async function bindPhone(
|
||||
export async function updateMember(
|
||||
session: Session,
|
||||
id: number,
|
||||
body: { role?: string; org_unit_id?: number; status?: string }
|
||||
body: { role?: string; org_unit_id?: number; status?: string; phone?: string }
|
||||
) {
|
||||
const res = await apiFetch(`/api/v1/admin/members/${id}`, {
|
||||
method: "PUT",
|
||||
|
||||
Reference in New Issue
Block a user