管理后台左侧列表调整
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
Button, Drawer, Form, Input, Modal, Select, Space, Table, Tag, Typography, message,
|
||||
Button, Drawer, Form, Input, Modal, Radio, Select, Space, Table, Tag, Typography, message,
|
||||
} from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { request, type HqProfile } from '../lib/api';
|
||||
@@ -8,7 +8,15 @@ import { ACCOUNT_STATUS_LABELS, fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
|
||||
type Row = {
|
||||
id: string; phone: string; name: string; adminRole: string; status: string; lastLoginAt: string | null; createdAt: string;
|
||||
id: string;
|
||||
phone: string;
|
||||
loginName: string | null;
|
||||
hasPassword: boolean;
|
||||
name: string;
|
||||
adminRole: string;
|
||||
status: string;
|
||||
lastLoginAt: string | null;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
const ROLE_LABELS: Record<string, string> = {
|
||||
@@ -38,6 +46,7 @@ export default function HqAccountsPage() {
|
||||
const [detail, setDetail] = useState<Row | null>(null);
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const [createOpen, setCreateOpen] = useState(false);
|
||||
const credentialType = Form.useWatch('credentialType', createForm) ?? 'phone';
|
||||
const isSuperAdmin = profile?.adminRole === 'SUPER_ADMIN';
|
||||
|
||||
useEffect(() => {
|
||||
@@ -46,7 +55,18 @@ export default function HqAccountsPage() {
|
||||
|
||||
const columns: ColumnsType<Row> = [
|
||||
{ title: '姓名', dataIndex: 'name' },
|
||||
{ title: '用户名', dataIndex: 'loginName', width: 120, render: (v) => v || '—' },
|
||||
{ title: '手机', dataIndex: 'phone', width: 130 },
|
||||
{
|
||||
title: '登录方式',
|
||||
width: 110,
|
||||
render: (_, r) => (
|
||||
<Space size={4}>
|
||||
{r.hasPassword ? <Tag color="blue">密码</Tag> : null}
|
||||
<Tag>短信</Tag>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{ title: '角色', dataIndex: 'adminRole', width: 110, render: (r) => ROLE_LABELS[r] || r },
|
||||
{ title: '状态', dataIndex: 'status', width: 90, render: (s) => <Tag>{ACCOUNT_STATUS_LABELS[s] || s}</Tag> },
|
||||
{ title: '最后登录', dataIndex: 'lastLoginAt', width: 160, render: fmtTime },
|
||||
@@ -55,7 +75,12 @@ export default function HqAccountsPage() {
|
||||
render: (_, row) => (
|
||||
<Button type="link" size="small" disabled={!isSuperAdmin} onClick={() => {
|
||||
setDetail(row);
|
||||
editForm.setFieldsValue(row);
|
||||
editForm.setFieldsValue({
|
||||
name: row.name,
|
||||
loginName: row.loginName,
|
||||
adminRole: row.adminRole,
|
||||
status: row.status,
|
||||
});
|
||||
setDrawerOpen(true);
|
||||
}}>编辑</Button>
|
||||
),
|
||||
@@ -66,7 +91,18 @@ export default function HqAccountsPage() {
|
||||
<div>
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>HQ 账户</Typography.Title>
|
||||
{isSuperAdmin && <Button type="primary" onClick={() => setCreateOpen(true)}>新建账户</Button>}
|
||||
{isSuperAdmin && (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
createForm.resetFields();
|
||||
createForm.setFieldsValue({ credentialType: 'phone', adminRole: 'OPS' });
|
||||
setCreateOpen(true);
|
||||
}}
|
||||
>
|
||||
新建账户
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="phone" label="手机"><Input allowClear /></Form.Item>
|
||||
@@ -82,7 +118,9 @@ export default function HqAccountsPage() {
|
||||
<Button type="primary" onClick={async () => {
|
||||
if (!detail) return;
|
||||
const v = await editForm.validateFields();
|
||||
await request(`/admin/hq-accounts/${detail.id}`, { method: 'PUT', body: JSON.stringify(v) });
|
||||
const payload = { ...v };
|
||||
if (!payload.password) delete payload.password;
|
||||
await request(`/admin/hq-accounts/${detail.id}`, { method: 'PUT', body: JSON.stringify(payload) });
|
||||
message.success('已保存');
|
||||
setDrawerOpen(false);
|
||||
void reload();
|
||||
@@ -90,6 +128,10 @@ export default function HqAccountsPage() {
|
||||
}>
|
||||
<Form form={editForm} layout="vertical">
|
||||
<Form.Item name="name" label="姓名" rules={[{ required: true }]}><Input /></Form.Item>
|
||||
<Form.Item name="loginName" label="用户名"><Input placeholder="用于密码登录" /></Form.Item>
|
||||
<Form.Item name="password" label="新密码" extra="留空则不修改">
|
||||
<Input.Password placeholder="至少 6 位" />
|
||||
</Form.Item>
|
||||
<Form.Item name="adminRole" label="角色">
|
||||
<Select options={Object.entries(ROLE_LABELS).map(([value, label]) => ({ value, label }))} />
|
||||
</Form.Item>
|
||||
@@ -98,20 +140,47 @@ export default function HqAccountsPage() {
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Drawer>
|
||||
<Modal title="新建 HQ 账户" open={createOpen} onCancel={() => setCreateOpen(false)} onOk={async () => {
|
||||
const v = await createForm.validateFields();
|
||||
await request('/admin/hq-accounts', { method: 'POST', body: JSON.stringify(v) });
|
||||
message.success('已创建');
|
||||
setCreateOpen(false);
|
||||
createForm.resetFields();
|
||||
void reload();
|
||||
}}>
|
||||
<Form form={createForm} layout="vertical">
|
||||
<Form.Item name="phone" label="手机" rules={[{ required: true }]}><Input /></Form.Item>
|
||||
<Modal
|
||||
title="新建 HQ 账户"
|
||||
open={createOpen}
|
||||
onCancel={() => setCreateOpen(false)}
|
||||
onOk={async () => {
|
||||
const v = await createForm.validateFields();
|
||||
await request('/admin/hq-accounts', { method: 'POST', body: JSON.stringify(v) });
|
||||
message.success('已创建');
|
||||
setCreateOpen(false);
|
||||
createForm.resetFields();
|
||||
void reload();
|
||||
}}
|
||||
>
|
||||
<Form form={createForm} layout="vertical" initialValues={{ credentialType: 'phone', adminRole: 'OPS' }}>
|
||||
<Form.Item name="credentialType" label="创建方式">
|
||||
<Radio.Group>
|
||||
<Radio value="phone">手机号账户(短信登录)</Radio>
|
||||
<Radio value="password">用户名密码账户</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item name="name" label="姓名" rules={[{ required: true }]}><Input /></Form.Item>
|
||||
<Form.Item name="adminRole" label="角色" initialValue="OPS">
|
||||
<Form.Item name="adminRole" label="角色">
|
||||
<Select options={Object.entries(ROLE_LABELS).map(([value, label]) => ({ value, label }))} />
|
||||
</Form.Item>
|
||||
{credentialType === 'phone' ? (
|
||||
<Form.Item name="phone" label="手机号" rules={[{ required: true, message: '请输入手机号' }]}>
|
||||
<Input maxLength={11} />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<>
|
||||
<Form.Item name="loginName" label="用户名" rules={[{ required: true, message: '请输入用户名' }]}>
|
||||
<Input autoComplete="off" />
|
||||
</Form.Item>
|
||||
<Form.Item name="password" label="密码" rules={[{ required: true, message: '请输入密码' }, { min: 6, message: '至少 6 位' }]}>
|
||||
<Input.Password autoComplete="new-password" />
|
||||
</Form.Item>
|
||||
<Form.Item name="phone" label="手机号(可选)" extra="不填则自动生成占位手机号,用于满足账号唯一约束">
|
||||
<Input maxLength={11} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user