| | |
| | | import RegionCascader from '@/components/RegionCascader'; |
| | | import { PlusOutlined } from '@ant-design/icons'; |
| | | import { history, useAccess } from '@umijs/max'; |
| | | import { Button, Descriptions, Form, Input, Modal, Pagination, Space, Table, message } from 'antd'; |
| | | import { Button, Descriptions, Form, Input, Modal, Pagination, Select, Space, Table, message } from 'antd'; |
| | | import React, { useEffect, useState } from 'react'; |
| | | import { createAgent, deleteAgent, getAgentDetail, getAgentPage, updateAgent } from './service'; |
| | | |
| | | type RegionValue = { regionCode?: string[]; regionName?: string }; |
| | | type AgentItem = { id: number; agentName: string; contactInfo?: string; address?: string; regionNames?: string[]; createdAt?: string }; |
| | | type RegionValue = { regionCode?: string[] }; |
| | | type SearchValues = { regionPath?: string[]; clinicName?: string; contactInfo?: string; status?: number }; |
| | | type AgentItem = { id: number; agentName: string; contactInfo?: string; address?: string; regionNames?: string[]; clinicCount?: number; status?: number; createdAt?: string }; |
| | | const statusOptions = [{ label: '启用', value: 1 }, { label: '禁用', value: 0 }, { label: '注销', value: 2 }]; |
| | | const statusText = (value?: number) => value === 1 ? '启用' : value === 2 ? '注销' : '禁用'; |
| | | |
| | | const AgentPage: React.FC = () => { |
| | | const access = useAccess(); |
| | | const [searchForm] = Form.useForm<{ regionPath?: string[]; agentName?: string }>(); |
| | | const [searchForm] = Form.useForm<SearchValues>(); |
| | | const [form] = Form.useForm(); |
| | | const [list, setList] = useState<AgentItem[]>([]); |
| | | const [pagination, setPagination] = useState({ page: 1, pageSize: 10, total: 0 }); |
| | |
| | | setLoading(true); |
| | | try { |
| | | const response = await getAgentPage({ |
| | | page, |
| | | pageSize, |
| | | agentName: search.agentName?.trim() || undefined, |
| | | page, pageSize, |
| | | regionCode: search.regionPath?.[search.regionPath.length - 1], |
| | | clinicName: search.clinicName?.trim() || undefined, |
| | | contactInfo: search.contactInfo?.trim() || undefined, |
| | | status: search.status, |
| | | }); |
| | | setList(response.data?.list || []); |
| | | setPagination({ |
| | | page: Number(response.data?.page || page), |
| | | pageSize: Number(response.data?.pageSize || pageSize), |
| | | total: Number(response.data?.total || 0), |
| | | }); |
| | | } finally { |
| | | setLoading(false); |
| | | } |
| | | setPagination({ page: Number(response.data?.page || page), pageSize: Number(response.data?.pageSize || pageSize), total: Number(response.data?.total || 0) }); |
| | | } finally { setLoading(false); } |
| | | }; |
| | | |
| | | useEffect(() => { |
| | | if (!canView) history.replace('/Welcome'); |
| | | else void load(); |
| | | }, [canView]); |
| | | useEffect(() => { if (!canView) history.replace('/Welcome'); else void load(); }, [canView]); |
| | | |
| | | const openForm = async (id?: number) => { |
| | | setEditingId(id); |
| | | setDetail(undefined); |
| | | setOpen(true); |
| | | form.resetFields(); |
| | | if (!id) { |
| | | form.setFieldsValue({ regions: [{}] }); |
| | | return; |
| | | } |
| | | setEditingId(id); setDetail(undefined); setOpen(true); form.resetFields(); |
| | | if (!id) { form.setFieldsValue({ regions: [{}], status: 1 }); return; } |
| | | const response = await getAgentDetail(id); |
| | | form.setFieldsValue({ |
| | | ...response.data, |
| | | regions: (response.data?.regions || []).map((region: any) => ({ |
| | | ...region, |
| | | regionCode: region.regionPath, |
| | | })), |
| | | }); |
| | | form.setFieldsValue({ ...response.data, regions: (response.data?.regions || []).map((region: any) => ({ ...region, regionCode: region.regionPath })) }); |
| | | }; |
| | | |
| | | const openDetail = async (id: number) => { |
| | | const response = await getAgentDetail(id); |
| | | setEditingId(undefined); |
| | | setDetail(response.data); |
| | | setOpen(true); |
| | | }; |
| | | const openDetail = async (id: number) => { const response = await getAgentDetail(id); setEditingId(undefined); setDetail(response.data); setOpen(true); }; |
| | | |
| | | const submit = async () => { |
| | | const values = await form.validateFields(); |
| | | const regions = (values.regions || []).map((region: RegionValue) => ({ |
| | | regionCode: region.regionCode?.[region.regionCode.length - 1], |
| | | })); |
| | | const regions = (values.regions || []).map((region: RegionValue) => ({ regionCode: region.regionCode?.[region.regionCode.length - 1] })); |
| | | try { |
| | | if (editingId) await updateAgent(editingId, { ...values, regions }); |
| | | else await createAgent({ ...values, regions }); |
| | | message.success(editingId ? '保存成功' : '新增成功'); |
| | | setOpen(false); |
| | | await load(editingId ? pagination.page : 1); |
| | | } catch (error) { |
| | | message.error((error as Error)?.message || '保存失败'); |
| | | } |
| | | if (editingId) await updateAgent(editingId, { ...values, regions }); else await createAgent({ ...values, regions }); |
| | | message.success(editingId ? '保存成功' : '新增成功'); setOpen(false); await load(editingId ? pagination.page : 1); |
| | | } catch (error) { message.error((error as Error)?.message || '保存失败'); } |
| | | }; |
| | | |
| | | const columns = [ |
| | | { title: '序号', width: 70, render: (_: any, __: any, index: number) => (pagination.page - 1) * pagination.pageSize + index + 1 }, |
| | | { title: '代理负责人', dataIndex: 'agentName' }, |
| | | { title: '负责人', dataIndex: 'agentName' }, |
| | | { title: '电话', dataIndex: 'contactInfo', render: (value: string) => value || '-' }, |
| | | { title: '负责省份', render: (_: any, record: AgentItem) => (record.regionNames || []).join('、') || '-' }, |
| | | { title: '联系信息', dataIndex: 'contactInfo', render: (value: string) => value || '-' }, |
| | | { title: '详细地址', dataIndex: 'address', render: (value: string) => value || '-' }, |
| | | { title: '下级诊所数', dataIndex: 'clinicCount', width: 110, render: (value: number) => value ?? 0 }, |
| | | { title: '状态', dataIndex: 'status', width: 90, render: statusText }, |
| | | { title: '创建时间', dataIndex: 'createdAt', width: 180 }, |
| | | { |
| | | title: '操作', |
| | | width: 180, |
| | | render: (_: any, record: AgentItem, index: number) => <Space size={0}> |
| | | {access['member:agent:detail'] && <Button type="link" onClick={() => openDetail(record.id)}>详情</Button>} |
| | | {access['member:agent:edit'] && <Button type="link" onClick={() => openForm(record.id)}>编辑</Button>} |
| | | {access['member:agent:delete'] && !(pagination.page === 1 && index === 0) && <Button type="link" danger onClick={() => Modal.confirm({ |
| | | title: '确定删除该省代理吗?', |
| | | onOk: async () => { await deleteAgent(record.id); message.success('删除成功'); await load(); }, |
| | | })}>删除</Button>} |
| | | </Space>, |
| | | }, |
| | | { title: '操作', width: 180, render: (_: any, record: AgentItem, index: number) => <Space size={0}> |
| | | {access['member:agent:detail'] && <Button type="link" onClick={() => openDetail(record.id)}>详情</Button>} |
| | | {access['member:agent:edit'] && <Button type="link" onClick={() => openForm(record.id)}>编辑</Button>} |
| | | {access['member:agent:delete'] && !(pagination.page === 1 && index === 0) && <Button type="link" danger onClick={() => Modal.confirm({ title: '确定删除该省代理吗?', onOk: async () => { await deleteAgent(record.id); message.success('删除成功'); await load(); } })}>删除</Button>} |
| | | </Space> }, |
| | | ]; |
| | | |
| | | return <div style={{ padding: 24 }}> |
| | | <h2>省代理管理</h2> |
| | | <Form form={searchForm} layout="inline" onFinish={() => load(1)}> |
| | | <Form.Item name="regionPath" label="负责省份"><RegionCascader allowClear changeOnSelect placeholder="全部" /></Form.Item> |
| | | <Form.Item name="agentName" label="代理负责人"><Input allowClear placeholder="请输入" /></Form.Item> |
| | | <Form.Item name="regionPath" label="诊所区域"><RegionCascader allowClear changeOnSelect placeholder="全部" /></Form.Item> |
| | | <Form.Item name="clinicName" label="诊所名称"><Input allowClear placeholder="请输入" /></Form.Item> |
| | | <Form.Item name="contactInfo" label="电话"><Input allowClear placeholder="请输入" /></Form.Item> |
| | | <Form.Item name="status" label="状态"><Select allowClear placeholder="请选择" style={{ width: 120 }} options={statusOptions} /></Form.Item> |
| | | <Button onClick={() => { searchForm.resetFields(); void load(1); }}>重置</Button> |
| | | <Button type="primary" htmlType="submit" style={{ marginLeft: 8 }}>查询</Button> |
| | | </Form> |
| | | <div style={{ margin: '16px 0' }}>{access['member:agent:add'] && <Button type="primary" icon={<PlusOutlined />} onClick={() => openForm()}>新增</Button>}</div> |
| | | <Table rowKey="id" columns={columns as any} dataSource={list} loading={loading} pagination={false} /> |
| | | <Table rowKey="id" columns={columns as any} dataSource={list} loading={loading} pagination={false} scroll={{ x: 1200 }} /> |
| | | <Pagination style={{ marginTop: 16, textAlign: 'right' }} current={pagination.page} pageSize={pagination.pageSize} total={pagination.total} showSizeChanger onChange={load} /> |
| | | <Modal open={open} title={detail ? '省代理详情' : editingId ? '编辑省代理' : '新增省代理'} width={760} onCancel={() => setOpen(false)} footer={detail ? null : <><Button onClick={() => setOpen(false)}>取消</Button><Button type="primary" onClick={submit}>确定</Button></>}> |
| | | {detail ? <Descriptions bordered column={2} items={[ |
| | | ['代理负责人', detail.agentName], ['负责省份', (detail.regionNames || []).join('、')], |
| | | ['开户银行', detail.bankName], ['银行账号', detail.accountNumber], ['账户名称', detail.accountHolder], |
| | | ['开户支行', detail.branchName], ['联系信息', detail.contactInfo], ['详细地址', detail.address], |
| | | ].map(([label, children]) => ({ label, children: children || '-' }))} /> : |
| | | ['负责人', detail.agentName], ['电话', detail.contactInfo], ['负责省份', (detail.regionNames || []).join('、')], |
| | | ['详细地址', detail.address], ['下级诊所数', detail.clinicCount], ['状态', statusText(detail.status)], |
| | | ['开户银行', detail.bankName], ['银行账号', detail.accountNumber], ['账户名称', detail.accountHolder], ['开户支行', detail.branchName], |
| | | ].map(([label, children]) => ({ label, children: children ?? '-' }))} /> : |
| | | <Form form={form} labelCol={{ span: 5 }}> |
| | | <Form.Item name="agentName" label="代理负责人" rules={[{ required: true, message: '请输入代理负责人' }]}><Input maxLength={50} /></Form.Item> |
| | | <Form.Item name="agentName" label="负责人" rules={[{ required: true, message: '请输入负责人' }]}><Input maxLength={50} /></Form.Item> |
| | | <Form.List name="regions" rules={[{ validator: async (_, value) => { if (!value?.length) throw new Error('请选择负责省份'); } }]}> |
| | | {(fields, { add, remove }, { errors }) => <Form.Item label="负责省份" required> |
| | | {fields.map(({ key, name, ...rest }) => <Space key={key} align="baseline"> |
| | | <Form.Item {...rest} name={[name, 'regionCode']} rules={[{ required: true, message: '请选择负责省份' }]}><RegionCascader changeOnSelect /></Form.Item> |
| | | {fields.length > 1 && <Button onClick={() => remove(name)}>删除</Button>} |
| | | </Space>)} |
| | | <Button type="dashed" onClick={() => add({})}>添加负责省份</Button> |
| | | <Form.ErrorList errors={errors} /> |
| | | {fields.map(({ key, name, ...rest }) => <Space key={key} align="baseline"><Form.Item {...rest} name={[name, 'regionCode']} rules={[{ required: true, message: '请选择负责省份' }]}><RegionCascader changeOnSelect /></Form.Item>{fields.length > 1 && <Button onClick={() => remove(name)}>删除</Button>}</Space>)} |
| | | <Button type="dashed" onClick={() => add({})}>添加负责省份</Button><Form.ErrorList errors={errors} /> |
| | | </Form.Item>} |
| | | </Form.List> |
| | | <Form.Item name="contactInfo" label="电话"><Input maxLength={200} /></Form.Item> |
| | | <Form.Item name="address" label="详细地址"><Input.TextArea rows={3} maxLength={255} /></Form.Item> |
| | | <Form.Item name="status" label="状态" rules={[{ required: true, message: '请选择状态' }]}><Select options={statusOptions} /></Form.Item> |
| | | <Form.Item name="bankName" label="开户银行"><Input maxLength={100} /></Form.Item> |
| | | <Form.Item name="accountNumber" label="银行账号"><Input maxLength={30} /></Form.Item> |
| | | <Form.Item name="accountHolder" label="账户名称"><Input maxLength={50} /></Form.Item> |
| | | <Form.Item name="branchName" label="开户支行"><Input maxLength={100} /></Form.Item> |
| | | <Form.Item name="contactInfo" label="联系信息"><Input maxLength={200} /></Form.Item> |
| | | <Form.Item name="address" label="详细地址"><Input.TextArea rows={3} maxLength={255} /></Form.Item> |
| | | </Form>} |
| | | </Modal> |
| | | </div>; |
| | | }; |
| | | |
| | | export default AgentPage; |