From 3df055ea1038e0bbcbbbf80ccbeea43c82952932 Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期二, 18 八月 2026 12:51:56 +0800
Subject: [PATCH] feat0806: 优化代理搜索条件

---
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/dto/ProvincialAgentSaveRequest.java       |    5 +
 tcm-training-admin-backend/sql/20260806_admin.sql                                                               |    1 
 tcm-training-admin/src/pages/member/agent/index.tsx                                                             |  128 +++++++++++++++---------------------------
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/entity/ProvincialAgent.java               |    2 
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/vo/ProvincialAgentListItemVO.java         |    4 +
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/controller/ProvincialAgentController.java |    4 
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java    |   34 +++++++++--
 7 files changed, 87 insertions(+), 91 deletions(-)

diff --git a/tcm-training-admin-backend/sql/20260806_admin.sql b/tcm-training-admin-backend/sql/20260806_admin.sql
index 0a0a676..362b675 100644
--- a/tcm-training-admin-backend/sql/20260806_admin.sql
+++ b/tcm-training-admin-backend/sql/20260806_admin.sql
@@ -10,6 +10,7 @@
     account_holder VARCHAR(50) COMMENT '账户名称/户名(即开户人姓名或公司名称)',
     branch_name VARCHAR(100) COMMENT '开户支行名称(如:成都高新支行)',
     contact_info VARCHAR(200) COMMENT '联系信息(可填电话、邮箱、手机号等)',
+    status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1启用,0禁用,2注销',
     address VARCHAR(255) COMMENT '详细地址(如:成都市高新区天府大道XX号)',
     `deleted` tinyint(1) NOT NULL DEFAULT '0',
   `created_by` varchar(64) NOT NULL DEFAULT '系统初始化' COMMENT '创建人',
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/controller/ProvincialAgentController.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/controller/ProvincialAgentController.java
index 0cbfbef..1b58d36 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/controller/ProvincialAgentController.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/controller/ProvincialAgentController.java
@@ -22,8 +22,8 @@
     }
 
     @GetMapping("/provincial-agents")
-    public ApiResponse<PageResponse<ProvincialAgentListItemVO>> page(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int pageSize, @RequestParam(required = false) String agentName, @RequestParam(required = false) String regionCode) {
-        return ApiResponse.ok(service.page(page, pageSize, agentName, regionCode));
+    public ApiResponse<PageResponse<ProvincialAgentListItemVO>> page(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int pageSize, @RequestParam(required = false) String regionCode, @RequestParam(required = false) String clinicName, @RequestParam(required = false) String contactInfo, @RequestParam(required = false) Integer status) {
+        return ApiResponse.ok(service.page(page, pageSize, regionCode, clinicName, contactInfo, status));
     }
 
     @GetMapping("/provincial-agents/{id}")
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/dto/ProvincialAgentSaveRequest.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/dto/ProvincialAgentSaveRequest.java
index d16c331..0973955 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/dto/ProvincialAgentSaveRequest.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/dto/ProvincialAgentSaveRequest.java
@@ -5,6 +5,8 @@
 
 import javax.validation.Valid;
 import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
 import javax.validation.constraints.Size;
 import java.util.ArrayList;
 import java.util.List;
@@ -27,6 +29,9 @@
     private String contactInfo;
     @Size(max = 255)
     private String address;
+    @Min(value = 0, message = "状态值不正确")
+    @Max(value = 2, message = "状态值不正确")
+    private Integer status;
     @Valid
     @Size(min = 1, message = "请选择负责省份")
     private List<RegionItem> regions = new ArrayList<RegionItem>();
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/entity/ProvincialAgent.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/entity/ProvincialAgent.java
index a5b9a03..4711ecf 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/entity/ProvincialAgent.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/entity/ProvincialAgent.java
@@ -37,6 +37,8 @@
     @TableField("contact_info")
     private String contactInfo;
 
+    private Integer status;
+
     @TableField("address")
     private String address;
 
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java
index ebcb162..b91d688 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java
@@ -37,17 +37,27 @@
         this.clinicMapper = clinicMapper;
     }
 
