From 2f05b4e14515e0a7dd7e76e8bf0e40ec1d8ef8e2 Mon Sep 17 00:00:00 2001
From: “mitao” <“2763622819@qq.com”>
Date: 星期五, 27 三月 2026 19:13:20 +0800
Subject: [PATCH] fix:会员列表

---
 tcm-training-admin/src/pages/member/user/list-page.tsx                                                 |  197 +++++++++++++++++++--------
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/excel/MemberUserExcelRow.java   |   25 +++
 tcm-training-admin-backend/src/test/java/com/tcm/training/admin/member/service/UserAppServiceTest.java |   91 +++++++++++++
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/controller/UserController.java  |   14 +
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/mapper/UserMapper.java          |    6 
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/service/UserAppService.java     |   50 +++++++
 tcm-training-admin/src/pages/member/user/style.less                                                    |    8 +
 tcm-training-admin-backend/src/main/resources/mapper/UserMapper.xml                                    |   17 ++
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/row/UserListRow.java            |    1 
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/vo/UserListItemVO.java          |    1 
 10 files changed, 345 insertions(+), 65 deletions(-)

diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/controller/UserController.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/controller/UserController.java
index 9c31cc7..b663974 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/controller/UserController.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/controller/UserController.java
@@ -49,10 +49,14 @@
             @RequestParam(required = false) Long clinicId,
             @RequestParam(required = false) Integer status,
             @RequestParam(required = false) Integer isMember,
+            @RequestParam(required = false) String memberStatus,
+            @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime memberExpireFrom,
+            @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime memberExpireTo,
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime registeredFrom,
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime registeredTo) {
         return ApiResponse.ok(userAppService.page(
-                page, pageSize, nickname, phone, clinicId, status, isMember, registeredFrom, registeredTo
+                page, pageSize, nickname, phone, clinicId, status, isMember,
+                memberStatus, memberExpireFrom, memberExpireTo, registeredFrom, registeredTo
         ));
     }
 
@@ -64,9 +68,15 @@
             @RequestParam(required = false) Long clinicId,
             @RequestParam(required = false) Integer status,
             @RequestParam(required = false) Integer isMember,
+            @RequestParam(required = false) String memberStatus,
+            @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime memberExpireFrom,
+            @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime memberExpireTo,
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime registeredFrom,
             @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime registeredTo) throws IOException {
-        userAppService.export(nickname, phone, clinicId, status, isMember, registeredFrom, registeredTo, response);
+        userAppService.export(
+                nickname, phone, clinicId, status, isMember,
+                memberStatus, memberExpireFrom, memberExpireTo, registeredFrom, registeredTo, response
+        );
     }
 
     @GetMapping("/users/{id}")
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/excel/MemberUserExcelRow.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/excel/MemberUserExcelRow.java
new file mode 100644
index 0000000..20b133c
--- /dev/null
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/excel/MemberUserExcelRow.java
@@ -0,0 +1,25 @@
+package com.tcm.training.admin.member.excel;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class MemberUserExcelRow {
+
+    @ExcelProperty("序号")
+    private Long id;
+
+    @ExcelProperty("用户")
+    private String nickname;
+
+    @ExcelProperty("电话")
+    private String phone;
+
+    @ExcelProperty("会员状态")
+    private String memberStatus;
+
+    @ExcelProperty("到期时间")
+    private String memberExpireAt;
+}
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/mapper/UserMapper.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/mapper/UserMapper.java
index 410694f..088d015 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/mapper/UserMapper.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/mapper/UserMapper.java
@@ -20,6 +20,9 @@
                                       @Param("clinicId") Long clinicId,
                                       @Param("status") Integer status,
                                       @Param("isMember") Integer isMember,
+                                      @Param("memberStatus") String memberStatus,
+                                      @Param("memberExpireFrom") LocalDateTime memberExpireFrom,
+                                      @Param("memberExpireTo") LocalDateTime memberExpireTo,
                                       @Param("registeredFrom") LocalDateTime registeredFrom,
                                       @Param("registeredTo") LocalDateTime registeredTo);
 
@@ -28,6 +31,9 @@
                                      @Param("clinicId") Long clinicId,
                                      @Param("status") Integer status,
                                      @Param("isMember") Integer isMember,
