forked from gxwebsoft/mp-10550
新增:优惠券、积分明细
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
export const ENV_CONFIG = {
|
export const ENV_CONFIG = {
|
||||||
// 开发环境
|
// 开发环境
|
||||||
development: {
|
development: {
|
||||||
API_BASE_URL: 'https://cms-api.websoft.top/api',
|
API_BASE_URL: 'http://127.0.0.1:9200/api',
|
||||||
APP_NAME: '时里院子市集',
|
APP_NAME: '时里院子市集',
|
||||||
DEBUG: 'true',
|
DEBUG: 'true',
|
||||||
},
|
},
|
||||||
|
|||||||
65
src/api/shop/shopCoupon/model/index.ts
Normal file
65
src/api/shop/shopCoupon/model/index.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
35
src/api/shop/shopGoodsCoupon/model/index.ts
Normal file
35
src/api/shop/shopGoodsCoupon/model/index.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ export interface UserBalanceLog {
|
|||||||
userId?: number;
|
userId?: number;
|
||||||
scene?: number;
|
scene?: number;
|
||||||
money?: string;
|
money?: string;
|
||||||
|
balance?: number;
|
||||||
describe?: string;
|
describe?: string;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
sortNumber?: number;
|
sortNumber?: number;
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ const ArticleTabs = (props: any) => {
|
|||||||
|
|
||||||
const reload = async (value) => {
|
const reload = async (value) => {
|
||||||
const {data} = props
|
const {data} = props
|
||||||
pageCmsArticle({categoryId: data[value].navigationId, page: 1, limit: 10}).then((res) => {
|
pageCmsArticle({
|
||||||
|
categoryId: data[value].navigationId,
|
||||||
|
page: 1,
|
||||||
|
status: 0,
|
||||||
|
limit: 10
|
||||||
|
}).then((res) => {
|
||||||
res && setList(res?.list || [])
|
res && setList(res?.list || [])
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
import {useEffect, useState} from "react";
|
import {useState, useEffect, CSSProperties} from 'react'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import {Button, Cell, Space, Empty, ConfigProvider, Tabs, TabPane, Tag} from '@nutui/nutui-react-taro'
|
import {Cell, InfiniteLoading, Tabs, TabPane, Tag, Empty, ConfigProvider} from '@nutui/nutui-react-taro'
|
||||||
import {View} from '@tarojs/components'
|
|
||||||
import {pageUserCoupon, getUserCouponCount} from "@/api/user/coupon";
|
import {pageUserCoupon, getUserCouponCount} from "@/api/user/coupon";
|
||||||
import {UserCoupon as UserCouponType} from "@/api/user/coupon/model";
|
import {UserCoupon as UserCouponType} from "@/api/user/coupon/model";
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
|
||||||
|
const InfiniteUlStyle: CSSProperties = {
|
||||||
|
height: '100vh',
|
||||||
|
width: '100%',
|
||||||
|
padding: '0',
|
||||||
|
overflowY: 'auto',
|
||||||
|
overflowX: 'hidden',
|
||||||
|
}
|
||||||
|
|
||||||
const UserCoupon = () => {
|
const UserCoupon = () => {
|
||||||
const [list, setList] = useState<UserCouponType[]>([])
|
const [list, setList] = useState<UserCouponType[]>([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [page, setPage] = useState(1)
|
||||||
|
const [hasMore, setHasMore] = useState(true)
|
||||||
const [activeTab, setActiveTab] = useState('0')
|
const [activeTab, setActiveTab] = useState('0')
|
||||||
const [couponCount, setCouponCount] = useState({
|
const [couponCount, setCouponCount] = useState({
|
||||||
total: 0,
|
total: 0,
|
||||||
@@ -23,42 +32,43 @@ const UserCoupon = () => {
|
|||||||
{ key: '3', title: '已过期', status: 2 }
|
{ key: '3', title: '已过期', status: 2 }
|
||||||
]
|
]
|
||||||
|
|
||||||
const reload = (status?: number) => {
|
useEffect(() => {
|
||||||
setLoading(true)
|
reload()
|
||||||
|
loadCouponCount()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const loadMore = async () => {
|
||||||
|
setPage(page + 1)
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
const userId = Taro.getStorageSync('UserId')
|
const userId = Taro.getStorageSync('UserId')
|
||||||
|
|
||||||
console.log('Loading coupons for userId:', userId, 'status:', status)
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
console.warn('No userId found in storage')
|
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '请先登录',
|
title: '请先登录',
|
||||||
icon: 'error'
|
icon: 'error'
|
||||||
});
|
});
|
||||||
setLoading(false)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tab = tabs.find(t => t.key === activeTab)
|
||||||
pageUserCoupon({
|
pageUserCoupon({
|
||||||
userId: parseInt(userId),
|
userId: parseInt(userId),
|
||||||
status: status,
|
status: tab?.status,
|
||||||
page: 1,
|
page
|
||||||
limit: 20
|
}).then(res => {
|
||||||
|
console.log(res)
|
||||||
|
const newList = res?.list || [];
|
||||||
|
setList([...list, ...newList])
|
||||||
|
setHasMore(newList.length > 0)
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Coupon error:', error)
|
||||||
|
Taro.showToast({
|
||||||
|
title: error?.message || '获取失败',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.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 loadCouponCount = () => {
|
||||||
@@ -74,15 +84,15 @@ const UserCoupon = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
reload()
|
|
||||||
loadCouponCount()
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onTabChange = (index: string) => {
|
const onTabChange = (index: string) => {
|
||||||
setActiveTab(index)
|
setActiveTab(index)
|
||||||
const tab = tabs.find(t => t.key === index)
|
setList([]) // 清空列表
|
||||||
reload(tab?.status)
|
setPage(1) // 重置页码
|
||||||
|
setHasMore(true) // 重置hasMore
|
||||||
|
// 延迟执行reload,确保状态更新完成
|
||||||
|
setTimeout(() => {
|
||||||
|
reload()
|
||||||
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCouponTypeText = (type?: number) => {
|
const getCouponTypeText = (type?: number) => {
|
||||||
@@ -122,86 +132,91 @@ const UserCoupon = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<ConfigProvider>
|
|
||||||
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
|
||||||
height: 'calc(100vh - 300px)',
|
|
||||||
}}>
|
|
||||||
<div>加载中...</div>
|
|
||||||
</div>
|
|
||||||
</ConfigProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigProvider>
|
<ConfigProvider>
|
||||||
<View>
|
<View className="h-screen">
|
||||||
<Tabs value={activeTab} onChange={onTabChange}>
|
<Tabs value={activeTab} onChange={onTabChange}>
|
||||||
{tabs.map(tab => (
|
{tabs.map(tab => (
|
||||||
<TabPane key={tab.key} title={tab.title}>
|
<TabPane key={tab.key} title={tab.title}>
|
||||||
<View className="p-4">
|
<ul style={InfiniteUlStyle} id="scroll">
|
||||||
{list.map((item, index) => (
|
<InfiniteLoading
|
||||||
<Cell.Group key={index} className="mb-4">
|
target="scroll"
|
||||||
<Cell className="coupon-item p-4">
|
hasMore={hasMore}
|
||||||
<View className="flex justify-between items-center">
|
onLoadMore={loadMore}
|
||||||
<View className="flex-1">
|
onScroll={() => {
|
||||||
<View className="flex items-center mb-2">
|
console.log('onScroll')
|
||||||
<View className="coupon-value text-2xl font-bold text-red-500 mr-3">
|
}}
|
||||||
{formatCouponValue(item.type, item.value)}
|
onScrollToUpper={() => {
|
||||||
</View>
|
console.log('onScrollToUpper')
|
||||||
<View className="flex flex-col">
|
}}
|
||||||
<View className="text-base font-medium text-gray-800">
|
loadingText={
|
||||||
{item.name || getCouponTypeText(item.type)}
|
<>
|
||||||
</View>
|
加载中
|
||||||
{item.minAmount && parseFloat(item.minAmount) > 0 && (
|
</>
|
||||||
<View className="text-sm text-gray-500">
|
}
|
||||||
满¥{item.minAmount}可用
|
loadMoreText={
|
||||||
|
<>
|
||||||
|
没有更多了
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View className="p-4">
|
||||||
|
{list.length === 0 ? (
|
||||||
|
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
||||||
|
height: 'calc(100vh - 400px)',
|
||||||
|
}}>
|
||||||
|
<Empty
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}}
|
||||||
|
description="您还没有优惠券"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
list.map((item, index) => (
|
||||||
|
<Cell.Group key={`${item.couponId}-${index}`} className="mb-4">
|
||||||
|
<Cell className="coupon-item p-4">
|
||||||
|
<View className="flex justify-between items-center">
|
||||||
|
<View className="flex-1">
|
||||||
|
<View className="flex items-center mb-2">
|
||||||
|
<View className="coupon-value text-2xl font-bold text-red-500 mr-3">
|
||||||
|
{formatCouponValue(item.type, item.value)}
|
||||||
|
</View>
|
||||||
|
<View className="flex flex-col">
|
||||||
|
<View className="text-base font-medium text-gray-800">
|
||||||
|
{item.name || getCouponTypeText(item.type)}
|
||||||
|
</View>
|
||||||
|
{item.minAmount && parseFloat(item.minAmount) > 0 && (
|
||||||
|
<View className="text-sm text-gray-500">
|
||||||
|
满¥{item.minAmount}可用
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View className="flex justify-between items-center text-xs text-gray-400">
|
<View className="flex justify-between items-center text-xs text-gray-400">
|
||||||
<View>
|
<View>
|
||||||
有效期: {item.startTime ? new Date(item.startTime).toLocaleDateString() : ''} - {item.endTime ? new Date(item.endTime).toLocaleDateString() : ''}
|
有效期: {item.startTime ? new Date(item.startTime).toLocaleDateString() : ''} - {item.endTime ? new Date(item.endTime).toLocaleDateString() : ''}
|
||||||
</View>
|
</View>
|
||||||
<Tag type={getCouponStatusColor(item.status)} size="small">
|
<Tag type={getCouponStatusColor(item.status)} size="small">
|
||||||
{getCouponStatusText(item.status)}
|
{getCouponStatusText(item.status)}
|
||||||
</Tag>
|
</Tag>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{item.comments && (
|
{item.comments && (
|
||||||
<View className="text-xs text-gray-500 mt-2 p-2 bg-gray-50 rounded">
|
<View className="text-xs text-gray-500 mt-2 p-2 bg-gray-50 rounded">
|
||||||
{item.comments}
|
{item.comments}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)}
|
</Cell>
|
||||||
</View>
|
</Cell.Group>
|
||||||
</View>
|
))
|
||||||
</Cell>
|
)}
|
||||||
</Cell.Group>
|
</View>
|
||||||
))}
|
</InfiniteLoading>
|
||||||
</View>
|
</ul>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
))}
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|||||||
@@ -1,50 +1,59 @@
|
|||||||
import {useEffect, useState} from "react";
|
import {useState, useEffect, CSSProperties} from 'react'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import {Button, Cell, Space, Empty, ConfigProvider, Card} from '@nutui/nutui-react-taro'
|
import {Cell, InfiniteLoading, Card, Empty, ConfigProvider} from '@nutui/nutui-react-taro'
|
||||||
import {View} from '@tarojs/components'
|
|
||||||
import {pageUserPointsLog, getUserPointsStats} from "@/api/user/points";
|
import {pageUserPointsLog, getUserPointsStats} from "@/api/user/points";
|
||||||
import {UserPointsLog as UserPointsLogType, UserPointsStats} from "@/api/user/points/model";
|
import {UserPointsLog as UserPointsLogType, UserPointsStats} from "@/api/user/points/model";
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
|
||||||
|
const InfiniteUlStyle: CSSProperties = {
|
||||||
|
height: '100vh',
|
||||||
|
width: '100%',
|
||||||
|
padding: '0',
|
||||||
|
overflowY: 'auto',
|
||||||
|
overflowX: 'hidden',
|
||||||
|
}
|
||||||
|
|
||||||
const UserPoints = () => {
|
const UserPoints = () => {
|
||||||
const [list, setList] = useState<UserPointsLogType[]>([])
|
const [list, setList] = useState<UserPointsLogType[]>([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [page, setPage] = useState(1)
|
||||||
|
const [hasMore, setHasMore] = useState(true)
|
||||||
const [stats, setStats] = useState<UserPointsStats>({})
|
const [stats, setStats] = useState<UserPointsStats>({})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reload()
|
||||||
|
loadPointsStats()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const loadMore = async () => {
|
||||||
|
setPage(page + 1)
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
const reload = () => {
|
const reload = () => {
|
||||||
setLoading(true)
|
|
||||||
const userId = Taro.getStorageSync('UserId')
|
const userId = Taro.getStorageSync('UserId')
|
||||||
|
|
||||||
console.log('Loading points log for userId:', userId)
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
console.warn('No userId found in storage')
|
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '请先登录',
|
title: '请先登录',
|
||||||
icon: 'error'
|
icon: 'error'
|
||||||
});
|
});
|
||||||
setLoading(false)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pageUserPointsLog({
|
pageUserPointsLog({
|
||||||
userId: parseInt(userId),
|
userId: parseInt(userId),
|
||||||
page: 1,
|
page
|
||||||
limit: 20
|
}).then(res => {
|
||||||
|
console.log(res)
|
||||||
|
const newList = res?.list || [];
|
||||||
|
setList([...list, ...newList])
|
||||||
|
setHasMore(newList.length > 0)
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Points log error:', error)
|
||||||
|
Taro.showToast({
|
||||||
|
title: error?.message || '获取失败',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.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 loadPointsStats = () => {
|
||||||
@@ -60,11 +69,6 @@ const UserPoints = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
reload()
|
|
||||||
loadPointsStats()
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getPointsTypeText = (type?: number) => {
|
const getPointsTypeText = (type?: number) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 1: return '获得积分'
|
case 1: return '获得积分'
|
||||||
@@ -85,21 +89,9 @@ const UserPoints = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<ConfigProvider>
|
|
||||||
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
|
||||||
height: 'calc(100vh - 300px)',
|
|
||||||
}}>
|
|
||||||
<div>加载中...</div>
|
|
||||||
</div>
|
|
||||||
</ConfigProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigProvider>
|
<ConfigProvider>
|
||||||
<View className="bg-gray-50 min-h-screen">
|
<View className="bg-gray-50 h-screen">
|
||||||
{/* 积分统计卡片 */}
|
{/* 积分统计卡片 */}
|
||||||
<View className="p-4">
|
<View className="p-4">
|
||||||
<Card className="points-stats-card">
|
<Card className="points-stats-card">
|
||||||
@@ -134,62 +126,84 @@ const UserPoints = () => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 积分记录 */}
|
{/* 积分记录 */}
|
||||||
<View className="px-4">
|
<View className="px-4 flex-1">
|
||||||
<View className="text-base font-medium text-gray-800 mb-3">积分明细</View>
|
|
||||||
|
|
||||||
{list.length === 0 ? (
|
<ul style={{...InfiniteUlStyle, height: 'calc(100vh - 200px)'}} id="scroll">
|
||||||
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
<InfiniteLoading
|
||||||
height: 'calc(100vh - 400px)',
|
target="scroll"
|
||||||
}}>
|
hasMore={hasMore}
|
||||||
<Empty
|
onLoadMore={loadMore}
|
||||||
style={{
|
onScroll={() => {
|
||||||
backgroundColor: 'transparent'
|
console.log('onScroll')
|
||||||
}}
|
}}
|
||||||
description="您还没有积分记录"
|
onScrollToUpper={() => {
|
||||||
/>
|
console.log('onScrollToUpper')
|
||||||
<Space>
|
}}
|
||||||
<Button onClick={() => reload()}>刷新</Button>
|
loadingText={
|
||||||
</Space>
|
<>
|
||||||
</div>
|
加载中
|
||||||
) : (
|
</>
|
||||||
list.map((item, index) => (
|
}
|
||||||
<Cell.Group key={index} className="mb-3">
|
loadMoreText={
|
||||||
<Cell className="flex flex-col gap-2 p-4">
|
<>
|
||||||
<View className="flex justify-between items-start">
|
没有更多了
|
||||||
<View className="flex-1">
|
</>
|
||||||
<View className="font-medium text-base text-gray-800 mb-1">
|
}
|
||||||
{getPointsTypeText(item.type)}
|
>
|
||||||
</View>
|
<View className="p-4">
|
||||||
<View className="text-sm text-gray-500">
|
{list.length === 0 ? (
|
||||||
{item.reason || '无备注'}
|
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
||||||
</View>
|
height: 'calc(100vh - 500px)',
|
||||||
</View>
|
}}>
|
||||||
<View className={`text-lg font-bold ${getPointsTypeColor(item.type)}`}>
|
<Empty
|
||||||
{item.type === 1 ? '+' : item.type === 2 ? '-' : ''}
|
style={{
|
||||||
{item.points || 0}
|
backgroundColor: 'transparent'
|
||||||
</View>
|
}}
|
||||||
</View>
|
description="您还没有积分记录"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
list.map((item, index) => (
|
||||||
|
<Cell.Group key={`${item.logId}-${index}`} className="mb-3">
|
||||||
|
<Cell className="flex flex-col gap-2 p-4">
|
||||||
|
<View className="flex justify-between items-start">
|
||||||
|
<View className="flex-1">
|
||||||
|
<View className="font-medium text-base text-gray-800 mb-1">
|
||||||
|
{getPointsTypeText(item.type)}
|
||||||
|
</View>
|
||||||
|
<View className="text-sm text-gray-500">
|
||||||
|
{item.reason || '无备注'}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className={`text-lg font-bold ${getPointsTypeColor(item.type)}`}>
|
||||||
|
{item.type === 1 ? '+' : item.type === 2 ? '-' : ''}
|
||||||
|
{item.points || 0}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
<View className="flex justify-between items-center text-xs text-gray-400 mt-2">
|
<View className="flex justify-between items-center text-xs text-gray-400 mt-2">
|
||||||
<View>
|
<View>
|
||||||
{item.createTime ? new Date(item.createTime).toLocaleString() : ''}
|
{item.createTime ? new Date(item.createTime).toLocaleString() : ''}
|
||||||
</View>
|
</View>
|
||||||
{item.orderId && (
|
{item.orderId && (
|
||||||
<View>
|
<View>
|
||||||
订单: {item.orderId}
|
订单: {item.orderId}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{item.comments && (
|
{item.comments && (
|
||||||
<View className="text-xs text-gray-500 mt-1 p-2 bg-gray-50 rounded">
|
<View className="text-xs text-gray-500 mt-1 p-2 bg-gray-50 rounded">
|
||||||
备注: {item.comments}
|
备注: {item.comments}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</Cell>
|
</Cell>
|
||||||
</Cell.Group>
|
</Cell.Group>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
</View>
|
||||||
|
</InfiniteLoading>
|
||||||
|
</ul>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
|
|||||||
@@ -1,134 +1,105 @@
|
|||||||
import {useEffect, useState} from "react";
|
import {useState, useEffect, CSSProperties} from 'react'
|
||||||
import Taro from '@tarojs/taro'
|
import {Cell, InfiniteLoading} from '@nutui/nutui-react-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 {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 [page, setPage] = useState(1)
|
||||||
const [loadingMore, setLoadingMore] = useState(false)
|
|
||||||
const [refreshing, setRefreshing] = useState(false)
|
|
||||||
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) {
|
const loadMore = async () => {
|
||||||
return (
|
setPage(page + 1)
|
||||||
<ConfigProvider>
|
reload();
|
||||||
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
|
||||||
height: 'calc(100vh - 300px)',
|
|
||||||
}}>
|
|
||||||
<div>加载中...</div>
|
|
||||||
</div>
|
|
||||||
</ConfigProvider>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.length == 0) {
|
const reload = () => {
|
||||||
return (
|
pageUserBalanceLog({page}).then(res => {
|
||||||
<ConfigProvider>
|
console.log(res)
|
||||||
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
const newList = res?.list || [];
|
||||||
height: 'calc(100vh - 300px)',
|
setList([...list, ...newList])
|
||||||
}}>
|
setHasMore(newList.length > 0)
|
||||||
<Empty
|
})
|
||||||
style={{
|
|
||||||
backgroundColor: 'transparent'
|
|
||||||
}}
|
|
||||||
description="您还没有消费记录"
|
|
||||||
/>
|
|
||||||
<Space>
|
|
||||||
<Button onClick={() => reload()}>刷新</Button>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
</ConfigProvider>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigProvider>
|
<>
|
||||||
<View className="p-4">
|
<ul style={InfiniteUlStyle} id="scroll">
|
||||||
{list.map((item, index) => (
|
<InfiniteLoading
|
||||||
<Cell.Group key={index} className="mb-4">
|
target="scroll"
|
||||||
<Cell className="flex flex-col gap-2 p-4">
|
hasMore={hasMore}
|
||||||
<View className="flex justify-between items-start w-full">
|
onLoadMore={loadMore}
|
||||||
<View className="flex-1">
|
onScroll={() => {
|
||||||
<View className="font-medium text-base text-gray-800 mb-1">
|
console.log('onScroll')
|
||||||
{item.scene === 10 ? '会员充值' : item.scene === 20 ? '用户消费' : item.scene === 30 ? '管理员操作' : '订单退款'}
|
}}
|
||||||
|
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 className="flex justify-between w-full items-center text-xs text-gray-400 mt-2">
|
||||||
|
<View>
|
||||||
|
{item.createTime}
|
||||||
|
</View>
|
||||||
|
<View>余额:{item?.balance}</View>
|
||||||
</View>
|
</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">
|
{item.remark && (
|
||||||
<View>
|
<View className="text-xs text-gray-500 mt-1 p-2 bg-gray-50 rounded">
|
||||||
{item.createTime}
|
备注: {item.remark}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
)}
|
||||||
|
</Cell>
|
||||||
{item.remark && (
|
</Cell.Group>
|
||||||
<View className="text-xs text-gray-500 mt-1 p-2 bg-gray-50 rounded">
|
))}
|
||||||
备注: {item.remark}
|
</View>
|
||||||
</View>
|
</InfiniteLoading>
|
||||||
)}
|
</ul>
|
||||||
</Cell>
|
</>
|
||||||
</Cell.Group>
|
)
|
||||||
))}
|
}
|
||||||
</View>
|
export default Wallet
|
||||||
</ConfigProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Wallet;
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import {User} from "@/api/system/user/model";
|
|||||||
// 模版套餐ID - 请根据实际情况修改
|
// 模版套餐ID - 请根据实际情况修改
|
||||||
export const TEMPLATE_ID = '10550';
|
export const TEMPLATE_ID = '10550';
|
||||||
// 服务接口 - 请根据实际情况修改
|
// 服务接口 - 请根据实际情况修改
|
||||||
// export const SERVER_API_URL = 'https://server.websoft.top/api';
|
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 = 'http://127.0.0.1:8000/api';
|
||||||
/**
|
/**
|
||||||
* 保存用户信息到本地存储
|
* 保存用户信息到本地存储
|
||||||
* @param token
|
* @param token
|
||||||
|
|||||||
Reference in New Issue
Block a user