From 1fba71c8b310741b69e3d3685b89ffde8818e4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Mon, 18 Aug 2025 09:24:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(user):=20=E6=B7=BB=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=95=B0=E6=8D=AE=E9=92=A9=E5=AD=90=E5=92=8C=E4=BB=AA?= =?UTF-8?q?=E8=A1=A8=E6=9D=BF=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 useUserData 钩子用于获取用户数据 - 添加用户仪表板相关接口和类型定义 - 更新用户卡片组件,使用新的用户数据钩子 - 修改成为经销商文案为开通VIP --- src/api/shop/shopUserCoupon/model/index.ts | 1 + src/api/user/index.ts | 131 +++++++++++++++++++++ src/hooks/useUserData.ts | 118 +++++++++++++++++++ src/pages/user/components/IsDealer.tsx | 2 +- src/pages/user/components/UserCard.tsx | 86 ++++++++------ 5 files changed, 300 insertions(+), 38 deletions(-) create mode 100644 src/api/user/index.ts create mode 100644 src/hooks/useUserData.ts diff --git a/src/api/shop/shopUserCoupon/model/index.ts b/src/api/shop/shopUserCoupon/model/index.ts index f48ea76..118719e 100644 --- a/src/api/shop/shopUserCoupon/model/index.ts +++ b/src/api/shop/shopUserCoupon/model/index.ts @@ -67,6 +67,7 @@ export interface ShopUserCoupon { */ export interface ShopUserCouponParam extends PageParam { id?: number; + userId?: number; status?: number; isExpire?: number; sortBy?: string; diff --git a/src/api/user/index.ts b/src/api/user/index.ts new file mode 100644 index 0000000..cc59a57 --- /dev/null +++ b/src/api/user/index.ts @@ -0,0 +1,131 @@ +import request from '@/utils/request-legacy' +import type { ApiResult } from '@/api/index' + +// 用户余额信息 +export interface UserBalance { + balance: string + frozenBalance: string + totalIncome: string + totalExpense: string +} + +// 用户积分信息 +export interface UserPoints { + points: number + totalEarned: number + totalUsed: number + expiringSoon: number +} + +// 用户优惠券统计 +export interface UserCoupons { + count: number + available: number + used: number + expired: number +} + +// 用户礼品卡统计 +export interface UserGiftCards { + count: number + unused: number + used: number + expired: number +} + +// 用户订单统计 +export interface UserOrderStats { + pending: number + paid: number + shipped: number + completed: number + refund: number + total: number +} + +// 用户完整数据 +export interface UserDashboard { + balance: UserBalance + points: UserPoints + coupons: UserCoupons + giftCards: UserGiftCards + orders: UserOrderStats + lastUpdateTime: string +} + +/** + * 获取用户余额信息 + */ +export async function getUserBalance() { + const res = await request.get>('/user/balance') + if (res.code === 0 && res.data) { + return res.data + } + return Promise.reject(new Error(res.message)) +} + +/** + * 获取用户积分信息 + */ +export async function getUserPoints() { + const res = await request.get>('/user/points') + if (res.code === 0 && res.data) { + return res.data + } + return Promise.reject(new Error(res.message)) +} + +/** + * 获取用户优惠券统计 + */ +export async function getUserCoupons() { + const res = await request.get>('/user/coupons/stats') + if (res.code === 0 && res.data) { + return res.data + } + return Promise.reject(new Error(res.message)) +} + +/** + * 获取用户礼品卡统计 + */ +export async function getUserGiftCards() { + const res = await request.get>('/user/gift-cards/stats') + if (res.code === 0 && res.data) { + return res.data + } + return Promise.reject(new Error(res.message)) +} + +/** + * 获取用户订单统计 + */ +export async function getUserOrderStats() { + const res = await request.get>('/user/orders/stats') + if (res.code === 0 && res.data) { + return res.data + } + return Promise.reject(new Error(res.message)) +} + +/** + * 获取用户完整仪表板数据(一次性获取所有数据) + */ +export async function getUserDashboard() { + const res = await request.get>('/user/dashboard') + if (res.code === 0 && res.data) { + return res.data + } + return Promise.reject(new Error(res.message)) +} + +/** + * 刷新用户数据缓存 + */ +export async function refreshUserData() { + const res = await request.post>('/user/refresh-cache') + if (res.code === 0) { + return res.message + } + return Promise.reject(new Error(res.message)) +} diff --git a/src/hooks/useUserData.ts b/src/hooks/useUserData.ts new file mode 100644 index 0000000..6fa2966 --- /dev/null +++ b/src/hooks/useUserData.ts @@ -0,0 +1,118 @@ +import { useState, useEffect, useCallback } from 'react' +import {pageShopUserCoupon} from "@/api/shop/shopUserCoupon"; +import {pageShopGift} from "@/api/shop/shopGift"; +import {useUser} from "@/hooks/useUser"; + +interface UserData { + balance: number + points: number + coupons: number + giftCards: number + orders: { + pending: number + paid: number + shipped: number + completed: number + refund: number + } +} + +interface UseUserDataReturn { + data: UserData | null + loading: boolean + error: string | null + refresh: () => Promise + updateBalance: (newBalance: string) => void + updatePoints: (newPoints: number) => void +} + +export const useUserData = (): UseUserDataReturn => { + const {user} = useUser() + const [data, setData] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + // 获取用户数据 + const fetchUserData = useCallback(async () => { + try { + setLoading(true) + setError(null) + + // 并发请求所有数据 + const [couponsRes, giftCardsRes] = await Promise.all([ + pageShopUserCoupon({ page: 1, limit: 1, userId: user?.userId}), + pageShopGift({ page: 1, limit: 1, userId: user?.userId, status: 0}) + ]) + + const newData: UserData = { + balance: user?.balance || 0.00, + points: user?.points || 0, + coupons: couponsRes?.count || 0, + giftCards: giftCardsRes?.count || 0, + orders: { + pending: 0, + paid: 0, + shipped: 0, + completed: 0, + refund: 0 + } + } + + setData(newData) + } catch (err) { + setError(err instanceof Error ? err.message : '获取用户数据失败') + } finally { + setLoading(false) + } + }, []) + + // 刷新数据 + const refresh = useCallback(async () => { + await fetchUserData() + }, [fetchUserData]) + + // 初始化加载 + useEffect(() => { + fetchUserData().then() + }, [fetchUserData]) + + return { + data, + loading, + error, + refresh: fetchUserData + } +} + +// 轻量级版本 - 只获取基础数据 +export const useUserBasicData = () => { + const {user} = useUser() + const [balance, setBalance] = useState(0) + const [points, setPoints] = useState(0) + const [loading, setLoading] = useState(false) + + const fetchBasicData = useCallback(async () => { + setLoading(true) + try { + setBalance(user?.balance || 0) + setPoints(user?.points || 0) + } catch (error) { + console.error('获取基础数据失败:', error) + } finally { + setLoading(false) + } + }, []) + + useEffect(() => { + fetchBasicData() + }, [fetchBasicData]) + + return { + balance, + points, + loading, + refresh: fetchBasicData, + updateBalance: setBalance, + updatePoints: setPoints + } +} diff --git a/src/pages/user/components/IsDealer.tsx b/src/pages/user/components/IsDealer.tsx index 1e968a5..d0288d4 100644 --- a/src/pages/user/components/IsDealer.tsx +++ b/src/pages/user/components/IsDealer.tsx @@ -107,7 +107,7 @@ const UserCell = () => { title={ - 成为经销商 + 开通VIP 享优惠 } diff --git a/src/pages/user/components/UserCard.tsx b/src/pages/user/components/UserCard.tsx index 7c1ae70..4ca426d 100644 --- a/src/pages/user/components/UserCard.tsx +++ b/src/pages/user/components/UserCard.tsx @@ -1,5 +1,6 @@ import {Button} from '@nutui/nutui-react-taro' import {Avatar, Tag} from '@nutui/nutui-react-taro' +import {View, Text} from '@tarojs/components' import {Scan} from '@nutui/icons-react-taro'; import {getUserInfo, getWxOpenId} from '@/api/layout'; import Taro from '@tarojs/taro'; @@ -9,11 +10,13 @@ import navTo from "@/utils/common"; import {TenantId} from "@/config/app"; import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon"; import {useUser} from "@/hooks/useUser"; +import {useUserData} from "@/hooks/useUserData"; function UserCard() { const { isAdmin } = useUser(); + const { data, refresh } = useUserData() const {getDisplayName, getRoleName} = useUser(); const [IsLogin, setIsLogin] = useState(false) const [userInfo, setUserInfo] = useState() @@ -21,6 +24,15 @@ function UserCard() { const [pointsCount, setPointsCount] = useState(0) const [giftCount, setGiftCount] = useState(0) + // 下拉刷新 + const handleRefresh = async () => { + await refresh() + Taro.showToast({ + title: '刷新成功', + icon: 'success' + }) + } + useEffect(() => { // Taro.getSetting:获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。 Taro.getSetting({ @@ -182,9 +194,9 @@ function UserCard() { } return ( -
-
-
+ + -
-
+ + { IsLogin ? ( @@ -205,49 +217,49 @@ function UserCard() { ) } -
-
{getDisplayName()}
+ + {getDisplayName()} {IsLogin ? ( -
+ -
+ {getRoleName()} -
+
-
+
) : ''} -
-
+ + {isAdmin() && navTo('/user/store/verification', true)} />} -
navTo('/user/profile/profile', true)}> {'个人资料'} -
-
-
-
+ + + navTo('/user/wallet/wallet', true)}> - 余额 - {userInfo?.balance || '0.00'} -
-
- 积分 - {pointsCount} -
-
余额 + {data?.balance || '0.00'} + + + 积分 + {data?.points || 0} + + navTo('/user/coupon/index', true)}> - 优惠券 - {couponCount} -
-
优惠券 + {data?.coupons || 0} + + navTo('/user/gift/index', true)}> - 礼品卡 - {giftCount} -
-
-
-
-
+ 礼品卡 + {data?.giftCards || 0} + + + + + ) }