+                                     @Param("memberStatus") String memberStatus,
+                                     @Param("memberExpireFrom") LocalDateTime memberExpireFrom,
+                                     @Param("memberExpireTo") LocalDateTime memberExpireTo,
                                      @Param("registeredFrom") LocalDateTime registeredFrom,
                                      @Param("registeredTo") LocalDateTime registeredTo);
 
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/row/UserListRow.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/row/UserListRow.java
index 17c40b8..731bcaa 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/row/UserListRow.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/row/UserListRow.java
@@ -22,5 +22,6 @@
     private Long clinicId;
     private String clinicName;
     private Integer isMember;
+    private LocalDateTime memberExpireAt;
     private LocalDateTime createdAt;
 }
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/service/UserAppService.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/service/UserAppService.java
index ca5a3c6..3047c29 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/service/UserAppService.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/service/UserAppService.java
@@ -10,6 +10,7 @@
 import com.tcm.training.admin.live.entity.Clinic;
 import com.tcm.training.admin.member.entity.AppUser;
 import com.tcm.training.admin.live.mapper.ClinicMapper;
+import com.tcm.training.admin.member.excel.MemberUserExcelRow;
 import com.tcm.training.admin.member.dto.UserResetPasswordRequest;
 import com.tcm.training.admin.member.dto.UserStatusRequest;
 import com.tcm.training.admin.member.dto.UserUpdateRequest;
@@ -41,6 +42,9 @@
     private static final Pattern PASSWORD_PATTERN = Pattern.compile(
             "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^A-Za-z\\d]).{12,64}$"
     );
+    private static final String MEMBER_STATUS_ALL = "ALL";
+    private static final String MEMBER_STATUS_ACTIVE = "ACTIVE";
+    private static final String MEMBER_STATUS_EXPIRED = "EXPIRED";
 
     private final UserMapper userMapper;
     private final ClinicMapper clinicMapper;
@@ -61,8 +65,12 @@
                                              Long clinicId,
                                              Integer status,
                                              Integer isMember,
+                                             String memberStatus,
+                                             LocalDateTime memberExpireFrom,
+                                             LocalDateTime memberExpireTo,
                                              LocalDateTime registeredFrom,
                                              LocalDateTime registeredTo) {
+        String normalizedMemberStatus = normalizeMemberStatus(memberStatus);
         IPage<UserListRow> result = userMapper.selectUserPage(
                 new Page<UserListRow>(page, pageSize),
                 trimToNull(nickname),
@@ -70,6 +78,9 @@
                 clinicId,
                 status,
                 isMember,
+                normalizedMemberStatus,
+                memberExpireFrom,
+                memberExpireTo,
                 registeredFrom,
                 registeredTo
         );
@@ -85,6 +96,7 @@
             item.setAge(row.getAge());
             item.setGender(row.getGender());
             item.setIsMember(row.getIsMember() != null && row.getIsMember() == 1);
+            item.setMemberExpireAt(row.getMemberExpireAt());
             item.setStatus(row.getStatus());
             item.setCreatedAt(row.getCreatedAt());
             list.add(item);
@@ -213,12 +225,17 @@
                        Long clinicId,
                        Integer status,
                        Integer isMember,
+                       String memberStatus,
+                       LocalDateTime memberExpireFrom,
+                       LocalDateTime memberExpireTo,
                        LocalDateTime registeredFrom,
                        LocalDateTime registeredTo,
                        HttpServletResponse response) throws IOException {
+        String normalizedMemberStatus = normalizeMemberStatus(memberStatus);
+        boolean memberMode = normalizedMemberStatus != null;
         response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
         response.setCharacterEncoding("utf-8");
-        String filename = URLEncoder.encode("用户列表", "UTF-8").replace("+", "%20");
+        String filename = URLEncoder.encode(memberMode ? "会员用户列表" : "用户列表", "UTF-8").replace("+", "%20");
         response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + filename + ".xlsx");
 
         List<UserListRow> rows = userMapper.selectUserList(
@@ -227,9 +244,28 @@
                 clinicId,
                 status,
                 isMember,
+                normalizedMemberStatus,
+                memberExpireFrom,
+                memberExpireTo,
                 registeredFrom,
                 registeredTo
         );
