feat(dealer): 添加分销商收益明细页面并优化订单管理功能

- 新增收益明细页面,支持下拉刷新和上拉加载更多
- 在app.config.ts中注册收益明细页面路由
- 更新API基础URL配置,统一使用mp-api域名
- 优化提交表单逻辑,确保refereeId参数为数字类型
- 修改订单页面,添加resourceId参数以正确过滤分销订单
- 修复订单号显示逻辑,优先使用接口返回的订单号
- 优化订单列表项点击事件,跳转到收益明细页面
- 更新客户名称显示格式,包含昵称和用户ID
- 调整订单详情展示布局和信息内容
This commit is contained in:
2026-01-25 13:32:49 +08:00
parent 0d6eb331c8
commit aff888794f
9 changed files with 247 additions and 21 deletions

View File

@@ -3,19 +3,19 @@ export const ENV_CONFIG = {
// 开发环境 // 开发环境
development: { development: {
// API_BASE_URL: 'http://127.0.0.1:9200/api', // API_BASE_URL: 'http://127.0.0.1:9200/api',
API_BASE_URL: 'https://cms-api.websoft.top/api', API_BASE_URL: 'https://mp-api.websoft.top/api',
APP_NAME: '开发环境', APP_NAME: '开发环境',
DEBUG: 'true', DEBUG: 'true',
}, },
// 生产环境 // 生产环境
production: { production: {
API_BASE_URL: 'https://cms-api.websoft.top/api', API_BASE_URL: 'https://mp-api.websoft.top/api',
APP_NAME: '桂乐淘', APP_NAME: '桂乐淘',
DEBUG: 'false', DEBUG: 'false',
}, },
// 测试环境 // 测试环境
test: { test: {
API_BASE_URL: 'https://cms-api.s209.websoft.top/api', API_BASE_URL: 'https://mp-api.s209.websoft.top/api',
APP_NAME: '测试环境', APP_NAME: '测试环境',
DEBUG: 'true', DEBUG: 'true',
} }

View File

@@ -31,5 +31,11 @@ export interface ShopDealerCapital {
*/ */
export interface ShopDealerCapitalParam extends PageParam { export interface ShopDealerCapitalParam extends PageParam {
id?: number; id?: number;
// 仅查询当前分销商的收益/资金明细
userId?: number;
// 可选:按订单过滤
orderId?: number;
// 可选:资金流动类型过滤
flowType?: number;
keywords?: string; keywords?: string;
} }

View File

@@ -8,6 +8,9 @@ export interface ShopDealerOrder {
id?: number; id?: number;
// 买家用户ID // 买家用户ID
userId?: number; userId?: number;
nickname?: string;
// 订单编号(部分接口会直接返回订单号字符串)
orderNo?: string;
// 订单ID // 订单ID
orderId?: number; orderId?: number;
// 订单总金额(不含运费) // 订单总金额(不含运费)
@@ -47,5 +50,7 @@ export interface ShopDealerOrderParam extends PageParam {
secondUserId?: number; secondUserId?: number;
thirdUserId?: number; thirdUserId?: number;
userId?: number; userId?: number;
// 数据权限/资源ID通常传当前登录用户ID
resourceId?: number;
keywords?: string; keywords?: string;
} }

View File

@@ -73,6 +73,7 @@ export default {
"apply/add", "apply/add",
"withdraw/index", "withdraw/index",
"orders/index", "orders/index",
"capital/index",
"team/index", "team/index",
"qrcode/index", "qrcode/index",
"invite-stats/index", "invite-stats/index",

View File

@@ -127,7 +127,7 @@ const AddUserAddress = () => {
} }
// 提交表单 // 提交表单
const submitSucceed = async (values: any) => { const submitSucceed = async (values: User) => {
try { try {
// 验证必填字段 // 验证必填字段
if (!values.phone && !FormData?.phone) { if (!values.phone && !FormData?.phone) {
@@ -192,7 +192,7 @@ const AddUserAddress = () => {
userId: user?.userId, userId: user?.userId,
realName: values.realName || FormData?.nickname, realName: values.realName || FormData?.nickname,
mobile: values.phone || FormData?.phone, mobile: values.phone || FormData?.phone,
refereeId: values.refereeId || FormData?.refereeId refereeId: Number(values.refereeId) || Number(FormData?.refereeId)
}) })
if (roles.length > 0) { if (roles.length > 0) {

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '收益明细'
})

View File

@@ -0,0 +1,2 @@
/* Intentionally empty: styling is done via utility classes. */

View File

@@ -0,0 +1,199 @@
import React, {useCallback, useEffect, useState} from 'react'
import {View, Text, ScrollView} from '@tarojs/components'
import {Empty, Tag, 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 PAGE_SIZE = 10
const DealerCapital: React.FC = () => {
const {dealerUser} = useDealerUser()
const [loading, setLoading] = useState(false)
const [refreshing, setRefreshing] = useState(false)
const [loadingMore, setLoadingMore] = useState(false)
const [currentPage, setCurrentPage] = useState(1)
const [hasMore, setHasMore] = useState(true)
const [records, setRecords] = useState<ShopDealerCapital[]>([])
const getFlowTypeText = (flowType?: number) => {
switch (flowType) {
case 10:
return '佣金收入'
case 20:
return '提现支出'
case 30:
return '转账支出'
case 40:
return '转账收入'
default:
return '资金变动'
}
}
const getFlowTypeTag = (flowType?: number) => {
// 收入success支出danger其它default
if (flowType === 10 || flowType === 40) return 'success'
if (flowType === 20 || flowType === 30) return 'danger'
return 'default'
}
const formatMoney = (flowType?: number, money?: string) => {
const isIncome = flowType === 10 || flowType === 40
const isExpense = flowType === 20 || flowType === 30
const sign = isIncome ? '+' : isExpense ? '-' : ''
return `${sign}${money || '0.00'}`
}
const fetchRecords = 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: PAGE_SIZE,
// 只显示与当前登录用户相关的收益明细
userId: dealerUser.userId
})
const list = result?.list || []
if (page === 1) {
setRecords(list)
} else {
setRecords(prev => [...prev, ...list])
}
setHasMore(list.length === PAGE_SIZE)
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 fetchRecords(1, true)
}
const handleLoadMore = async () => {
if (!loadingMore && hasMore) {
await fetchRecords(currentPage + 1)
}
}
useEffect(() => {
if (dealerUser?.userId) {
fetchRecords(1)
}
}, [fetchRecords, dealerUser?.userId])
if (!dealerUser) {
return (
<View className="bg-gray-50 min-h-screen flex items-center justify-center">
<Loading/>
<Text className="text-gray-500 mt-2">...</Text>
</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 && records.length === 0 ? (
<View className="text-center py-8">
<Loading/>
<Text className="text-gray-500 mt-2">...</Text>
</View>
) : records.length > 0 ? (
<>
{records.map((item) => (
<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">
{item.describe || '收益明细'}
</Text>
<Tag type={getFlowTypeTag(item.flowType)}>
{getFlowTypeText(item.flowType)}
</Tag>
</View>
<View className="flex justify-between items-center mb-1">
<Text className="text-sm text-gray-400">
</Text>
<Text
className={`text-sm font-semibold ${
item.flowType === 10 || item.flowType === 40 ? 'text-green-600' :
item.flowType === 20 || item.flowType === 30 ? 'text-red-500' :
'text-gray-700'
}`}
>
{formatMoney(item.flowType, item.money)}
</Text>
</View>
<View className="flex justify-between items-center">
<Text className="text-sm text-gray-400">
{/*用户:{item.userId ?? '-'}*/}
</Text>
<Text className="text-sm text-gray-400">
{item.createTime || '-'}
</Text>
</View>
</View>
))}
{loadingMore && (
<View className="text-center py-4">
<Loading/>
<Text className="text-gray-500 mt-1 text-sm">...</Text>
</View>
)}
{!hasMore && records.length > 0 && (
<View className="text-center py-4">
<Text className="text-gray-400 text-sm"></Text>
</View>
)}
</>
) : (
<Empty description="暂无收益明细"/>
)}
</View>
</ScrollView>
</PullToRefresh>
</View>
)
}
export default DealerCapital

View File

@@ -24,7 +24,8 @@ const DealerOrders: React.FC = () => {
// 获取订单数据 // 获取订单数据
const fetchOrders = useCallback(async (page: number = 1, isRefresh: boolean = false) => { const fetchOrders = useCallback(async (page: number = 1, isRefresh: boolean = false) => {
if (!dealerUser?.userId) return // 需要当前登录用户ID用于 resourceId 参数)
if (!dealerUser || !dealerUser.userId) return
try { try {
if (isRefresh) { if (isRefresh) {
@@ -37,14 +38,17 @@ const DealerOrders: React.FC = () => {
const result = await pageShopDealerOrder({ const result = await pageShopDealerOrder({
page, page,
limit: 10 limit: 10,
// 后端需要 resourceId=当前登录用户ID 才能正确过滤分销订单
resourceId: dealerUser.userId
}) })
if (result?.list) { if (result?.list) {
const newOrders = result.list.map(order => ({ const newOrders = result.list.map(order => ({
...order, ...order,
orderNo: `${order.orderId}`, // 优先使用接口返回的订单号;没有则降级展示 orderId
customerName: `用户${order.userId}`, orderNo: order.orderNo ?? (order.orderId != null ? String(order.orderId) : undefined),
customerName: `${order.nickname}${order.userId}`,
userCommission: order.firstMoney || '0.00' userCommission: order.firstMoney || '0.00'
})) }))
@@ -102,32 +106,37 @@ const DealerOrders: React.FC = () => {
return 'warning' return 'warning'
} }
const handleGoCapital = () => {
Taro.navigateTo({url: '/dealer/capital/index'})
}
const renderOrderItem = (order: OrderWithDetails) => ( const renderOrderItem = (order: OrderWithDetails) => (
<View key={order.id} className="bg-white rounded-lg p-4 mb-3 shadow-sm"> <View
key={order.id}
className="bg-white rounded-lg p-4 mb-3 shadow-sm"
onClick={handleGoCapital}
>
<View className="flex justify-between items-start mb-1"> <View className="flex justify-between items-start mb-1">
<Text className="font-semibold text-gray-800"> <Text className="font-semibold text-gray-800">
{order.orderNo} {order.orderNo || '-'}
</Text> </Text>
<Tag type={getStatusColor(order.isSettled, order.isInvalid)}> <Tag type={getStatusColor(order.isSettled, order.isInvalid)}>
{getStatusText(order.isSettled, order.isInvalid)} {getStatusText(order.isSettled, order.isInvalid)}
</Tag> </Tag>
</View> </View>
<View className="flex justify-between items-center mb-1"> {/*<View className="flex justify-between items-center mb-1">*/}
<Text className="text-sm text-gray-400"> {/* <Text className="text-sm text-gray-400">*/}
¥{order.orderPrice || '0.00'} {/* 订单金额:¥{order.orderPrice || '0.00'}*/}
</Text> {/* </Text>*/}
<Text className="text-sm text-orange-500 font-semibold"> {/*</View>*/}
¥{order.userCommission}
</Text>
</View>
<View className="flex justify-between items-center"> <View className="flex justify-between items-center">
<Text className="text-sm text-gray-400"> <Text className="text-sm text-gray-400">
{order.customerName} {order.createTime}
</Text> </Text>
<Text className="text-sm text-gray-400"> <Text className="text-sm text-gray-400">
{order.createTime} ¥{order.orderPrice || '0.00'}
</Text> </Text>
</View> </View>
</View> </View>