9 changed files with 431 additions and 325 deletions
@ -0,0 +1,65 @@ |
|||||
|
import type { PageParam } from '@/api/index'; |
||||
|
|
||||
|
/** |
||||
|
* 优惠券 |
||||
|
*/ |
||||
|
export interface ShopCoupon { |
||||
|
// id
|
||||
|
id?: number; |
||||
|
// 优惠券名称
|
||||
|
name?: string; |
||||
|
// 优惠券描述
|
||||
|
description?: string; |
||||
|
// 优惠券类型(10满减券 20折扣券 30免费劵)
|
||||
|
type?: number; |
||||
|
// 满减券-减免金额
|
||||
|
reducePrice?: string; |
||||
|
// 折扣券-折扣率(0-100)
|
||||
|
discount?: number; |
||||
|
// 最低消费金额
|
||||
|
minPrice?: string; |
||||
|
// 到期类型(10领取后生效 20固定时间)
|
||||
|
expireType?: number; |
||||
|
// 领取后生效-有效天数
|
||||
|
expireDay?: number; |
||||
|
// 有效期开始时间
|
||||
|
startTime?: string; |
||||
|
// 有效期结束时间
|
||||
|
endTime?: string; |
||||
|
// 适用范围(10全部商品 20指定商品 30指定分类)
|
||||
|
applyRange?: number; |
||||
|
// 适用范围配置(json格式)
|
||||
|
applyRangeConfig?: string; |
||||
|
// 是否过期(0未过期 1已过期)
|
||||
|
isExpire?: number; |
||||
|
// 排序(数字越小越靠前)
|
||||
|
sortNumber?: number; |
||||
|
// 状态, 0正常, 1禁用
|
||||
|
status?: number; |
||||
|
// 是否删除, 0否, 1是
|
||||
|
deleted?: number; |
||||
|
// 创建用户ID
|
||||
|
userId?: number; |
||||
|
// 租户id
|
||||
|
tenantId?: number; |
||||
|
// 创建时间
|
||||
|
createTime?: string; |
||||
|
// 修改时间
|
||||
|
updateTime?: string; |
||||
|
// 发放总数量(-1表示无限制)
|
||||
|
totalCount?: number; |
||||
|
// 已发放数量
|
||||
|
issuedCount?: number; |
||||
|
// 每人限领数量(-1表示无限制)
|
||||
|
limitPerUser?: number; |
||||
|
// 是否启用(0禁用 1启用)
|
||||
|
enabled?: string; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 优惠券搜索条件 |
||||
|
*/ |
||||
|
export interface ShopCouponParam extends PageParam { |
||||
|
id?: number; |
||||
|
keywords?: string; |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
import type { PageParam } from '@/api/index'; |
||||
|
|
||||
|
/** |
||||
|
* 商品优惠券表 |
||||
|
*/ |
||||
|
export interface ShopGoodsCoupon { |
||||
|
//
|
||||
|
id?: number; |
||||
|
// 商品id
|
||||
|
goodsId?: number; |
||||
|
// 优惠劵id
|
||||
|
issueCouponId?: number; |
||||
|
// 排序(数字越小越靠前)
|
||||
|
sortNumber?: number; |
||||
|
// 状态, 0正常, 1冻结
|
||||
|
status?: number; |
||||
|
// 是否删除, 0否, 1是
|
||||
|
deleted?: number; |
||||
|
// 用户ID
|
||||
|
userId?: number; |
||||
|
// 租户id
|
||||
|
tenantId?: number; |
||||
|
// 注册时间
|
||||
|
createTime?: string; |
||||
|
// 修改时间
|
||||
|
updateTime?: string; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 商品优惠券表搜索条件 |
||||
|
*/ |
||||
|
export interface ShopGoodsCouponParam extends PageParam { |
||||
|
id?: number; |
||||
|
keywords?: string; |
||||
|
} |
@ -1,134 +1,105 @@ |
|||||
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 {useState, useEffect, CSSProperties} from 'react' |
||||
|
import {Cell, InfiniteLoading} from '@nutui/nutui-react-taro' |
||||
import {pageUserBalanceLog} from "@/api/user/balance-log"; |
import {pageUserBalanceLog} from "@/api/user/balance-log"; |
||||
import {UserBalanceLog} from "@/api/user/balance-log/model"; |
import {UserBalanceLog} from "@/api/user/balance-log/model"; |
||||
import {formatCurrency} from "@/utils/common"; |
import {formatCurrency} from "@/utils/common"; |
||||
|
import {View} from '@tarojs/components' |
||||
|
|
||||
|
const InfiniteUlStyle: CSSProperties = { |
||||
|
height: '100vh', |
||||
|
width: '100%', |
||||
|
padding: '0', |
||||
|
overflowY: 'auto', |
||||
|
overflowX: 'hidden', |
||||
|
} |
||||
|
|
||||
const Wallet = () => { |
const Wallet = () => { |
||||
const [list, setList] = useState<UserBalanceLog[]>([]) |
const [list, setList] = useState<UserBalanceLog[]>([]) |
||||
const [loading, setLoading] = useState(false) |
|
||||
const [loadingMore, setLoadingMore] = useState(false) |
|
||||
const [refreshing, setRefreshing] = useState(false) |
|
||||
|
const [page, setPage] = useState(1) |
||||
const [hasMore, setHasMore] = useState(true) |
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(() => { |
useEffect(() => { |
||||
reload() |
reload() |
||||
}, []); |
|
||||
|
}, []) |
||||
|
|
||||
if (loading) { |
|
||||
return ( |
|
||||
<ConfigProvider> |
|
||||
<div className={'h-full flex flex-col justify-center items-center'} style={{ |
|
||||
height: 'calc(100vh - 300px)', |
|
||||
}}> |
|
||||
<div>加载中...</div> |
|
||||
</div> |
|
||||
</ConfigProvider> |
|
||||
) |
|
||||
|
const loadMore = async () => { |
||||
|
setPage(page + 1) |
||||
|
reload(); |
||||
} |
} |
||||
|
|
||||
if (list.length == 0) { |
|
||||
return ( |
|
||||
<ConfigProvider> |
|
||||
<div className={'h-full flex flex-col justify-center items-center'} style={{ |
|
||||
height: 'calc(100vh - 300px)', |
|
||||
}}> |
|
||||
<Empty |
|
||||
style={{ |
|
||||
backgroundColor: 'transparent' |
|
||||
}} |
|
||||
description="您还没有消费记录" |
|
||||
/> |
|
||||
<Space> |
|
||||
<Button onClick={() => reload()}>刷新</Button> |
|
||||
</Space> |
|
||||
</div> |
|
||||
</ConfigProvider> |
|
||||
) |
|
||||
|
const reload = () => { |
||||
|
pageUserBalanceLog({page}).then(res => { |
||||
|
console.log(res) |
||||
|
const newList = res?.list || []; |
||||
|
setList([...list, ...newList]) |
||||
|
setHasMore(newList.length > 0) |
||||
|
}) |
||||
} |
} |
||||
|
|
||||
return ( |
return ( |
||||
<ConfigProvider> |
|
||||
<View className="p-4"> |
|
||||
{list.map((item, index) => ( |
|
||||
<Cell.Group key={index} className="mb-4"> |
|
||||
<Cell className="flex flex-col gap-2 p-4"> |
|
||||
<View className="flex justify-between items-start w-full"> |
|
||||
<View className="flex-1"> |
|
||||
<View className="font-medium text-base text-gray-800 mb-1"> |
|
||||
{item.scene === 10 ? '会员充值' : item.scene === 20 ? '用户消费' : item.scene === 30 ? '管理员操作' : '订单退款'} |
|
||||
|
<> |
||||
|
<ul style={InfiniteUlStyle} id="scroll"> |
||||
|
<InfiniteLoading |
||||
|
target="scroll" |
||||
|
hasMore={hasMore} |
||||
|
onLoadMore={loadMore} |
||||
|
onScroll={() => { |
||||
|
console.log('onScroll') |
||||
|
}} |
||||
|
onScrollToUpper={() => { |
||||
|
console.log('onScrollToUpper') |
||||
|
}} |
||||
|
loadingText={ |
||||
|
<> |
||||
|
加载中 |
||||
|
</> |
||||
|
} |
||||
|
loadMoreText={ |
||||
|
<> |
||||
|
没有更多了 |
||||
|
</> |
||||
|
} |
||||
|
> |
||||
|
<View className="p-4"> |
||||
|
{list.map((item, index) => ( |
||||
|
<Cell.Group key={`${item.logId}-${index}`} className="mb-4"> |
||||
|
<Cell className="flex flex-col gap-2 p-4"> |
||||
|
<View className="flex justify-between items-start w-full"> |
||||
|
<View className="flex-1"> |
||||
|
<View className="font-medium text-base text-gray-800 mb-1"> |
||||
|
{item.scene === 10 ? '会员充值' : item.scene === 20 ? '用户消费' : item.scene === 30 ? '管理员操作' : '订单退款'} |
||||
|
</View> |
||||
|
<View className="text-sm text-gray-500"> |
||||
|
{item.comments} |
||||
|
</View> |
||||
|
</View> |
||||
|
<View className={`text-lg font-bold ${ |
||||
|
item.scene === 10 ? 'text-orange-500' : '' |
||||
|
}`}>
|
||||
|
{item.scene === 10 ? '+' : '-'} |
||||
|
{formatCurrency(Number(item.money), 'CNY') || '0.00'} |
||||
|
</View> |
||||
</View> |
</View> |
||||
<View className="text-sm text-gray-500"> |
|
||||
{item.comments} |
|
||||
</View> |
|
||||
</View> |
|
||||
<View className={`text-lg font-bold ${ |
|
||||
item.scene === 10 ? 'text-green-500' : '' |
|
||||
}`}>
|
|
||||
{item.scene === 10 ? '+' : '-'} |
|
||||
{formatCurrency(Number(item.money), 'CNY') || '0.00'} |
|
||||
</View> |
|
||||
</View> |
|
||||
|
|
||||
<View className="flex justify-between w-full items-center text-xs text-gray-400 mt-2"> |
|
||||
<View> |
|
||||
{item.createTime} |
|
||||
</View> |
|
||||
</View> |
|
||||
|
|
||||
{item.remark && ( |
|
||||
<View className="text-xs text-gray-500 mt-1 p-2 bg-gray-50 rounded"> |
|
||||
备注: {item.remark} |
|
||||
</View> |
|
||||
)} |
|
||||
</Cell> |
|
||||
</Cell.Group> |
|
||||
))} |
|
||||
</View> |
|
||||
</ConfigProvider> |
|
||||
); |
|
||||
}; |
|
||||
|
<View className="flex justify-between w-full items-center text-xs text-gray-400 mt-2"> |
||||
|
<View> |
||||
|
{item.createTime} |
||||
|
</View> |
||||
|
<View>余额:{item?.balance}</View> |
||||
|
</View> |
||||
|
|
||||
export default Wallet; |
|
||||
|
{item.remark && ( |
||||
|
<View className="text-xs text-gray-500 mt-1 p-2 bg-gray-50 rounded"> |
||||
|
备注: {item.remark} |
||||
|
</View> |
||||
|
)} |
||||
|
</Cell> |
||||
|
</Cell.Group> |
||||
|
))} |
||||
|
</View> |
||||
|
</InfiniteLoading> |
||||
|
</ul> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
export default Wallet |
||||
|
Loading…
Reference in new issue