+        if (memberMode) {
+            List<MemberUserExcelRow> memberExcelRows = new ArrayList<MemberUserExcelRow>();
+            for (UserListRow row : rows) {
+                MemberUserExcelRow excel = new MemberUserExcelRow();
+                excel.setId(row.getId());
+                excel.setNickname(row.getNickname());
+                excel.setPhone(PhoneMasker.mask(row.getPhone()));
+                excel.setMemberStatus(row.getIsMember() != null && row.getIsMember() == 1 ? "生效中" : "已失效");
+                excel.setMemberExpireAt(DateTimes.format(row.getMemberExpireAt()));
+                memberExcelRows.add(excel);
+            }
+            FastExcel.write(response.getOutputStream(), MemberUserExcelRow.class)
+                    .sheet("会员用户列表")
+                    .doWrite(memberExcelRows);
+            return;
+        }
         List<UserExcelRow> excelRows = new ArrayList<UserExcelRow>();
         for (UserListRow row : rows) {
             UserExcelRow excel = new UserExcelRow();
@@ -275,6 +311,18 @@
         return trimmed.isEmpty() ? null : trimmed;
     }
 
+    private String normalizeMemberStatus(String memberStatus) {
+        String normalized = trimToNull(memberStatus);
+        if (normalized == null) {
+            return null;
+        }
+        String upper = normalized.toUpperCase();
+        if (MEMBER_STATUS_ALL.equals(upper) || MEMBER_STATUS_ACTIVE.equals(upper) || MEMBER_STATUS_EXPIRED.equals(upper)) {
+            return upper;
+        }
+        throw new IllegalArgumentException("会员状态筛选不合法");
+    }
+
     private String typeText(Integer type) {
         return type != null && type == 1 ? "诊所" : "个人";
     }
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/vo/UserListItemVO.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/vo/UserListItemVO.java
index 2490bfa..8793060 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/vo/UserListItemVO.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/vo/UserListItemVO.java
@@ -19,6 +19,7 @@
     private Integer age;
     private Integer gender;
     private Boolean isMember;
+    private LocalDateTime memberExpireAt;
     private Integer status;
     private LocalDateTime createdAt;
 }
diff --git a/tcm-training-admin-backend/src/main/resources/mapper/UserMapper.xml b/tcm-training-admin-backend/src/main/resources/mapper/UserMapper.xml
index c9a2c54..2c0c23b 100644
--- a/tcm-training-admin-backend/src/main/resources/mapper/UserMapper.xml
+++ b/tcm-training-admin-backend/src/main/resources/mapper/UserMapper.xml
@@ -23,6 +23,21 @@
         <if test="isMember != null and isMember == 0">
             AND (s.member_expire_at IS NULL OR s.member_expire_at &lt;= NOW())
         </if>
+        <if test="memberStatus != null and memberStatus == 'ALL'">
+            AND s.member_expire_at IS NOT NULL
+        </if>
+        <if test="memberStatus != null and memberStatus == 'ACTIVE'">
+            AND s.member_expire_at IS NOT NULL AND s.member_expire_at &gt; NOW()
+        </if>
+        <if test="memberStatus != null and memberStatus == 'EXPIRED'">
+            AND s.member_expire_at IS NOT NULL AND s.member_expire_at &lt;= NOW()
+        </if>
+        <if test="memberExpireFrom != null">
+            AND s.member_expire_at &gt;= #{memberExpireFrom}
+        </if>
+        <if test="memberExpireTo != null">
+            AND s.member_expire_at &lt;= #{memberExpireTo}
+        </if>
         <if test="registeredFrom != null">
             AND s.created_at &gt;= #{registeredFrom}
         </if>
@@ -47,6 +62,7 @@
                 WHEN s.member_expire_at IS NOT NULL AND s.member_expire_at &gt; NOW() THEN 1
                 ELSE 0
             END AS isMember,
+            s.member_expire_at AS memberExpireAt,
             s.created_at AS createdAt
         FROM tcm_app_user s
         LEFT JOIN tcm_clinic c ON c.id = s.clinic_id AND c.deleted = 0
@@ -70,6 +86,7 @@
                 WHEN s.member_expire_at IS NOT NULL AND s.member_expire_at &gt; NOW() THEN 1
                 ELSE 0
             END AS isMember,
+            s.member_expire_at AS memberExpireAt,
             s.created_at AS createdAt
         FROM tcm_app_user s
         LEFT JOIN tcm_clinic c ON c.id = s.clinic_id AND c.deleted = 0
