feat(dealer): 新增资金明细页面及功能

- 在 dealer/capital 路由配置中添加 detail 子路由
- 创建资金明细详情页组件 /dealer/capital/detail.tsx
- 实现资金流水列表展示,支持下拉刷新和上拉加载更多
- 添加日期筛选功能,允许按年月查看资金记录
-优化资金类型显示逻辑,区分电费收益、提现支出等不同类型
- 修改资金明细接口字段,将 describe 字段改为 comments
- 调整页面布局样式,适配移动端显示效果
-为资金明细项添加点击跳转至详情页的功能- 更新订单页面日期选择器交互方式和位置- 统一页面标题和导航栏文案为“详情”
This commit is contained in:
2025-10-13 21:04:55 +08:00
parent cb69033b7e
commit 545fb393ea
6 changed files with 285 additions and 95 deletions

View File

@@ -1,4 +1,4 @@
import type { PageParam } from '@/api/index';
import type { PageParam } from '@/api';
/**
* 分销商资金明细表
@@ -15,7 +15,7 @@ export interface ShopDealerCapital {
// 金额
money?: string;
// 描述
describe?: string;
comments?: string;
// 对方用户ID
toUserId?: number;
// 商城ID

View File

@@ -59,6 +59,7 @@ export default defineAppConfig({
"withdraw/index",
"orders/index",
"capital/index",
"capital/detail",
"team/index",
"qrcode/index",
"invite-stats/index",

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '详情'
})

View File

@@ -0,0 +1,174 @@
import React, {useState, useEffect, useCallback} from 'react'
import {View, Text, ScrollView} from '@tarojs/components'
import {Empty, PullToRefresh, Loading} from '@nutui/nutui-react-taro'
import Taro from '@tarojs/taro'
import {pageShopDealerCapital} from '@/api/shop/shopDealerCapital'
import {useDealerUser} from '@/hooks/useDealerUser'
import type {ShopDealerCapital} from '@/api/shop/shopDealerCapital/model'
const DealerCapital: React.FC = () => {
const [loading, setLoading] = useState<boolean>(false)
const [refreshing, setRefreshing] = useState<boolean>(false)
const [loadingMore, setLoadingMore] = useState<boolean>(false)
const [capital, setCapital] = useState<ShopDealerCapital[]>([])
const [currentPage, setCurrentPage] = useState<number>(1)
const [hasMore, setHasMore] = useState<boolean>(true)
const {dealerUser} = useDealerUser()
// 获取订单数据
const fetchCapital = useCallback(async (page: number = 1, isRefresh: boolean = false) => {
if (!dealerUser?.userId) return
try {
if (isRefresh) {
setRefreshing(true)
} else if (page === 1) {
setLoading(true)
} else {
setLoadingMore(true)
}
const result = await pageShopDealerCapital({
page,
limit: 10
})
if (result?.list) {
const newCapital = result.list.map(item => ({
...item,
orderId: item.orderId
}))
if (page === 1) {
setCapital(newCapital)
} else {
setCapital(prev => [...prev, ...newCapital])
}
setHasMore(newCapital.length === 10)
setCurrentPage(page)
}
} catch (error) {
console.error('获取分销订单失败:', error)
Taro.showToast({
title: '获取订单失败',
icon: 'error'
})
} finally {
setLoading(false)
setRefreshing(false)
setLoadingMore(false)
}
}, [dealerUser?.userId])
// 下拉刷新
const handleRefresh = async () => {
await fetchCapital(1, true)
}
// 加载更多
const handleLoadMore = async () => {
if (!loadingMore && hasMore) {
await fetchCapital(currentPage + 1)
}
}
const getFlowType = (index?: number) => {
if (index === 10) return '电费收益'
if (index === 20) return '提现支出'
if (index === 30) return '转账支出'
if (index === 40) return '转账收入'
return 'warning'
}
// 初始化加载数据
useEffect(() => {
if (dealerUser?.userId) {
fetchCapital(1)
}
}, [fetchCapital])
const renderCapitalItem = (item: ShopDealerCapital) => (
<View key={item.id} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
<View className="flex justify-between items-start mb-1">
<Text className="font-semibold text-gray-800">
{getFlowType(item.flowType)}
</Text>
<Text className="text-lg text-orange-500 font-semibold">
¥{Number(item.money).toFixed(2)}
</Text>
</View>
{item.comments && (
<View className="flex justify-between items-center mb-1">
<Text className="text-sm text-gray-400">
{item.comments}
</Text>
</View>
)}
<View className="flex justify-between items-center mb-1">
<Text className="text-sm text-gray-400">
{item.orderId}
</Text>
</View>
<View className="flex justify-between items-center mb-1">
<Text className="text-sm text-gray-400">
{item.createTime}
</Text>
</View>
</View>
)
return (
<View className="min-h-screen bg-gray-50">
<PullToRefresh
onRefresh={handleRefresh}
disabled={refreshing}
pullingText="下拉刷新"
canReleaseText="释放刷新"
refreshingText="刷新中..."
completeText="刷新完成"
>
<ScrollView
scrollY
className="h-screen"
onScrollToLower={handleLoadMore}
lowerThreshold={50}
>
<View className="p-4">
{loading && capital.length === 0 ? (
<View className="text-center py-8">
<Loading/>
<Text className="text-gray-500 mt-2">...</Text>
</View>
) : capital.length > 0 ? (
<>
{capital.map(renderCapitalItem)}
{loadingMore && (
<View className="text-center py-4">
<Loading/>
<Text className="text-gray-500 mt-1 text-sm">...</Text>
</View>
)}
{!hasMore && capital.length > 0 && (
<View className="text-center py-4">
<Text className="text-gray-400 text-sm"></Text>
</View>
)}
</>
) : (
<Empty description="暂无收益" style={{
backgroundColor: 'transparent'
}}/>
)}
</View>
</ScrollView>
</PullToRefresh>
</View>
)
}
export default DealerCapital

View File

@@ -1,29 +1,28 @@
import React, {useState, useEffect, useCallback} from 'react'
import {View, Text, ScrollView} from '@tarojs/components'
import {Empty, PullToRefresh, Loading} from '@nutui/nutui-react-taro'
import {Empty, PullToRefresh, Cell, DatePicker, Loading} from '@nutui/nutui-react-taro'
import Taro from '@tarojs/taro'
import {pageShopDealerOrder} from '@/api/shop/shopDealerOrder'
import {pageShopDealerCapital} from '@/api/shop/shopDealerCapital'
import {useDealerUser} from '@/hooks/useDealerUser'
import type {ShopDealerOrder} from '@/api/shop/shopDealerOrder/model'
import type {ShopDealerCapital} from '@/api/shop/shopDealerCapital/model'
import navTo from "@/utils/common";
interface OrderWithDetails extends ShopDealerOrder {
orderNo?: string
customerName?: string
userCommission?: string
}
const DealerOrders: React.FC = () => {
const DealerCapital: React.FC = () => {
const [loading, setLoading] = useState<boolean>(false)
const d = new Date()
const currDate = `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
const [date, setDate] = useState(currDate)
const [show1, setShow1] = useState(false)
const [refreshing, setRefreshing] = useState<boolean>(false)
const [loadingMore, setLoadingMore] = useState<boolean>(false)
const [orders, setOrders] = useState<OrderWithDetails[]>([])
const [capital, setCapital] = useState<ShopDealerCapital[]>([])
const [currentPage, setCurrentPage] = useState<number>(1)
const [hasMore, setHasMore] = useState<boolean>(true)
const {dealerUser} = useDealerUser()
// 获取订单数据
const fetchOrders = useCallback(async (page: number = 1, isRefresh: boolean = false) => {
const fetchCapital = useCallback(async (page: number = 1, isRefresh: boolean = false) => {
if (!dealerUser?.userId) return
try {
@@ -35,27 +34,24 @@ const DealerOrders: React.FC = () => {
setLoadingMore(true)
}
const result = await pageShopDealerOrder({
isInvalid: 0,
const result = await pageShopDealerCapital({
page,
limit: 10
})
if (result?.list) {
const newOrders = result.list.map(order => ({
...order,
orderNo: `${order.orderId}`,
customerName: `用户${order.userId}`,
userCommission: order.firstMoney || '0.00'
const newCapital = result.list.map(item => ({
...item,
orderId: item.orderId
}))
if (page === 1) {
setOrders(newOrders)
setCapital(newCapital)
} else {
setOrders(prev => [...prev, ...newOrders])
setCapital(prev => [...prev, ...newCapital])
}
setHasMore(newOrders.length === 10)
setHasMore(newCapital.length === 10)
setCurrentPage(page)
}
@@ -74,67 +70,55 @@ const DealerOrders: React.FC = () => {
// 下拉刷新
const handleRefresh = async () => {
await fetchOrders(1, true)
await fetchCapital(1, true)
}
// 加载更多
const handleLoadMore = async () => {
if (!loadingMore && hasMore) {
await fetchOrders(currentPage + 1)
await fetchCapital(currentPage + 1)
}
}
const getFlowType = (index?: number) => {
if (index === 10) return '电费收益'
if (index === 20) return '提现支出'
if (index === 30) return '转账支出'
if (index === 40) return '转账收入'
return 'warning'
}
// 初始化加载数据
useEffect(() => {
if (dealerUser?.userId) {
fetchOrders(1)
fetchCapital(1)
}
}, [fetchOrders])
}, [fetchCapital])
const getStatusText = (isSettled?: number, isInvalid?: number) => {
if (isInvalid === 1) return '未签约'
if (isSettled === 1) return '已结算'
return '待结算'
}
// const getStatusColor = (isSettled?: number, isInvalid?: number) => {
// if (isInvalid === 1) return 'danger'
// if (isSettled === 1) return 'success'
// return 'warning'
// }
const renderOrderItem = (order: OrderWithDetails) => (
<View key={order.id} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
const renderCapitalItem = (item: ShopDealerCapital) => (
<View key={item.id} className="bg-white rounded-lg p-4 mb-3 shadow-sm" onClick={() => navTo(`/dealer/capital/detail?id=${item.id}`)}>
<View className="flex justify-between items-start mb-1">
<Text className="font-semibold text-gray-800">
{getFlowType(item.flowType)}
</Text>
<Text className="text-lg text-orange-500 font-semibold">
¥{(Number(order.orderPrice) * 10).toFixed(2)}
¥{Number(item.money).toFixed(2)}
</Text>
</View>
{/*{item.comments && (*/}
{/* <View className="flex justify-between items-center mb-1">*/}
{/* <Text className="text-sm text-gray-400">*/}
{/* {item.comments}*/}
{/* </Text>*/}
{/* </View>*/}
{/*)}*/}
<View className="flex justify-between items-center mb-1">
<Text className="text-sm text-gray-400">
{order.comments}
{item.createTime}
</Text>
<Text className="text-sm text-gray-400">
{order.rate}
</Text>
</View>
<View className="flex justify-between items-center">
<Text className="text-sm text-gray-400">
{order.orderPrice || '0.00'}
</Text>
<Text className="text-sm text-gray-400">
{getStatusText(order.isSettled, order.isInvalid)}
</Text>
</View>
<View className="flex justify-between items-center mb-1">
<Text className="text-sm text-gray-400">
{order.settleTime}
{item.money}
</Text>
</View>
@@ -143,6 +127,13 @@ const DealerOrders: React.FC = () => {
return (
<View className="min-h-screen bg-gray-50">
<Cell.Group divider={false}>
<Cell
description={date ? `${date}` : '请选择'}
extra={'选择月份'}
onClick={() => setShow1(true)}
/>
</Cell.Group>
<PullToRefresh
onRefresh={handleRefresh}
disabled={refreshing}
@@ -153,26 +144,28 @@ const DealerOrders: React.FC = () => {
>
<ScrollView
scrollY
className="h-screen"
style={{
height: 'calc(100vh - 110px)',
}}
onScrollToLower={handleLoadMore}
lowerThreshold={50}
>
<View className="p-4">
{loading && orders.length === 0 ? (
{loading && capital.length === 0 ? (
<View className="text-center py-8">
<Loading/>
<Text className="text-gray-500 mt-2">...</Text>
</View>
) : orders.length > 0 ? (
) : capital.length > 0 ? (
<>
{orders.map(renderOrderItem)}
{capital.map(renderCapitalItem)}
{loadingMore && (
<View className="text-center py-4">
<Loading/>
<Text className="text-gray-500 mt-1 text-sm">...</Text>
</View>
)}
{!hasMore && orders.length > 0 && (
{!hasMore && capital.length > 0 && (
<View className="text-center py-4">
<Text className="text-gray-400 text-sm"></Text>
</View>
@@ -186,8 +179,23 @@ const DealerOrders: React.FC = () => {
</View>
</ScrollView>
</PullToRefresh>
<DatePicker
title="日期选择"
visible={show1}
pickerProps={{
popupProps: {zIndex: 1220},
}}
type={'year-month'}
defaultValue={new Date(`${currDate}`)}
showChinese
onCancel={() => setShow1(false)}
onConfirm={(_, values) => {
setShow1(false)
setDate(`${values[0]}${values[1]}`)
}}
/>
</View>
)
}
export default DealerOrders
export default DealerCapital

View File

@@ -12,10 +12,11 @@ interface OrderWithDetails extends ShopDealerOrder {
userCommission?: string
}
const DealerOrders: React.FC = () => {
const DealerOrder: React.FC = () => {
const [loading, setLoading] = useState<boolean>(false)
const d = new Date()
const currDay = `${d.getFullYear()}${d.getMonth() + 1}`
const currDay = `${d.getFullYear()}${d.getMonth() + 1}`;
const currDate = `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
const [date, setDate] = useState(currDay)
const [show1, setShow1] = useState(false)
const [refreshing, setRefreshing] = useState<boolean>(false)
@@ -76,7 +77,7 @@ const DealerOrders: React.FC = () => {
setRefreshing(false)
setLoadingMore(false)
}
}, [dealerUser?.userId])
}, [dealerUser?.userId, date])
// 下拉刷新
const handleRefresh = async () => {
@@ -95,7 +96,7 @@ const DealerOrders: React.FC = () => {
if (dealerUser?.userId) {
fetchOrders(1)
}
}, [fetchOrders])
}, [fetchOrders, date])
const getStatusText = (isSettled?: number, isInvalid?: number) => {
if (isInvalid === 1) return '未签约'
@@ -161,6 +162,13 @@ const DealerOrders: React.FC = () => {
return (
<View className="min-h-screen bg-gray-50">
<Cell.Group divider={false}>
<Cell
description={date ? `${date}` : '请选择'}
extra={'选择月份'}
onClick={() => setShow1(true)}
/>
</Cell.Group>
<PullToRefresh
onRefresh={handleRefresh}
disabled={refreshing}
@@ -169,34 +177,15 @@ const DealerOrders: React.FC = () => {
refreshingText="刷新中..."
completeText="刷新完成"
>
<Cell
description={date ? `${date}` : '请选择'}
extra={'选择月份'}
onClick={() => setShow1(true)}
/>
<DatePicker
title="日期选择"
visible={show1}
pickerProps={{
popupProps: {zIndex: 1220},
}}
type={'year-month'}
defaultValue={new Date(`${date}`)}
showChinese
onCancel={() => setShow1(false)}
onConfirm={(_, values) => {
setShow1(false)
setDate(`${values[0]}${values[1]}`)
fetchOrders(1).then()
}}
/>
<ScrollView
scrollY
className="h-screen"
style={{
height: 'calc(100vh - 100px)',
}}
onScrollToLower={handleLoadMore}
lowerThreshold={50}
>
<View className="p-4">
<View className="px-4">
{loading && orders.length === 0 ? (
<View className="text-center py-8">
<Loading/>
@@ -225,8 +214,23 @@ const DealerOrders: React.FC = () => {
</View>
</ScrollView>
</PullToRefresh>
<DatePicker
title="日期选择"
visible={show1}
pickerProps={{
popupProps: {zIndex: 1220},
}}
type={'year-month'}
defaultValue={new Date(`${currDate}`)}
showChinese
onCancel={() => setShow1(false)}
onConfirm={(_, values) => {
setShow1(false)
setDate(`${values[0]}${values[1]}`)
}}
/>
</View>
)
}
export default DealerOrders
export default DealerOrder