-    public PageResponse<ProvincialAgentListItemVO> page(int page, int pageSize, String agentName, String regionCode) {
+    public PageResponse<ProvincialAgentListItemVO> page(int page, int pageSize, String regionCode,
+                                                        String clinicName, String contactInfo, Integer status) {
         LambdaQueryWrapper<ProvincialAgent> query = new LambdaQueryWrapper<ProvincialAgent>()
                 .eq(ProvincialAgent::getDeleted, 0)
-                .like(trim(agentName) != null, ProvincialAgent::getAgentName, trim(agentName))
-                .orderByAsc(ProvincialAgent::getCreatedAt).orderByDesc(ProvincialAgent::getId);
+                .like(trim(contactInfo) != null, ProvincialAgent::getContactInfo, trim(contactInfo))
+                .eq(status != null, ProvincialAgent::getStatus, status)
+                .orderByAsc(ProvincialAgent::getCreatedAt).orderByAsc(ProvincialAgent::getId);
         if (trim(regionCode) != null) {
             List<Long> ids = new ArrayList<Long>();
             for (AgentRegion region : regionMapper.selectList(new LambdaQueryWrapper<AgentRegion>()
-                    .eq(AgentRegion::getRegionCode, trim(regionCode))))
-                ids.add(region.getAgentId());
-            if (ids.isEmpty()) return PageResponse.of(new ArrayList<ProvincialAgentListItemVO>(), 0, page, pageSize);
+                    .eq(AgentRegion::getRegionCode, trim(regionCode)))) ids.add(region.getAgentId());
+            if (ids.isEmpty()) return emptyPage(page, pageSize);
+            query.in(ProvincialAgent::getId, ids);
+        }
+        if (trim(clinicName) != null) {
+            List<Long> ids = new ArrayList<Long>();
+            for (Clinic clinic : clinicMapper.selectList(new LambdaQueryWrapper<Clinic>()
+                    .like(Clinic::getName, trim(clinicName)))) {
+                if (clinic.getAgentId() != null && !ids.contains(clinic.getAgentId())) ids.add(clinic.getAgentId());
+            }
+            if (ids.isEmpty()) return emptyPage(page, pageSize);
             query.in(ProvincialAgent::getId, ids);
         }
         IPage<ProvincialAgent> result = agentMapper.selectPage(new Page<ProvincialAgent>(page, pageSize), query);
@@ -56,6 +66,9 @@
         return PageResponse.of(list, result.getTotal(), result.getCurrent(), result.getSize());
     }
 
+    private PageResponse<ProvincialAgentListItemVO> emptyPage(int page, int pageSize) {
+        return PageResponse.of(new ArrayList<ProvincialAgentListItemVO>(), 0, page, pageSize);
+    }
     public ProvincialAgentDetailVO detail(Long id) {
         return toDetail(require(id));
     }
@@ -99,6 +112,7 @@
         agent.setAccountHolder(trim(request.getAccountHolder()));
         agent.setBranchName(trim(request.getBranchName()));
         agent.setContactInfo(trim(request.getContactInfo()));
+        agent.setStatus(request.getStatus() == null ? 1 : request.getStatus());
         agent.setAddress(trim(request.getAddress()));
     }
 
@@ -130,10 +144,14 @@
         vo.setContactInfo(agent.getContactInfo());
         vo.setAddress(agent.getAddress());
         vo.setCreatedAt(agent.getCreatedAt());
+        vo.setStatus(agent.getStatus());
+        vo.setClinicCount(clinicMapper.selectCount(new LambdaQueryWrapper<Clinic>().eq(Clinic::getAgentId, agent.getId())));
         List<AgentRegion> regions = regions(agent.getId());
         vo.setRegionCount(regions.size());
         List<String> names = new ArrayList<String>();
-        for (AgentRegion region : regions) names.add(resolveRegion(region.getRegionCode()).name);
+        for (AgentRegion region : regions) {
+            names.add(resolveRegion(region.getRegionCode()).name);
+        }
         vo.setRegionNames(names);
         return vo;
     }
@@ -148,6 +166,8 @@
         vo.setRegionCount(base.getRegionCount());
         vo.setRegionNames(base.getRegionNames());
         vo.setCreatedAt(base.getCreatedAt());
+        vo.setStatus(base.getStatus());
+        vo.setClinicCount(base.getClinicCount());
         vo.setBankName(agent.getBankName());
         vo.setAccountNumber(agent.getAccountNumber());
         vo.setAccountHolder(agent.getAccountHolder());
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/vo/ProvincialAgentListItemVO.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/vo/ProvincialAgentListItemVO.java
index 6f7bbfd..676f3f8 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/vo/ProvincialAgentListItemVO.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/vo/ProvincialAgentListItemVO.java
@@ -16,4 +16,8 @@
     private Integer regionCount;
     private List<String> regionNames;
     private LocalDateTime createdAt;
+
+    private Integer status;
+
+    private Long clinicCount;
 }
diff --git a/tcm-training-admin/src/pages/member/agent/index.tsx b/tcm-training-admin/src/pages/member/agent/index.tsx
index ae7946a..e64bb1c 100644
--- a/tcm-training-admin/src/pages/member/agent/index.tsx
+++ b/tcm-training-admin/src/pages/member/agent/index.tsx
@@ -1,16 +1,19 @@
 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 });
@@ -25,128 +28,89 @@
     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;
\ No newline at end of file

--
Gitblit v1.7.1