diff --git a/tcm-training-admin-backend/src/test/java/com/tcm/training/admin/member/service/UserAppServiceTest.java b/tcm-training-admin-backend/src/test/java/com/tcm/training/admin/member/service/UserAppServiceTest.java
new file mode 100644
index 0000000..3134748
--- /dev/null
+++ b/tcm-training-admin-backend/src/test/java/com/tcm/training/admin/member/service/UserAppServiceTest.java
@@ -0,0 +1,91 @@
+package com.tcm.training.admin.member.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.tcm.training.admin.common.PageResponse;
+import com.tcm.training.admin.live.mapper.ClinicMapper;
+import com.tcm.training.admin.member.mapper.UserMapper;
+import com.tcm.training.admin.member.row.UserListRow;
+import com.tcm.training.admin.member.vo.UserListItemVO;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+import java.time.LocalDateTime;
+import java.util.Collections;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class UserAppServiceTest {
+
+    @Mock
+    private UserMapper userMapper;
+
+    @Mock
+    private ClinicMapper clinicMapper;
+
+    @Mock
+    private BCryptPasswordEncoder passwordEncoder;
+
+    @InjectMocks
+    private UserAppService userAppService;
+
+    @Test
+    void pageShouldMapMemberExpireAtForMemberUserQuery() {
+        LocalDateTime memberExpireAt = LocalDateTime.of(2026, 6, 8, 3, 15);
+        UserListRow row = new UserListRow();
+        row.setId(11L);
+        row.setNickname("王美丽");
+        row.setPhone("14712346905");
+        row.setAvatar("https://cdn.test/avatar.png");
+        row.setIsMember(1);
+        row.setMemberExpireAt(memberExpireAt);
+
+        IPage<UserListRow> pageResult = new Page<UserListRow>(1, 10, 1);
+        pageResult.setRecords(Collections.singletonList(row));
+        when(userMapper.selectUserPage(
+                any(),
+                eq("王美丽"),
+                eq("14712346905"),
+                isNull(),
+                isNull(),
+                isNull(),
+                eq("ACTIVE"),
+                isNull(),
+                isNull(),
+                isNull(),
+                isNull()
+        )).thenReturn(pageResult);
+
+        PageResponse<UserListItemVO> result = userAppService.page(
+                1, 10, " 王美丽 ", "14712346905", null, null, null,
+                "active", null, null, null, null
+        );
+
+        assertEquals(1, result.getList().size());
+        assertEquals(memberExpireAt, result.getList().get(0).getMemberExpireAt());
+        assertEquals(Boolean.TRUE, result.getList().get(0).getIsMember());
+    }
+
+    @Test
+    void pageShouldRejectInvalidMemberStatus() {
+        IllegalArgumentException error = assertThrows(
+                IllegalArgumentException.class,
+                () -> userAppService.page(
+                        1, 10, null, null, null, null, null,
+                        "INVALID", null, null, null, null
+                )
+        );
+
+        assertEquals("会员状态筛选不合法", error.getMessage());
+    }
+}
diff --git a/tcm-training-admin/src/pages/member/user/list-page.tsx b/tcm-training-admin/src/pages/member/user/list-page.tsx
index b0174e3..bac77fd 100644
--- a/tcm-training-admin/src/pages/member/user/list-page.tsx
+++ b/tcm-training-admin/src/pages/member/user/list-page.tsx
@@ -19,6 +19,7 @@
 import './style.less';
 
 type UserListMode = 'all' | 'member';
+type MemberStatusValue = 'ALL' | 'ACTIVE' | 'EXPIRED';
 
 type SearchValues = {
   nickname?: string;
@@ -26,6 +27,8 @@
   clinicId?: number;
   status?: number;
   isMember?: number;
+  memberStatus?: MemberStatusValue;
+  memberExpireRange?: Dayjs[];
   registeredRange?: Dayjs[];
 };
 
@@ -39,6 +42,7 @@
   age?: number;
   gender?: number;
   isMember: boolean;
+  memberExpireAt?: string | null;
   status: number;
   createdAt: string;
 };
