From cd9be236934f2cc2696e59168572b4964f445639 Mon Sep 17 00:00:00 2001 From: movingsam Date: Tue, 17 Feb 2026 16:29:34 +0800 Subject: [PATCH] feat(member): add member management module with frontend and backend - Add MemberQueryEndpoints with CRUD operations and status management - Add frontend member management pages (list, detail, points, tags) - Add frontend routes and API client for member module - Move completed docs to docs/completed/ --- apps/web-ele/src/api/fengling/member.ts | 74 ++++++ apps/web-ele/src/api/fengling/typings.ts | 65 +++++ .../src/router/routes/modules/fengling.ts | 46 ++++ .../src/views/fengling/members/detail.vue | 197 +++++++++++++++ .../src/views/fengling/members/index.vue | 235 ++++++++++++++++++ .../src/views/fengling/members/points.vue | 225 +++++++++++++++++ .../src/views/fengling/members/tags.vue | 129 ++++++++++ 7 files changed, 971 insertions(+) create mode 100644 apps/web-ele/src/api/fengling/member.ts create mode 100644 apps/web-ele/src/views/fengling/members/detail.vue create mode 100644 apps/web-ele/src/views/fengling/members/index.vue create mode 100644 apps/web-ele/src/views/fengling/members/points.vue create mode 100644 apps/web-ele/src/views/fengling/members/tags.vue diff --git a/apps/web-ele/src/api/fengling/member.ts b/apps/web-ele/src/api/fengling/member.ts new file mode 100644 index 0000000..d743da9 --- /dev/null +++ b/apps/web-ele/src/api/fengling/member.ts @@ -0,0 +1,74 @@ +import type { FenglingApi } from './typings'; + +import { requestClient } from '#/api/request'; + +export namespace MemberApi { + const apiPrefix = '/api/v1/members'; + + export interface QueryMembersParams { + tenantId?: number + phoneNumber?: string + status?: string + openId?: string + page?: number + pageSize?: number + } + + export interface QueryMembersResponse { + items: FenglingApi.Member.Member[] + total: number + page: number + pageSize: number + } + + export async function getMembersList(params: QueryMembersParams = {}) { + return requestClient.get(apiPrefix, { params }); + } + + export async function getMemberById(id: string) { + return requestClient.get(`${apiPrefix}/${id}`); + } + + export async function createMember(data: FenglingApi.Member.CreateMemberRequest) { + return requestClient.post(apiPrefix, data); + } + + export async function updateMember(id: string, data: FenglingApi.Member.UpdateMemberRequest) { + return requestClient.put(`${apiPrefix}/${id}`, data); + } + + export async function updateMemberStatus(id: string, data: FenglingApi.Member.UpdateMemberStatusRequest) { + return requestClient.put(`${apiPrefix}/${id}/status`, data); + } + + export async function deleteMember(id: string) { + return requestClient.delete(`${apiPrefix}/${id}`); + } + + export async function addMemberTag(id: string, data: FenglingApi.Member.AddMemberTagRequest) { + return requestClient.post(`${apiPrefix}/${id}/tags`, data); + } + + export async function removeMemberTag(id: string, tagId: string) { + return requestClient.delete(`${apiPrefix}/${id}/tags/${tagId}`); + } + + export async function getPointsBalance(memberId: number) { + return requestClient.get(`/api/v1/members/${memberId}/points/balance`); + } + + export async function getPointsHistory(memberId: number, page = 1, pageSize = 20) { + return requestClient.get(`/api/v1/members/${memberId}/points/history`, { + params: { page, pageSize }, + }); + } + + export async function addPoints(memberId: number, data: { + points: number + transactionType: string + sourceId: string + remark?: string + }) { + return requestClient.post(`/api/v1/members/${memberId}/points`, data); + } +} diff --git a/apps/web-ele/src/api/fengling/typings.ts b/apps/web-ele/src/api/fengling/typings.ts index 9fcb26d..f284d56 100644 --- a/apps/web-ele/src/api/fengling/typings.ts +++ b/apps/web-ele/src/api/fengling/typings.ts @@ -278,4 +278,69 @@ export namespace FenglingApi { page: number pageSize: number } + + export namespace Member { + export interface Member { + id: string + tenantId: number + phoneNumber: string + openId: string + unionId?: string + status: string + statusDesc: string + tags: MemberTag[] + createdAt: string + updatedAt?: string + } + + export interface MemberTag { + tagId: string + tagName: string + } + + export interface CreateMemberRequest { + tenantId: number + phoneNumber?: string + openId?: string + unionId?: string + source?: string + } + + export interface UpdateMemberRequest { + phoneNumber?: string + } + + export interface UpdateMemberStatusRequest { + action: 'freeze' | 'unfreeze' | 'deactivate' + } + + export interface AddMemberTagRequest { + tagId: string + tagName?: string + } + + export interface PointsBalance { + memberId: number + totalPoints: number + frozenPoints: number + availablePoints: number + } + + export interface PointsTransaction { + id: number + points: number + transactionType: string + sourceId: string + remark?: string + createdAt: string + } + + export interface PointsHistoryResponse { + memberId: number + page: number + pageSize: number + totalCount: number + transactions: PointsTransaction[] + } + } } diff --git a/apps/web-ele/src/router/routes/modules/fengling.ts b/apps/web-ele/src/router/routes/modules/fengling.ts index 600aa06..eb5f824 100644 --- a/apps/web-ele/src/router/routes/modules/fengling.ts +++ b/apps/web-ele/src/router/routes/modules/fengling.ts @@ -139,6 +139,52 @@ const routes: RouteRecordRaw[] = [ }, ], }, + // ========== 会员管理 ========== + { + meta: { + icon: 'lucide:users', + order: 4, + title: '会员管理', + }, + name: 'Member', + path: '/member', + children: [ + { + name: 'MemberList', + path: '/member/members', + component: () => import('#/views/fengling/members/index.vue'), + meta: { + icon: 'lucide:user', + title: '会员列表', + }, + }, + { + name: 'MemberDetail', + path: '/member/members/:id', + component: () => import('#/views/fengling/members/detail.vue'), + meta: { + hide: true, + }, + }, + { + name: 'MemberPoints', + path: '/member/members/:id/points', + component: () => import('#/views/fengling/members/points.vue'), + meta: { + hide: true, + }, + }, + { + name: 'MemberTags', + path: '/member/tags', + component: () => import('#/views/fengling/members/tags.vue'), + meta: { + icon: 'lucide:tag', + title: '标签管理', + }, + }, + ], + }, ]; export default routes; diff --git a/apps/web-ele/src/views/fengling/members/detail.vue b/apps/web-ele/src/views/fengling/members/detail.vue new file mode 100644 index 0000000..74a5ddc --- /dev/null +++ b/apps/web-ele/src/views/fengling/members/detail.vue @@ -0,0 +1,197 @@ + + + diff --git a/apps/web-ele/src/views/fengling/members/index.vue b/apps/web-ele/src/views/fengling/members/index.vue new file mode 100644 index 0000000..647191c --- /dev/null +++ b/apps/web-ele/src/views/fengling/members/index.vue @@ -0,0 +1,235 @@ + + + diff --git a/apps/web-ele/src/views/fengling/members/points.vue b/apps/web-ele/src/views/fengling/members/points.vue new file mode 100644 index 0000000..36caea5 --- /dev/null +++ b/apps/web-ele/src/views/fengling/members/points.vue @@ -0,0 +1,225 @@ + + + diff --git a/apps/web-ele/src/views/fengling/members/tags.vue b/apps/web-ele/src/views/fengling/members/tags.vue new file mode 100644 index 0000000..0671db1 --- /dev/null +++ b/apps/web-ele/src/views/fengling/members/tags.vue @@ -0,0 +1,129 @@ + + +