From c82a56eef79fef260745940dc9e06d5e4497a80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Fri, 8 Aug 2025 08:55:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E4=BC=98=E6=83=A0?= =?UTF-8?q?=E5=88=B8=E3=80=81=E7=A7=AF=E5=88=86=E6=98=8E=E7=BB=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/shop/shopGoodsReview/model/index.ts | 113 +++++++++++ src/api/user/balance-log/index.ts | 102 ++++++++++ src/api/user/balance-log/model/index.ts | 31 +++ src/api/user/coupon/index.ts | 78 +++++++ src/api/user/coupon/model/index.ts | 45 +++++ src/api/user/points/index.ts | 73 +++++++ src/api/user/points/model/index.ts | 49 +++++ src/app.config.ts | 5 +- src/pages/user/components/UserCard.tsx | 42 +++- src/user/coupon/coupon.ts | 4 + src/user/coupon/coupon.tsx | 213 ++++++++++++++++++++ src/user/points/points.config.ts | 4 + src/user/points/points.tsx | 199 ++++++++++++++++++ src/user/wallet/wallet.config.ts | 4 + src/user/wallet/wallet.scss | 0 src/user/wallet/wallet.tsx | 134 ++++++++++++ src/utils/common.ts | 14 ++ src/utils/server.ts | 4 +- 18 files changed, 1105 insertions(+), 9 deletions(-) create mode 100644 src/api/shop/shopGoodsReview/model/index.ts create mode 100644 src/api/user/balance-log/index.ts create mode 100644 src/api/user/balance-log/model/index.ts create mode 100644 src/api/user/coupon/index.ts create mode 100644 src/api/user/coupon/model/index.ts create mode 100644 src/api/user/points/index.ts create mode 100644 src/api/user/points/model/index.ts create mode 100644 src/user/coupon/coupon.ts create mode 100644 src/user/coupon/coupon.tsx create mode 100644 src/user/points/points.config.ts create mode 100644 src/user/points/points.tsx create mode 100644 src/user/wallet/wallet.config.ts create mode 100644 src/user/wallet/wallet.scss create mode 100644 src/user/wallet/wallet.tsx diff --git a/src/api/shop/shopGoodsReview/model/index.ts b/src/api/shop/shopGoodsReview/model/index.ts new file mode 100644 index 0000000..f851dde --- /dev/null +++ b/src/api/shop/shopGoodsReview/model/index.ts @@ -0,0 +1,113 @@ +import type { PageParam } from '@/api/index'; + +/** + * 商品评价 + */ +export interface ShopGoodsReview { + // 评价ID + reviewId?: number; + // 商品ID + goodsId?: number; + // 订单ID + orderId?: number; + // 用户ID + userId?: number; + // 用户昵称 + nickname?: string; + // 用户头像 + avatar?: string; + // 评价内容 + content?: string; + // 评分 1-5星 + rating?: number; + // 评价图片,JSON数组格式 + images?: string; + // 是否匿名评价 + isAnonymous?: boolean; + // 商家回复 + reply?: string; + // 商家回复时间 + replyTime?: string; + // 评价状态 0待审核 1已通过 2已拒绝 + status?: number; + // 是否置顶 + isTop?: boolean; + // 点赞数 + likeCount?: number; + // 创建时间 + createTime?: string; + // 更新时间 + updateTime?: string; + // 商品信息 + goodsName?: string; + goodsImage?: string; + goodsPrice?: string; + // SKU信息 + skuId?: number; + specInfo?: string; +} + +/** + * 评价统计 + */ +export interface ReviewStats { + // 总评价数 + totalCount: number; + // 好评数 + goodCount: number; + // 中评数 + mediumCount: number; + // 差评数 + badCount: number; + // 好评率 + goodRate: number; + // 平均评分 + avgRating: number; + // 各星级统计 + ratingStats: { + [key: number]: number; // 星级 -> 数量 + }; +} + +/** + * 评价查询参数 + */ +export interface ShopGoodsReviewParam extends PageParam { + // 商品ID + goodsId?: number; + // 用户ID + userId?: number; + // 订单ID + orderId?: number; + // 评分筛选 + rating?: number; + // 状态筛选 + status?: number; + // 是否有图片 + hasImages?: boolean; + // 排序方式 time:时间 rating:评分 like:点赞数 + sortBy?: string; + // 排序方向 asc:升序 desc:降序 + sortOrder?: string; +} + +/** + * 提交评价请求 + */ +export interface SubmitReviewRequest { + // 商品ID + goodsId: number; + // 订单ID + orderId: number; + // 评价内容 + content: string; + // 评分 + rating: number; + // 评价图片 + images?: string[]; + // 是否匿名 + isAnonymous?: boolean; + // SKU信息 + skuId?: number; + specInfo?: string; +} diff --git a/src/api/user/balance-log/index.ts b/src/api/user/balance-log/index.ts new file mode 100644 index 0000000..2a9f23f --- /dev/null +++ b/src/api/user/balance-log/index.ts @@ -0,0 +1,102 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api/index'; +import type { UserBalanceLog, UserBalanceLogParam } from './model'; +import {SERVER_API_URL} from "@/utils/server"; + +/** + * 分页查询余额明细 + */ +export async function pageUserBalanceLog(params: UserBalanceLogParam) { + const res = await request.get>>( + SERVER_API_URL + '/sys/user-balance-log/page', + params + ); + if (res.code === 0) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 查询余额明细列表 + */ +export async function listUserBalanceLog(params?: UserBalanceLogParam) { + const res = await request.get>( + SERVER_API_URL + '/sys/user-balance-log', + params + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 根据id查询余额明细 + */ +export async function getUserBalanceLog(id: number) { + const res = await request.get>( + SERVER_API_URL + '/sys/user-balance-log/' + id + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 添加余额明细 + */ +export async function addUserBalanceLog(data: UserBalanceLog) { + const res = await request.post>( + SERVER_API_URL + '/sys/user-balance-log', + data + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 修改余额明细 + */ +export async function updateUserBalanceLog(data: UserBalanceLog) { + const res = await request.put>( + SERVER_API_URL + '/sys/user-balance-log', + data + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 删除余额明细 + */ +export async function removeUserBalanceLog(id?: number) { + const res = await request.del>( + SERVER_API_URL + '/sys/user-balance-log/' + id + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 批量删除余额明细 + */ +export async function removeUserBalanceLogs(data: (number | undefined)[]) { + const res = await request.del>( + SERVER_API_URL + '/sys/user-balance-log/batch', + { + data + } + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/user/balance-log/model/index.ts b/src/api/user/balance-log/model/index.ts new file mode 100644 index 0000000..e2fc4ed --- /dev/null +++ b/src/api/user/balance-log/model/index.ts @@ -0,0 +1,31 @@ +import type { PageParam } from '@/api/index'; + +/** + * 余额明细 + */ +export interface UserBalanceLog { + logId?: number; + userId?: number; + scene?: number; + money?: string; + describe?: string; + remark?: string; + sortNumber?: number; + comments?: string; + status?: number; + deleted?: number; + tenantId?: number; + createTime?: string; + updateTime?: string; +} + +/** + * 用户搜索条件 + */ +export interface UserBalanceLogParam extends PageParam { + logId?: number; + userId?: number; + scene?: number; + createTimeStart?: string; + createTimeEnd?: string; +} diff --git a/src/api/user/coupon/index.ts b/src/api/user/coupon/index.ts new file mode 100644 index 0000000..a8e6483 --- /dev/null +++ b/src/api/user/coupon/index.ts @@ -0,0 +1,78 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api/index'; +import type { UserCoupon, UserCouponParam } from './model'; +import {SERVER_API_URL} from "@/utils/server"; + +/** + * 分页查询用户优惠券 + */ +export async function pageUserCoupon(params: UserCouponParam) { + const res = await request.get>>( + SERVER_API_URL + '/sys/user-coupon/page', + params + ); + if (res.code === 0) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 查询用户优惠券列表 + */ +export async function listUserCoupon(params?: UserCouponParam) { + const res = await request.get>( + SERVER_API_URL + '/sys/user-coupon', + params + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 获取用户优惠券统计 + */ +export async function getUserCouponCount(userId: number) { + const res = await request.get>( + SERVER_API_URL + '/sys/user-coupon/count', + { userId } + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 根据id查询用户优惠券 + */ +export async function getUserCoupon(id: number) { + const res = await request.get>( + SERVER_API_URL + '/sys/user-coupon/' + id + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 使用优惠券 + */ +export async function useCoupon(couponId: number, orderId: number) { + const res = await request.put>( + SERVER_API_URL + '/sys/user-coupon/use', + { couponId, orderId } + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/user/coupon/model/index.ts b/src/api/user/coupon/model/index.ts new file mode 100644 index 0000000..048ef62 --- /dev/null +++ b/src/api/user/coupon/model/index.ts @@ -0,0 +1,45 @@ +import type { PageParam } from '@/api/index'; + +/** + * 用户优惠券 + */ +export interface UserCoupon { + // 优惠券ID + couponId?: number; + // 用户ID + userId?: number; + // 优惠券名称 + name?: string; + // 优惠券类型 1-满减券 2-折扣券 3-免费券 + type?: number; + // 优惠券金额/折扣 + value?: string; + // 使用门槛金额 + minAmount?: string; + // 有效期开始时间 + startTime?: string; + // 有效期结束时间 + endTime?: string; + // 使用状态 0-未使用 1-已使用 2-已过期 + status?: number; + // 使用时间 + useTime?: string; + // 关联订单ID + orderId?: number; + // 备注 + comments?: string; + // 创建时间 + createTime?: string; + // 更新时间 + updateTime?: string; +} + +/** + * 用户优惠券搜索条件 + */ +export interface UserCouponParam extends PageParam { + userId?: number; + type?: number; + status?: number; + name?: string; +} diff --git a/src/api/user/points/index.ts b/src/api/user/points/index.ts new file mode 100644 index 0000000..0c64751 --- /dev/null +++ b/src/api/user/points/index.ts @@ -0,0 +1,73 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api/index'; +import type { UserPointsLog, UserPointsLogParam, UserPointsStats } from './model'; +import {SERVER_API_URL} from "@/utils/server"; + +/** + * 分页查询用户积分记录 + */ +export async function pageUserPointsLog(params: UserPointsLogParam) { + const res = await request.get>>( + SERVER_API_URL + '/sys/user-points-log/page', + params + ); + if (res.code === 0) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 查询用户积分记录列表 + */ +export async function listUserPointsLog(params?: UserPointsLogParam) { + const res = await request.get>( + SERVER_API_URL + '/sys/user-points-log', + params + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 获取用户积分统计 + */ +export async function getUserPointsStats(userId: number) { + const res = await request.get>( + SERVER_API_URL + '/sys/user-points-log/stats', + { userId } + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 根据id查询积分记录 + */ +export async function getUserPointsLog(id: number) { + const res = await request.get>( + SERVER_API_URL + '/sys/user-points-log/' + id + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 使用积分 + */ +export async function usePoints(userId: number, points: number, reason: string, orderId?: number) { + const res = await request.post>( + SERVER_API_URL + '/sys/user-points-log/use', + { userId, points, reason, orderId } + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/user/points/model/index.ts b/src/api/user/points/model/index.ts new file mode 100644 index 0000000..eccbace --- /dev/null +++ b/src/api/user/points/model/index.ts @@ -0,0 +1,49 @@ +import type { PageParam } from '@/api/index'; + +/** + * 用户积分记录 + */ +export interface UserPointsLog { + // 积分记录ID + logId?: number; + // 用户ID + userId?: number; + // 积分变动类型 1-获得 2-消费 3-过期 4-管理员调整 + type?: number; + // 积分变动数量 + points?: number; + // 变动原因 + reason?: string; + // 关联订单ID + orderId?: number; + // 备注 + comments?: string; + // 创建时间 + createTime?: string; + // 更新时间 + updateTime?: string; +} + +/** + * 用户积分统计 + */ +export interface UserPointsStats { + // 当前积分 + currentPoints?: number; + // 累计获得积分 + totalEarned?: number; + // 累计消费积分 + totalUsed?: number; + // 即将过期积分 + expiringSoon?: number; +} + +/** + * 用户积分搜索条件 + */ +export interface UserPointsLogParam extends PageParam { + userId?: number; + type?: number; + startTime?: string; + endTime?: string; +} diff --git a/src/app.config.ts b/src/app.config.ts index 4414b4d..054e82b 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -36,7 +36,10 @@ export default defineAppConfig({ "address/add", "address/wxAddress", "help/index", - "about/index" + "about/index", + "wallet/wallet", + "coupon/coupon", + "points/points" ] }, { diff --git a/src/pages/user/components/UserCard.tsx b/src/pages/user/components/UserCard.tsx index 2394f80..34b2ac7 100644 --- a/src/pages/user/components/UserCard.tsx +++ b/src/pages/user/components/UserCard.tsx @@ -6,11 +6,15 @@ import {useEffect, useState} from "react"; import {User} from "@/api/system/user/model"; import navTo from "@/utils/common"; import {TenantId} from "@/config/app"; +import {getUserCouponCount} from "@/api/user/coupon"; +import {getUserPointsStats} from "@/api/user/points"; function UserCard() { const [IsLogin, setIsLogin] = useState(false) const [userInfo, setUserInfo] = useState() const [roleName, setRoleName] = useState('注册用户') + const [couponCount, setCouponCount] = useState(0) + const [pointsCount, setPointsCount] = useState(0) useEffect(() => { @@ -30,6 +34,26 @@ function UserCard() { }); }, []); + const loadUserStats = (userId: number) => { + // 加载优惠券数量 + getUserCouponCount(userId) + .then((res: any) => { + setCouponCount(res.unused || 0) + }) + .catch((error: any) => { + console.error('Coupon count error:', error) + }) + + // 加载积分数量 + getUserPointsStats(userId) + .then((res: any) => { + setPointsCount(res.currentPoints || 0) + }) + .catch((error: any) => { + console.error('Points stats error:', error) + }) + } + const reload = () => { Taro.getUserInfo({ success: (res) => { @@ -44,6 +68,12 @@ function UserCard() { setUserInfo(data) setIsLogin(true); Taro.setStorageSync('UserId', data.userId) + + // 加载用户统计数据 + if (data.userId) { + loadUserStats(data.userId) + } + // 获取openId if (!data.openid) { Taro.login({ @@ -186,17 +216,17 @@ function UserCard() {
-
+
navTo('/user/wallet/wallet', true)}> 余额 - ¥ {userInfo?.balance} + ¥ {userInfo?.balance || '0.00'}
-
+
navTo('/user/coupon/coupon', true)}> 优惠券 - 0 + {couponCount}
-
+
navTo('/user/points/points', true)}> 积分 - 100 + {pointsCount}
diff --git a/src/user/coupon/coupon.ts b/src/user/coupon/coupon.ts new file mode 100644 index 0000000..3db94fc --- /dev/null +++ b/src/user/coupon/coupon.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '我的优惠券', + navigationBarTextStyle: 'black' +}) diff --git a/src/user/coupon/coupon.tsx b/src/user/coupon/coupon.tsx new file mode 100644 index 0000000..70cd554 --- /dev/null +++ b/src/user/coupon/coupon.tsx @@ -0,0 +1,213 @@ +import {useEffect, useState} from "react"; +import Taro from '@tarojs/taro' +import {Button, Cell, Space, Empty, ConfigProvider, Tabs, TabPane, Tag} from '@nutui/nutui-react-taro' +import {View} from '@tarojs/components' +import {pageUserCoupon, getUserCouponCount} from "@/api/user/coupon"; +import {UserCoupon as UserCouponType} from "@/api/user/coupon/model"; + +const UserCoupon = () => { + const [list, setList] = useState([]) + const [loading, setLoading] = useState(false) + const [activeTab, setActiveTab] = useState('0') + const [couponCount, setCouponCount] = useState({ + total: 0, + unused: 0, + used: 0, + expired: 0 + }) + + const tabs = [ + { key: '0', title: '全部', status: undefined }, + { key: '1', title: '未使用', status: 0 }, + { key: '2', title: '已使用', status: 1 }, + { key: '3', title: '已过期', status: 2 } + ] + + const reload = (status?: number) => { + setLoading(true) + const userId = Taro.getStorageSync('UserId') + + console.log('Loading coupons for userId:', userId, 'status:', status) + + if (!userId) { + console.warn('No userId found in storage') + Taro.showToast({ + title: '请先登录', + icon: 'error' + }); + setLoading(false) + return + } + + pageUserCoupon({ + userId: parseInt(userId), + status: status, + page: 1, + limit: 20 + }) + .then((res: any) => { + console.log('Coupon response:', res) + setList(res?.list || []) + }) + .catch((error: any) => { + console.error('Coupon error:', error) + Taro.showToast({ + title: error?.message || '获取失败', + icon: 'error' + }); + }) + .finally(() => { + setLoading(false) + }) + } + + const loadCouponCount = () => { + const userId = Taro.getStorageSync('UserId') + if (!userId) return + + getUserCouponCount(parseInt(userId)) + .then((res: any) => { + setCouponCount(res) + }) + .catch((error: any) => { + console.error('Coupon count error:', error) + }) + } + + useEffect(() => { + reload() + loadCouponCount() + }, []); + + const onTabChange = (index: string) => { + setActiveTab(index) + const tab = tabs.find(t => t.key === index) + reload(tab?.status) + } + + const getCouponTypeText = (type?: number) => { + switch (type) { + case 1: return '满减券' + case 2: return '折扣券' + case 3: return '免费券' + default: return '优惠券' + } + } + + const getCouponStatusText = (status?: number) => { + switch (status) { + case 0: return '未使用' + case 1: return '已使用' + case 2: return '已过期' + default: return '未知' + } + } + + const getCouponStatusColor = (status?: number) => { + switch (status) { + case 0: return 'success' + case 1: return 'default' + case 2: return 'danger' + default: return 'default' + } + } + + const formatCouponValue = (type?: number, value?: string) => { + if (!value) return '0' + switch (type) { + case 1: return `¥${value}` + case 2: return `${parseFloat(value) * 10}折` + case 3: return '免费' + default: return value + } + } + + if (loading) { + return ( + +
+
加载中...
+
+
+ ) + } + + if (list.length == 0) { + return ( + +
+ + + + +
+
+ ) + } + + return ( + + + + {tabs.map(tab => ( + + + {list.map((item, index) => ( + + + + + + + {formatCouponValue(item.type, item.value)} + + + + {item.name || getCouponTypeText(item.type)} + + {item.minAmount && parseFloat(item.minAmount) > 0 && ( + + 满¥{item.minAmount}可用 + + )} + + + + + + 有效期: {item.startTime ? new Date(item.startTime).toLocaleDateString() : ''} - {item.endTime ? new Date(item.endTime).toLocaleDateString() : ''} + + + {getCouponStatusText(item.status)} + + + + {item.comments && ( + + {item.comments} + + )} + + + + + ))} + + + ))} + + + + ); +}; + +export default UserCoupon; diff --git a/src/user/points/points.config.ts b/src/user/points/points.config.ts new file mode 100644 index 0000000..7fb10f8 --- /dev/null +++ b/src/user/points/points.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '我的积分', + navigationBarTextStyle: 'black' +}) diff --git a/src/user/points/points.tsx b/src/user/points/points.tsx new file mode 100644 index 0000000..da1c30b --- /dev/null +++ b/src/user/points/points.tsx @@ -0,0 +1,199 @@ +import {useEffect, useState} from "react"; +import Taro from '@tarojs/taro' +import {Button, Cell, Space, Empty, ConfigProvider, Card} from '@nutui/nutui-react-taro' +import {View} from '@tarojs/components' +import {pageUserPointsLog, getUserPointsStats} from "@/api/user/points"; +import {UserPointsLog as UserPointsLogType, UserPointsStats} from "@/api/user/points/model"; + +const UserPoints = () => { + const [list, setList] = useState([]) + const [loading, setLoading] = useState(false) + const [stats, setStats] = useState({}) + + const reload = () => { + setLoading(true) + const userId = Taro.getStorageSync('UserId') + + console.log('Loading points log for userId:', userId) + + if (!userId) { + console.warn('No userId found in storage') + Taro.showToast({ + title: '请先登录', + icon: 'error' + }); + setLoading(false) + return + } + + pageUserPointsLog({ + userId: parseInt(userId), + page: 1, + limit: 20 + }) + .then((res: any) => { + console.log('Points log response:', res) + setList(res?.list || []) + }) + .catch((error: any) => { + console.error('Points log error:', error) + Taro.showToast({ + title: error?.message || '获取失败', + icon: 'error' + }); + }) + .finally(() => { + setLoading(false) + }) + } + + const loadPointsStats = () => { + const userId = Taro.getStorageSync('UserId') + if (!userId) return + + getUserPointsStats(parseInt(userId)) + .then((res: any) => { + setStats(res) + }) + .catch((error: any) => { + console.error('Points stats error:', error) + }) + } + + useEffect(() => { + reload() + loadPointsStats() + }, []); + + const getPointsTypeText = (type?: number) => { + switch (type) { + case 1: return '获得积分' + case 2: return '消费积分' + case 3: return '积分过期' + case 4: return '管理员调整' + default: return '积分变动' + } + } + + const getPointsTypeColor = (type?: number) => { + switch (type) { + case 1: return 'text-green-500' + case 2: return 'text-red-500' + case 3: return 'text-gray-500' + case 4: return 'text-blue-500' + default: return 'text-gray-500' + } + } + + if (loading) { + return ( + +
+
加载中...
+
+
+ ) + } + + return ( + + + {/* 积分统计卡片 */} + + + + + {stats.currentPoints || 0} + + 当前积分 + + + + + {stats.totalEarned || 0} + + 累计获得 + + + + {stats.totalUsed || 0} + + 累计消费 + + + + {stats.expiringSoon || 0} + + 即将过期 + + + + + + + {/* 积分记录 */} + + 积分明细 + + {list.length === 0 ? ( +
+ + + + +
+ ) : ( + list.map((item, index) => ( + + + + + + {getPointsTypeText(item.type)} + + + {item.reason || '无备注'} + + + + {item.type === 1 ? '+' : item.type === 2 ? '-' : ''} + {item.points || 0} + + + + + + {item.createTime ? new Date(item.createTime).toLocaleString() : ''} + + {item.orderId && ( + + 订单: {item.orderId} + + )} + + + {item.comments && ( + + 备注: {item.comments} + + )} + + + )) + )} +
+
+
+ ); +}; + +export default UserPoints; diff --git a/src/user/wallet/wallet.config.ts b/src/user/wallet/wallet.config.ts new file mode 100644 index 0000000..2081aba --- /dev/null +++ b/src/user/wallet/wallet.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '余额明细', + navigationBarTextStyle: 'black' +}) diff --git a/src/user/wallet/wallet.scss b/src/user/wallet/wallet.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/user/wallet/wallet.tsx b/src/user/wallet/wallet.tsx new file mode 100644 index 0000000..1ecf44d --- /dev/null +++ b/src/user/wallet/wallet.tsx @@ -0,0 +1,134 @@ +import {useEffect, useState} from "react"; +import Taro from '@tarojs/taro' +import {Button, Cell, Space, Empty, ConfigProvider, InfiniteLoading} from '@nutui/nutui-react-taro' +import {View, ScrollView} from '@tarojs/components' +import {pageUserBalanceLog} from "@/api/user/balance-log"; +import {UserBalanceLog} from "@/api/user/balance-log/model"; +import {formatCurrency} from "@/utils/common"; + +const Wallet = () => { + const [list, setList] = useState([]) + const [loading, setLoading] = useState(false) + const [loadingMore, setLoadingMore] = useState(false) + const [refreshing, setRefreshing] = useState(false) + const [hasMore, setHasMore] = useState(true) + const [currentPage, setCurrentPage] = useState(1) + const [total, setTotal] = useState(0) + const pageSize = 20 + + const reload = () => { + setLoading(true) + const userId = Taro.getStorageSync('UserId') + + console.log('Loading balance log for userId:', userId) + + if (!userId) { + console.warn('No userId found in storage') + Taro.showToast({ + title: '请先登录', + icon: 'error' + }); + setLoading(false) + return + } + + pageUserBalanceLog({ + userId: parseInt(userId), + page: 1, + limit: 20 + }) + .then((res: any) => { + console.log('Balance log response:', res) + setList(res?.list || []) + }) + .catch((error: any) => { + console.error('Balance log error:', error) + Taro.showToast({ + title: error?.message || '获取失败', + icon: 'error' + }); + }) + .finally(() => { + setLoading(false) + }) + } + + useEffect(() => { + reload() + }, []); + + if (loading) { + return ( + +
+
加载中...
+
+
+ ) + } + + if (list.length == 0) { + return ( + +
+ + + + +
+
+ ) + } + + return ( + + + {list.map((item, index) => ( + + + + + + {item.scene === 10 ? '会员充值' : item.scene === 20 ? '用户消费' : item.scene === 30 ? '管理员操作' : '订单退款'} + + + {item.comments} + + + + {item.scene === 10 ? '+' : '-'} + {formatCurrency(Number(item.money), 'CNY') || '0.00'} + + + + + + {item.createTime} + + + + {item.remark && ( + + 备注: {item.remark} + + )} + + + ))} + + + ); +}; + +export default Wallet; diff --git a/src/utils/common.ts b/src/utils/common.ts index 2c67602..16c379d 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -108,6 +108,20 @@ export function truncateText(text: string, maxLength: number = 30): string { return text.substring(0, maxLength); } +/** + * 格式化货币 + * @param amount + * @param currency + */ +export function formatCurrency(amount: number, currency: string = 'CNY'): string { + return new Intl.NumberFormat('zh-CN', { + style: 'currency', + currency: currency, + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }).format(amount); +} + /** * 生成订单标题 * @param goodsNames 商品名称数组 diff --git a/src/utils/server.ts b/src/utils/server.ts index fb48f69..8cd39ba 100644 --- a/src/utils/server.ts +++ b/src/utils/server.ts @@ -4,8 +4,8 @@ import {User} from "@/api/system/user/model"; // 模版套餐ID - 请根据实际情况修改 export const TEMPLATE_ID = '10550'; // 服务接口 - 请根据实际情况修改 -export const SERVER_API_URL = 'https://server.websoft.top/api'; -// export const SERVER_API_URL = 'http://127.0.0.1:8000/api'; +// export const SERVER_API_URL = 'https://server.websoft.top/api'; +export const SERVER_API_URL = 'http://127.0.0.1:8000/api'; /** * 保存用户信息到本地存储 * @param token