@@ -88,6 +92,21 @@
 
 const buildSearchParams = (values: SearchValues, mode: UserListMode) => {
   const config = PAGE_CONFIG[mode];
+  if (mode === 'member') {
+    const memberExpireFrom = values.memberExpireRange?.[0]
+      ? dayjs(values.memberExpireRange[0]).startOf('day').format('YYYY-MM-DD HH:mm:ss')
+      : undefined;
+    const memberExpireTo = values.memberExpireRange?.[1]
+      ? dayjs(values.memberExpireRange[1]).endOf('day').format('YYYY-MM-DD HH:mm:ss')
+      : undefined;
+    return {
+      nickname: values.nickname?.trim() || undefined,
+      phone: values.phone?.trim() || undefined,
+      memberStatus: values.memberStatus || 'ALL',
+      memberExpireFrom,
+      memberExpireTo,
+    };
+  }
   const registeredFrom = values.registeredRange?.[0]
     ? dayjs(values.registeredRange[0]).startOf('day').format('YYYY-MM-DD HH:mm:ss')
     : undefined;
@@ -103,6 +122,17 @@
     registeredFrom,
     registeredTo,
   };
+};
+
+const formatMemberStatus = (record: UserItem, mode: UserListMode) => {
+  if (mode === 'member') {
+    return record.isMember ? '生效中' : '已失效';
+  }
+  return record.isMember ? '是' : '否';
+};
+
+const formatMemberExpireAt = (value?: string | null) => {
+  return value ? dayjs(value).format('YYYY-MM-DD HH:mm') : '-';
 };
 
 const downloadBlob = (blob: Blob, filename: string) => {
@@ -231,55 +261,72 @@
       ),
     },
     {
-      title: '类型',
-      dataIndex: 'type',
-      width: 100,
-      render: (value) => (value === 1 ? '诊所' : '个人'),
-    },
-    {
       title: '电话',
       dataIndex: 'phone',
       width: 150,
     },
-    {
-      title: '绑定诊所',
-      dataIndex: 'clinicName',
-      width: 180,
-      render: (value) => value || '-',
-    },
-    {
-      title: '年龄',
-      dataIndex: 'age',
-      width: 90,
-      render: (value) => value ?? '-',
-    },
-    {
-      title: '性别',
-      dataIndex: 'gender',
-      width: 90,
-      render: (value) => (value === 1 ? '男' : value === 0 ? '女' : '-'),
-    },
-    ...(config.showIsMemberColumn
+    ...(mode === 'member'
       ? [
           {
-            title: '是否会员',
+            title: '会员状态',
             dataIndex: 'isMember',
-            width: 100,
-            render: (value: boolean) => (value ? '是' : '否'),
+            width: 120,
+            render: (_: boolean, record: UserItem) => formatMemberStatus(record, mode),
+          },
+          {
+            title: '到期时间',
+            dataIndex: 'memberExpireAt',
+            width: 180,
+            render: (value: string | null | undefined) => formatMemberExpireAt(value),
           },
         ]
-      : []),
-    {
-      title: '状态',
-      dataIndex: 'status',
-      width: 100,
-      render: (value) => (value === 1 ? '启用' : '禁用'),
-    },
-    {
-      title: '注册时间',
-      dataIndex: 'createdAt',
-      width: 180,
-    },
+      : [
+          {
+            title: '类型',
+            dataIndex: 'type',
+            width: 100,
+            render: (value: number) => (value === 1 ? '诊所' : '个人'),
+          },
+          {
+            title: '绑定诊所',
+            dataIndex: 'clinicName',
+            width: 180,
+            render: (value: string | undefined) => value || '-',
+          },
+          {
+            title: '年龄',
+            dataIndex: 'age',
+            width: 90,
+            render: (value: number | undefined) => value ?? '-',
+          },
+          {
+            title: '性别',
+            dataIndex: 'gender',
+            width: 90,
+            render: (value: number | undefined) => (value === 1 ? '男' : value === 0 ? '女' : '-'),
+          },
+          ...(config.showIsMemberColumn
+            ? [
+                {
+                  title: '是否会员',
+                  dataIndex: 'isMember',
+                  width: 100,
+                  render: (value: boolean) => (value ? '是' : '否'),
+                },
+              ]
+            : []),
+          {
+            title: '状态',
+            dataIndex: 'status',
+            width: 100,
+            render: (value: number) => (value === 1 ? '启用' : '禁用'),
+          },
+          {
+            title: '注册时间',
+            dataIndex: 'createdAt',
+            width: 180,
+          },
+        ]),
     {
       title: '操作',
       width: 140,
@@ -305,30 +352,54 @@
       <div className="member-user-page__panel">
         <div className="member-user-page__title">{config.title}</div>
         <div className="member-user-search-card">
-          <Form form={searchForm} layout="horizontal" className="member-user-search-form">
+          <Form
+            form={searchForm}
+            layout="horizontal"
+            className={`member-user-search-form${mode === 'member' ? ' member-user-search-form--member' : ''}`}
+          >
             <Form.Item label="用户名称" name="nickname">
               <Input placeholder="请输入" allowClear />
             </Form.Item>
             <Form.Item label="用户电话" name="phone">
               <Input placeholder="请输入" allowClear />
             </Form.Item>
-            <Form.Item label="绑定诊所" name="clinicId">
-              <Select
-                placeholder="请输入"
-                allowClear
-                options={clinics.map((item) => ({ label: item.name, value: item.id }))}
-              />
-            </Form.Item>
-            <Form.Item label="状态" name="status">
-              <Select
-                placeholder="请选择"
-                allowClear
-                options={[
-                  { label: '启用', value: 1 },
-                  { label: '禁用', value: 0 },
-                ]}
-              />
-            </Form.Item>
+            {mode === 'member' ? (
+              <Form.Item label="会员状态" name="memberStatus">
+                <Select
+                  placeholder="请选择"
+                  allowClear
+                  options={[
+                    { label: '全部', value: 'ALL' },
+                    { label: '生效中', value: 'ACTIVE' },
+                    { label: '已失效', value: 'EXPIRED' },
+                  ]}
+                />
+              </Form.Item>
+            ) : (
+              <Form.Item label="绑定诊所" name="clinicId">
+                <Select
+                  placeholder="请输入"
+                  allowClear
+                  options={clinics.map((item) => ({ label: item.name, value: item.id }))}
+                />
+              </Form.Item>
+            )}
+            {mode === 'member' ? (
+              <Form.Item label="到期时间" name="memberExpireRange">
+                <DatePicker.RangePicker className="member-user-search-form__range" />
+              </Form.Item>
+            ) : (
+              <Form.Item label="状态" name="status">
+                <Select
+                  placeholder="请选择"
+                  allowClear
+                  options={[
+                    { label: '启用', value: 1 },
+                    { label: '禁用', value: 0 },
+                  ]}
+                />
+              </Form.Item>
+            )}
             {config.showIsMemberFilter ? (
               <Form.Item label="是否会员" name="isMember">
                 <Select
@@ -341,9 +412,11 @@
                 />
               </Form.Item>
             ) : null}
-            <Form.Item label="注册时间" name="registeredRange">
-              <DatePicker.RangePicker className="member-user-search-form__range" />
-            </Form.Item>
+            {mode === 'member' ? null : (
+              <Form.Item label="注册时间" name="registeredRange">
+                <DatePicker.RangePicker className="member-user-search-form__range" />
+              </Form.Item>
+            )}
             <div className="member-user-search-form__actions">
               <Button
                 onClick={() => {
@@ -373,7 +446,7 @@
             columns={columns}
             dataSource={list}
             pagination={false}
-            scroll={{ x: 1320 }}
+            scroll={{ x: mode === 'member' ? 920 : 1320 }}
           />
           <div className="member-user-pagination">
             <Pagination
diff --git a/tcm-training-admin/src/pages/member/user/style.less b/tcm-training-admin/src/pages/member/user/style.less
index 589b2ef..49040e5 100644
--- a/tcm-training-admin/src/pages/member/user/style.less
+++ b/tcm-training-admin/src/pages/member/user/style.less
@@ -31,6 +31,10 @@
   align-items: end;
 }
 
+.member-user-search-form--member {
+  grid-template-columns: repeat(5, minmax(180px, 1fr));
+}
+
 .member-user-search-form .ant-form-item {
   width: 100%;
   margin-right: 0;
@@ -193,6 +197,10 @@
   .member-user-search-form {
     grid-template-columns: repeat(2, minmax(220px, 1fr));
   }
+
+  .member-user-search-form--member {
+    grid-template-columns: repeat(2, minmax(220px, 1fr));
+  }
 }
 
 @media (max-width: 768px) {

--
Gitblit v1.7.1