补分销中心页面
This commit is contained in:
@@ -42,6 +42,16 @@ export default defineAppConfig({
|
|||||||
"points/points"
|
"points/points"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"root": "dealer",
|
||||||
|
"pages": [
|
||||||
|
"index",
|
||||||
|
"withdraw/index",
|
||||||
|
"orders/index",
|
||||||
|
"team/index",
|
||||||
|
"qrcode/index"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"root": "shop",
|
"root": "shop",
|
||||||
"pages": [
|
"pages": [
|
||||||
|
|||||||
75
src/dealer/components/EarningsCard.tsx
Normal file
75
src/dealer/components/EarningsCard.tsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { View, Button } from '@tarojs/components'
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
interface EarningsCardProps {
|
||||||
|
availableAmount?: number
|
||||||
|
pendingAmount?: number
|
||||||
|
onWithdraw?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function EarningsCard({
|
||||||
|
availableAmount = 0.00,
|
||||||
|
pendingAmount = 0.00,
|
||||||
|
onWithdraw
|
||||||
|
}: EarningsCardProps) {
|
||||||
|
|
||||||
|
const handleWithdraw = () => {
|
||||||
|
if (onWithdraw) {
|
||||||
|
onWithdraw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="mx-4 mb-4">
|
||||||
|
<View
|
||||||
|
className="rounded-2xl p-4 relative overflow-hidden"
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(135deg, #8B5CF6 0%, #A855F7 50%, #C084FC 100%)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* 装饰性背景元素 */}
|
||||||
|
<View
|
||||||
|
className="absolute -top-4 -right-4 w-20 h-20 rounded-full opacity-20"
|
||||||
|
style={{ backgroundColor: 'rgba(255, 255, 255, 0.2)' }}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
className="absolute -bottom-6 -left-6 w-16 h-16 rounded-full opacity-10"
|
||||||
|
style={{ backgroundColor: 'rgba(255, 255, 255, 0.2)' }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<View className="relative z-10">
|
||||||
|
{/* 可提现金额 */}
|
||||||
|
<View className="mb-4">
|
||||||
|
<View className="text-white text-base opacity-90 mb-1">
|
||||||
|
可提现 {availableAmount.toFixed(2)} 元
|
||||||
|
</View>
|
||||||
|
<View className="text-white text-base opacity-90">
|
||||||
|
待提现 {pendingAmount.toFixed(2)} 元
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 提现按钮 */}
|
||||||
|
<View className="flex justify-end">
|
||||||
|
<Button
|
||||||
|
className="bg-white text-purple-600 px-6 py-2 rounded-full text-sm font-medium border-0"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'white',
|
||||||
|
color: '#8B5CF6',
|
||||||
|
fontSize: '14px',
|
||||||
|
fontWeight: '500',
|
||||||
|
borderRadius: '20px',
|
||||||
|
padding: '8px 24px',
|
||||||
|
border: 'none'
|
||||||
|
}}
|
||||||
|
onClick={handleWithdraw}
|
||||||
|
>
|
||||||
|
去提现
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EarningsCard
|
||||||
102
src/dealer/components/FunctionMenu.tsx
Normal file
102
src/dealer/components/FunctionMenu.tsx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import { View } from '@tarojs/components'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
|
||||||
|
interface MenuItem {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
icon: string
|
||||||
|
color: string
|
||||||
|
bgColor: string
|
||||||
|
onClick?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function FunctionMenu() {
|
||||||
|
const menuItems: MenuItem[] = [
|
||||||
|
{
|
||||||
|
id: 'withdraw-detail',
|
||||||
|
title: '提现明细',
|
||||||
|
icon: '💰',
|
||||||
|
color: '#F59E0B',
|
||||||
|
bgColor: '#FEF3C7',
|
||||||
|
onClick: () => {
|
||||||
|
Taro.navigateTo({
|
||||||
|
url: '/dealer/withdraw/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'distribution-orders',
|
||||||
|
title: '分销订单',
|
||||||
|
icon: '📋',
|
||||||
|
color: '#EF4444',
|
||||||
|
bgColor: '#FEE2E2',
|
||||||
|
onClick: () => {
|
||||||
|
Taro.navigateTo({
|
||||||
|
url: '/dealer/orders/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'my-team',
|
||||||
|
title: '我的团队',
|
||||||
|
icon: '👥',
|
||||||
|
color: '#10B981',
|
||||||
|
bgColor: '#D1FAE5',
|
||||||
|
onClick: () => {
|
||||||
|
Taro.navigateTo({
|
||||||
|
url: '/dealer/team/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'promotion-qr',
|
||||||
|
title: '推广二维码',
|
||||||
|
icon: '📱',
|
||||||
|
color: '#3B82F6',
|
||||||
|
bgColor: '#DBEAFE',
|
||||||
|
onClick: () => {
|
||||||
|
Taro.navigateTo({
|
||||||
|
url: '/dealer/qrcode/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const handleMenuClick = (item: MenuItem) => {
|
||||||
|
if (item.onClick) {
|
||||||
|
item.onClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="mx-4 mb-4">
|
||||||
|
<View className="bg-white rounded-2xl p-4 shadow-sm">
|
||||||
|
<View className="grid grid-cols-2 gap-4">
|
||||||
|
{menuItems.map((item) => (
|
||||||
|
<View
|
||||||
|
key={item.id}
|
||||||
|
className="flex flex-col items-center justify-center py-6 rounded-xl cursor-pointer"
|
||||||
|
style={{ backgroundColor: item.bgColor }}
|
||||||
|
onClick={() => handleMenuClick(item)}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
className="w-12 h-12 rounded-full flex items-center justify-center mb-2 text-2xl"
|
||||||
|
style={{ backgroundColor: 'white' }}
|
||||||
|
>
|
||||||
|
{item.icon}
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
className="text-base font-medium"
|
||||||
|
style={{ color: item.color }}
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FunctionMenu
|
||||||
78
src/dealer/components/NavigationBar.tsx
Normal file
78
src/dealer/components/NavigationBar.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { View } from '@tarojs/components'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
|
||||||
|
interface NavigationBarProps {
|
||||||
|
title?: string
|
||||||
|
showBack?: boolean
|
||||||
|
showMore?: boolean
|
||||||
|
onBack?: () => void
|
||||||
|
onMore?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function NavigationBar({
|
||||||
|
title = '分销中心',
|
||||||
|
showBack = true,
|
||||||
|
showMore = true,
|
||||||
|
onBack,
|
||||||
|
onMore
|
||||||
|
}: NavigationBarProps) {
|
||||||
|
|
||||||
|
const handleBack = () => {
|
||||||
|
if (onBack) {
|
||||||
|
onBack()
|
||||||
|
} else {
|
||||||
|
Taro.navigateBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMore = () => {
|
||||||
|
if (onMore) {
|
||||||
|
onMore()
|
||||||
|
} else {
|
||||||
|
Taro.showActionSheet({
|
||||||
|
itemList: ['分享', '设置', '帮助']
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="relative">
|
||||||
|
{/* 状态栏占位 */}
|
||||||
|
<View style={{ height: Taro.getSystemInfoSync().statusBarHeight + 'px' }} />
|
||||||
|
|
||||||
|
{/* 导航栏 */}
|
||||||
|
<View className="flex items-center justify-between px-4 py-3 relative z-10">
|
||||||
|
{/* 左侧返回按钮 */}
|
||||||
|
<View className="w-8 h-8 flex items-center justify-center">
|
||||||
|
{showBack && (
|
||||||
|
<View
|
||||||
|
className="w-6 h-6 flex items-center justify-center cursor-pointer"
|
||||||
|
onClick={handleBack}
|
||||||
|
>
|
||||||
|
<View className="text-white text-lg">‹</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 中间标题 */}
|
||||||
|
<View className="flex-1 text-center">
|
||||||
|
<View className="text-white text-xl font-medium">{title}</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 右侧更多按钮 */}
|
||||||
|
<View className="w-8 h-8 flex items-center justify-center">
|
||||||
|
{showMore && (
|
||||||
|
<View
|
||||||
|
className="w-6 h-6 flex items-center justify-center cursor-pointer"
|
||||||
|
onClick={handleMore}
|
||||||
|
>
|
||||||
|
<View className="text-white text-lg">⋯</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NavigationBar
|
||||||
211
src/dealer/components/OrderIcon.tsx
Normal file
211
src/dealer/components/OrderIcon.tsx
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
import {useEffect, useState} from 'react'
|
||||||
|
import {navigateTo} from '@tarojs/taro'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import {Button} from '@tarojs/components';
|
||||||
|
import {Image} from '@nutui/nutui-react-taro'
|
||||||
|
import {getUserInfo, getWxOpenId} from "@/api/layout";
|
||||||
|
import {TenantId} from "@/config/app";
|
||||||
|
import {User} from "@/api/system/user/model";
|
||||||
|
// import News from "./News";
|
||||||
|
import {myPageBszxBm} from "@/api/bszx/bszxBm";
|
||||||
|
import {listCmsNavigation} from "@/api/cms/cmsNavigation";
|
||||||
|
|
||||||
|
const OrderIcon = () => {
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [isLogin, setIsLogin] = useState<boolean>(false)
|
||||||
|
const [userInfo, setUserInfo] = useState<User>()
|
||||||
|
const [bmLogs, setBmLogs] = useState<any>()
|
||||||
|
const [navItems, setNavItems] = useState<any>([])
|
||||||
|
|
||||||
|
/* 获取用户手机号 */
|
||||||
|
const handleGetPhoneNumber = ({detail}) => {
|
||||||
|
const {code, encryptedData, iv} = detail
|
||||||
|
Taro.login({
|
||||||
|
success: function () {
|
||||||
|
if (code) {
|
||||||
|
Taro.request({
|
||||||
|
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
code,
|
||||||
|
encryptedData,
|
||||||
|
iv,
|
||||||
|
notVerifyPhone: true,
|
||||||
|
refereeId: 0,
|
||||||
|
sceneType: 'save_referee',
|
||||||
|
tenantId: TenantId
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
'content-type': 'application/json',
|
||||||
|
TenantId
|
||||||
|
},
|
||||||
|
success: function (res) {
|
||||||
|
Taro.setStorageSync('access_token', res.data.data.access_token)
|
||||||
|
Taro.setStorageSync('UserId', res.data.data.user.userId)
|
||||||
|
setUserInfo(res.data.data.user)
|
||||||
|
Taro.setStorageSync('Phone', res.data.data.user.phone)
|
||||||
|
setIsLogin(true)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '登录成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.log('登录失败!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onLogin = (item: any, index: number) => {
|
||||||
|
if(!isLogin){
|
||||||
|
return navigateTo({url: `/pages/category/category?id=${item.navigationId}`})
|
||||||
|
}else {
|
||||||
|
// 报名链接
|
||||||
|
if(index == 0){
|
||||||
|
console.log(bmLogs,'bmLogs')
|
||||||
|
if(bmLogs && bmLogs.length > 0){
|
||||||
|
return navigateTo({url: `/bszx/bm-cert/bm-cert?id=${bmLogs[0].id}`})
|
||||||
|
}else {
|
||||||
|
navigateTo({url: `/user/profile/profile`})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 善款明细
|
||||||
|
if(item.navigationId == 4119){
|
||||||
|
return navigateTo({url: `/bszx/pay-record/pay-record`})
|
||||||
|
}
|
||||||
|
return navigateTo({url: `/pages/category/category?id=${item.navigationId}`})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
// 读取栏目
|
||||||
|
listCmsNavigation({parentId: 2828,hide: 0}).then(res => {
|
||||||
|
console.log(res,'9999')
|
||||||
|
setNavItems(res);
|
||||||
|
})
|
||||||
|
Taro.getUserInfo({
|
||||||
|
success: (res) => {
|
||||||
|
const avatar = res.userInfo.avatarUrl;
|
||||||
|
setUserInfo({
|
||||||
|
avatar,
|
||||||
|
nickname: res.userInfo.nickName,
|
||||||
|
sexName: res.userInfo.gender == 1 ? '男' : '女'
|
||||||
|
})
|
||||||
|
getUserInfo().then((data) => {
|
||||||
|
if (data) {
|
||||||
|
setUserInfo(data)
|
||||||
|
setIsLogin(true);
|
||||||
|
console.log(userInfo, 'userInfo...')
|
||||||
|
Taro.setStorageSync('UserId', data.userId)
|
||||||
|
// 获取openId
|
||||||
|
if (!data.openid) {
|
||||||
|
Taro.login({
|
||||||
|
success: (res) => {
|
||||||
|
getWxOpenId({code: res.code}).then(() => {
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
console.log('未登录')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 报名日志
|
||||||
|
myPageBszxBm({limit: 1}).then(res => {
|
||||||
|
if (res.list) {
|
||||||
|
setBmLogs(res.list);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const showAuthModal = () => {
|
||||||
|
Taro.showModal({
|
||||||
|
title: '授权提示',
|
||||||
|
content: '需要获取您的用户信息',
|
||||||
|
confirmText: '去授权',
|
||||||
|
cancelText: '取消',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
// 用户点击确认,打开授权设置页面
|
||||||
|
openSetting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const openSetting = () => {
|
||||||
|
// Taro.openSetting:调起客户端小程序设置界面,返回用户设置的操作结果。设置界面只会出现小程序已经向用户请求过的权限。
|
||||||
|
Taro.openSetting({
|
||||||
|
success: (res) => {
|
||||||
|
if (res.authSetting['scope.userInfo']) {
|
||||||
|
// 用户授权成功,可以获取用户信息
|
||||||
|
reload();
|
||||||
|
} else {
|
||||||
|
// 用户拒绝授权,提示授权失败
|
||||||
|
Taro.showToast({
|
||||||
|
title: '授权失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Taro.getSetting({
|
||||||
|
success: (res) => {
|
||||||
|
if (res.authSetting['scope.userInfo']) {
|
||||||
|
// 用户已经授权过,可以直接获取用户信息
|
||||||
|
console.log('用户已经授权过,可以直接获取用户信息')
|
||||||
|
reload();
|
||||||
|
} else {
|
||||||
|
// 用户未授权,需要弹出授权窗口
|
||||||
|
console.log('用户未授权,需要弹出授权窗口')
|
||||||
|
showAuthModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reload();
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'my-3'}>
|
||||||
|
<div className={'pt-4 bg-yellow-50 rounded-2xl'}
|
||||||
|
style={{background: 'linear-gradient(to bottom, #ffffff, #ffffcc)'}}>
|
||||||
|
<div className={'flex justify-between pb-2 px-1'}>
|
||||||
|
{
|
||||||
|
navItems.map((item, index) => (
|
||||||
|
<div key={index} className={'text-center'}>
|
||||||
|
{
|
||||||
|
isLogin && !loading ?
|
||||||
|
<div className={'flex flex-col justify-center items-center'} onClick={() => {
|
||||||
|
onLogin(item, index)
|
||||||
|
}}>
|
||||||
|
<Image src={item.icon} height={28} width={28} lazyLoad={false}/>
|
||||||
|
<div className={'mt-2'} style={{fontSize: '15px'}}>{item?.title}</div>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<Button className={'text-white'} open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}>
|
||||||
|
<div className={'flex flex-col justify-center items-center'}>
|
||||||
|
<Image src={item.icon} height={28} width={28} lazyLoad={false}/>
|
||||||
|
<div className={'mt-2 text-gray-700'} style={{fontSize: '15px'}}>{item?.title}</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/*<image src={'https://oss.wsdns.cn/20250224/18a2f3b807c94aac8a67af34e95534d6.jpeg'} className={'book'}>倡议书</image>*/}
|
||||||
|
{/*<News id={categoryId}/>*/}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default OrderIcon
|
||||||
135
src/dealer/components/UserCard.tsx
Normal file
135
src/dealer/components/UserCard.tsx
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import {Avatar} from '@nutui/nutui-react-taro'
|
||||||
|
import {getUserInfo, getWxOpenId} from '@/api/layout';
|
||||||
|
import Taro from '@tarojs/taro';
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {User} from "@/api/system/user/model";
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
function UserCard() {
|
||||||
|
const [IsLogin, setIsLogin] = useState<boolean>(false)
|
||||||
|
const [userInfo, setUserInfo] = useState<User>()
|
||||||
|
const [referrerName, setReferrerName] = useState<string>('平台')
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Taro.getSetting:获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
|
||||||
|
Taro.getSetting({
|
||||||
|
success: (res) => {
|
||||||
|
if (res.authSetting['scope.userInfo']) {
|
||||||
|
// 用户已经授权过,可以直接获取用户信息
|
||||||
|
console.log('用户已经授权过,可以直接获取用户信息')
|
||||||
|
reload();
|
||||||
|
} else {
|
||||||
|
// 用户未授权,需要弹出授权窗口
|
||||||
|
console.log('用户未授权,需要弹出授权窗口')
|
||||||
|
showAuthModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
Taro.getUserInfo({
|
||||||
|
success: (res) => {
|
||||||
|
const avatar = res.userInfo.avatarUrl;
|
||||||
|
setUserInfo({
|
||||||
|
avatar,
|
||||||
|
nickname: res.userInfo.nickName,
|
||||||
|
sexName: res.userInfo.gender == 1 ? '男' : '女'
|
||||||
|
})
|
||||||
|
getUserInfo().then((data) => {
|
||||||
|
if (data) {
|
||||||
|
setUserInfo(data)
|
||||||
|
setIsLogin(true);
|
||||||
|
Taro.setStorageSync('UserId', data.userId)
|
||||||
|
|
||||||
|
// 获取openId
|
||||||
|
if (!data.openid) {
|
||||||
|
Taro.login({
|
||||||
|
success: (res) => {
|
||||||
|
getWxOpenId({code: res.code}).then(() => {
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 获取推荐人信息
|
||||||
|
const referrer = data.nickname || '平台';
|
||||||
|
setReferrerName(referrer)
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
console.log('未登录')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const showAuthModal = () => {
|
||||||
|
Taro.showModal({
|
||||||
|
title: '授权提示',
|
||||||
|
content: '需要获取您的用户信息',
|
||||||
|
confirmText: '去授权',
|
||||||
|
cancelText: '取消',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
// 用户点击确认,打开授权设置页面
|
||||||
|
openSetting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const openSetting = () => {
|
||||||
|
// Taro.openSetting:调起客户端小程序设置界面,返回用户设置的操作结果。设置界面只会出现小程序已经向用户请求过的权限。
|
||||||
|
Taro.openSetting({
|
||||||
|
success: (res) => {
|
||||||
|
if (res.authSetting['scope.userInfo']) {
|
||||||
|
// 用户授权成功,可以获取用户信息
|
||||||
|
reload();
|
||||||
|
} else {
|
||||||
|
// 用户拒绝授权,提示授权失败
|
||||||
|
Taro.showToast({
|
||||||
|
title: '授权失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="mx-4 mb-4">
|
||||||
|
<View className="bg-white rounded-2xl p-4 shadow-sm">
|
||||||
|
<View className="flex items-center mb-3">
|
||||||
|
<Avatar
|
||||||
|
size="60"
|
||||||
|
src={userInfo?.avatar || 'https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png'}
|
||||||
|
shape="round"
|
||||||
|
/>
|
||||||
|
<View className="ml-3 flex-1">
|
||||||
|
<View className="text-xl font-medium text-gray-800 mb-1">
|
||||||
|
{IsLogin ? (userInfo?.nickname || '小程序用户') : '小程序.App.网站.系统开发-邓'}
|
||||||
|
</View>
|
||||||
|
<View className="text-base text-gray-500">
|
||||||
|
推荐人:{referrerName}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="border-t border-gray-100 pt-3">
|
||||||
|
<View className="flex justify-between items-center">
|
||||||
|
<View className="text-base text-gray-600">
|
||||||
|
已提现金额
|
||||||
|
</View>
|
||||||
|
<View className="text-xl font-medium text-gray-800">
|
||||||
|
0.00元
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserCard;
|
||||||
148
src/dealer/components/UserCell.tsx
Normal file
148
src/dealer/components/UserCell.tsx
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import {Cell} from '@nutui/nutui-react-taro'
|
||||||
|
import navTo from "@/utils/common";
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import {ArrowRight, ShieldCheck, LogisticsError, Location, Reward, Tips, Ask} from '@nutui/icons-react-taro'
|
||||||
|
|
||||||
|
const UserCell = () => {
|
||||||
|
|
||||||
|
const onLogout = () => {
|
||||||
|
Taro.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '确定要退出登录吗?',
|
||||||
|
success: function (res) {
|
||||||
|
if (res.confirm) {
|
||||||
|
Taro.removeStorageSync('access_token')
|
||||||
|
Taro.removeStorageSync('TenantId')
|
||||||
|
Taro.removeStorageSync('UserId')
|
||||||
|
Taro.removeStorageSync('userInfo')
|
||||||
|
Taro.reLaunch({
|
||||||
|
url: '/pages/index/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={'px-4'}>
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
style={{
|
||||||
|
backgroundImage: 'linear-gradient(to right bottom, #54a799, #177b73)',
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
<div style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
|
<Reward className={'text-orange-100 '} size={16}/>
|
||||||
|
<div>
|
||||||
|
<span style={{ fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>开通会员</span>
|
||||||
|
</div>
|
||||||
|
<span className={'text-white opacity-80 pl-3'}>享优惠</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
/>
|
||||||
|
<Cell.Group divider={true} description={
|
||||||
|
<div style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
|
<span style={{marginTop: '12px'}}>我的服务</span>
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
style={{
|
||||||
|
display: 'none'
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
<div style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
|
<LogisticsError size={16}/>
|
||||||
|
<span className={'pl-3 text-sm'}>我的钱包</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
align="center"
|
||||||
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
onClick={() => {
|
||||||
|
navTo('/user/wallet/index', true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
title={
|
||||||
|
<div style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
|
<Location size={16}/>
|
||||||
|
<span className={'pl-3 text-sm'}>收货地址</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
align="center"
|
||||||
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
onClick={() => {
|
||||||
|
navTo('/user/address/index', true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
title={
|
||||||
|
<div style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
|
<ShieldCheck size={16}/>
|
||||||
|
<span className={'pl-3 text-sm'}>实名认证</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
align="center"
|
||||||
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
onClick={() => {
|
||||||
|
navTo('/user/userVerify/index', true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
title={
|
||||||
|
<div style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
|
<Ask size={16}/>
|
||||||
|
<span className={'pl-3 text-sm'}>常见问题</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
align="center"
|
||||||
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
onClick={() => {
|
||||||
|
navTo('/user/help/index')
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
title={
|
||||||
|
<div style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
|
<Tips size={16}/>
|
||||||
|
<span className={'pl-3 text-sm'}>关于我们</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
align="center"
|
||||||
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
onClick={() => {
|
||||||
|
navTo('/user/about/index')
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Cell.Group>
|
||||||
|
<Cell.Group divider={true} description={
|
||||||
|
<div style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
|
<span style={{marginTop: '12px'}}>账号管理</span>
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
title="账号安全"
|
||||||
|
align="center"
|
||||||
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
onClick={() => navTo('/user/profile/profile', true)}
|
||||||
|
/>
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
title="退出登录"
|
||||||
|
align="center"
|
||||||
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
onClick={onLogout}
|
||||||
|
/>
|
||||||
|
</Cell.Group>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default UserCell
|
||||||
102
src/dealer/components/UserFooter.tsx
Normal file
102
src/dealer/components/UserFooter.tsx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import {loginBySms} from "@/api/passport/login";
|
||||||
|
import {useState} from "react";
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import {Popup} from '@nutui/nutui-react-taro'
|
||||||
|
import {UserParam} from "@/api/system/user/model";
|
||||||
|
import {Button} from '@nutui/nutui-react-taro'
|
||||||
|
import {Form, Input} from '@nutui/nutui-react-taro'
|
||||||
|
import {Copyright, Version} from "@/config/app";
|
||||||
|
const UserFooter = () => {
|
||||||
|
const [openLoginByPhone, setOpenLoginByPhone] = useState(false)
|
||||||
|
const [clickNum, setClickNum] = useState<number>(0)
|
||||||
|
const [FormData, setFormData] = useState<UserParam>(
|
||||||
|
{
|
||||||
|
phone: undefined,
|
||||||
|
password: undefined
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const onLoginByPhone = () => {
|
||||||
|
setFormData({})
|
||||||
|
setClickNum(clickNum + 1);
|
||||||
|
if (clickNum > 10) {
|
||||||
|
setOpenLoginByPhone(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeLoginByPhone = () => {
|
||||||
|
setClickNum(0)
|
||||||
|
setOpenLoginByPhone(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const submitByPhone = (values: any) => {
|
||||||
|
loginBySms({
|
||||||
|
phone: values.phone,
|
||||||
|
code: values.code
|
||||||
|
}).then(() => {
|
||||||
|
setOpenLoginByPhone(false);
|
||||||
|
setTimeout(() => {
|
||||||
|
Taro.reLaunch({
|
||||||
|
url: '/pages/index/index'
|
||||||
|
})
|
||||||
|
},1000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={'text-center py-4 w-full text-gray-300'} onClick={onLoginByPhone}>
|
||||||
|
<div className={'text-xs text-gray-400 py-1'}>当前版本:{Version}</div>
|
||||||
|
<div className={'text-xs text-gray-400 py-1'}>Copyright © { new Date().getFullYear() } {Copyright}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Popup
|
||||||
|
style={{width: '350px', padding: '10px'}}
|
||||||
|
visible={openLoginByPhone}
|
||||||
|
closeOnOverlayClick={false}
|
||||||
|
closeable={true}
|
||||||
|
onClose={closeLoginByPhone}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
style={{width: '350px',padding: '10px'}}
|
||||||
|
divider
|
||||||
|
initialValues={FormData}
|
||||||
|
labelPosition="left"
|
||||||
|
onFinish={(values) => submitByPhone(values)}
|
||||||
|
footer={
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button nativeType="submit" block style={{backgroundColor: '#000000',color: '#ffffff'}}>
|
||||||
|
提交
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
label={'手机号码'}
|
||||||
|
name="phone"
|
||||||
|
required
|
||||||
|
rules={[{message: '手机号码'}]}
|
||||||
|
>
|
||||||
|
<Input placeholder="请输入手机号码" maxLength={11} type="text"/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={'短信验证码'}
|
||||||
|
name="code"
|
||||||
|
required
|
||||||
|
rules={[{message: '短信验证码'}]}
|
||||||
|
>
|
||||||
|
<Input placeholder="请输入短信验证码" maxLength={6} type="text"/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Popup>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default UserFooter
|
||||||
3
src/dealer/index.config.ts
Normal file
3
src/dealer/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '分销中心'
|
||||||
|
})
|
||||||
77
src/dealer/index.scss
Normal file
77
src/dealer/index.scss
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// 分销中心页面样式
|
||||||
|
.dealer-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: linear-gradient(180deg, #60A5FA 0%, #3B82F6 50%, #1D4ED8 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导航栏样式
|
||||||
|
.navigation-bar {
|
||||||
|
position: relative;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户卡片样式
|
||||||
|
.user-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
margin: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收益卡片样式
|
||||||
|
.earnings-card {
|
||||||
|
background: linear-gradient(135deg, #8B5CF6 0%, #A855F7 50%, #C084FC 100%);
|
||||||
|
border-radius: 16px;
|
||||||
|
margin: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 功能菜单样式
|
||||||
|
.function-menu {
|
||||||
|
background: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
margin: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.menu-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 24px 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-text {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/dealer/index.tsx
Normal file
64
src/dealer/index.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import {useEffect} from 'react'
|
||||||
|
import { View } from '@tarojs/components'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import NavigationBar from "./components/NavigationBar"
|
||||||
|
import UserCard from "./components/UserCard"
|
||||||
|
import EarningsCard from "./components/EarningsCard"
|
||||||
|
import FunctionMenu from "./components/FunctionMenu"
|
||||||
|
import './index.scss'
|
||||||
|
function Index() {
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 设置页面标题
|
||||||
|
Taro.setNavigationBarTitle({
|
||||||
|
title: '分销中心'
|
||||||
|
})
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleWithdraw = () => {
|
||||||
|
Taro.showModal({
|
||||||
|
title: '提现',
|
||||||
|
content: '确定要进行提现操作吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '提现申请已提交',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBack = () => {
|
||||||
|
Taro.navigateBack({
|
||||||
|
delta: 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMore = () => {
|
||||||
|
Taro.showActionSheet({
|
||||||
|
itemList: ['分享给朋友', '客服咨询', '使用帮助'],
|
||||||
|
success: (res) => {
|
||||||
|
const actions = ['分享给朋友', '客服咨询', '使用帮助']
|
||||||
|
Taro.showToast({
|
||||||
|
title: actions[res.tapIndex],
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="min-h-screen" style={{
|
||||||
|
paddingTop: '20px',
|
||||||
|
background: 'linear-gradient(180deg, #60A5FA 0%, #3B82F6 50%, #1D4ED8 100%)'
|
||||||
|
}}>
|
||||||
|
<UserCard />
|
||||||
|
<EarningsCard onWithdraw={handleWithdraw} />
|
||||||
|
<FunctionMenu />
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Index
|
||||||
145
src/dealer/orders/index.scss
Normal file
145
src/dealer/orders/index.scss
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
.distribution-orders-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-card {
|
||||||
|
background: white;
|
||||||
|
margin: 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 20px; // 对应 text-xl
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1f2937;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 16px; // 对应 text-base
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders-container {
|
||||||
|
margin: 0 16px;
|
||||||
|
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders-list {
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
.order-item {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.order-no {
|
||||||
|
font-size: 14px; // 对应 text-sm
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status {
|
||||||
|
font-size: 14px; // 对应 text-sm
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-content {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.product-image {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-right: 12px;
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-info {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.product-name {
|
||||||
|
font-size: 20px; // 对应 text-xl,主要标题
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1f2937;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buyer-info {
|
||||||
|
font-size: 16px; // 对应 text-base
|
||||||
|
color: #6b7280;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-info {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.order-amount {
|
||||||
|
font-size: 16px; // 对应 text-base
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commission {
|
||||||
|
font-size: 20px; // 对应 text-xl,重要数字
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ef4444;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-time {
|
||||||
|
font-size: 14px; // 对应 text-sm
|
||||||
|
color: #9ca3af;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
238
src/dealer/orders/index.tsx
Normal file
238
src/dealer/orders/index.tsx
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { View, Image } from '@tarojs/components'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import { Empty, Tabs, TabPane } from '@nutui/nutui-react-taro'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
interface DistributionOrder {
|
||||||
|
id: string
|
||||||
|
orderNo: string
|
||||||
|
productName: string
|
||||||
|
productImage: string
|
||||||
|
buyerName: string
|
||||||
|
orderAmount: number
|
||||||
|
commission: number
|
||||||
|
commissionRate: number
|
||||||
|
status: 'pending' | 'confirmed' | 'settled'
|
||||||
|
statusText: string
|
||||||
|
createTime: string
|
||||||
|
settleTime?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function DistributionOrders() {
|
||||||
|
const [activeTab, setActiveTab] = useState('0')
|
||||||
|
const [orders, setOrders] = useState<DistributionOrder[]>([])
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [stats, setStats] = useState({
|
||||||
|
totalCommission: 0,
|
||||||
|
pendingCommission: 0,
|
||||||
|
settledCommission: 0,
|
||||||
|
totalOrders: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Taro.setNavigationBarTitle({
|
||||||
|
title: '分销订单'
|
||||||
|
})
|
||||||
|
loadOrders()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const loadOrders = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true)
|
||||||
|
// 模拟数据
|
||||||
|
const mockData: DistributionOrder[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
orderNo: 'DD202401150001',
|
||||||
|
productName: '有机蔬菜礼盒装',
|
||||||
|
productImage: 'https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png',
|
||||||
|
buyerName: '张***',
|
||||||
|
orderAmount: 299.00,
|
||||||
|
commission: 29.90,
|
||||||
|
commissionRate: 10,
|
||||||
|
status: 'settled',
|
||||||
|
statusText: '已结算',
|
||||||
|
createTime: '2024-01-15 14:30:00',
|
||||||
|
settleTime: '2024-01-16 10:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
orderNo: 'DD202401140002',
|
||||||
|
productName: '新鲜水果组合',
|
||||||
|
productImage: 'https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png',
|
||||||
|
buyerName: '李***',
|
||||||
|
orderAmount: 158.00,
|
||||||
|
commission: 15.80,
|
||||||
|
commissionRate: 10,
|
||||||
|
status: 'confirmed',
|
||||||
|
statusText: '已确认',
|
||||||
|
createTime: '2024-01-14 09:20:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
orderNo: 'DD202401130003',
|
||||||
|
productName: '农家土鸡蛋',
|
||||||
|
productImage: 'https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png',
|
||||||
|
buyerName: '王***',
|
||||||
|
orderAmount: 88.00,
|
||||||
|
commission: 8.80,
|
||||||
|
commissionRate: 10,
|
||||||
|
status: 'pending',
|
||||||
|
statusText: '待确认',
|
||||||
|
createTime: '2024-01-13 16:45:00'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 计算统计数据
|
||||||
|
const totalCommission = mockData.reduce((sum, order) => sum + order.commission, 0)
|
||||||
|
const pendingCommission = mockData.filter(o => o.status === 'pending').reduce((sum, order) => sum + order.commission, 0)
|
||||||
|
const settledCommission = mockData.filter(o => o.status === 'settled').reduce((sum, order) => sum + order.commission, 0)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setOrders(mockData)
|
||||||
|
setStats({
|
||||||
|
totalCommission,
|
||||||
|
pendingCommission,
|
||||||
|
settledCommission,
|
||||||
|
totalOrders: mockData.length
|
||||||
|
})
|
||||||
|
setLoading(false)
|
||||||
|
}, 1000)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载分销订单失败:', error)
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusColor = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'settled':
|
||||||
|
return '#10B981'
|
||||||
|
case 'confirmed':
|
||||||
|
return '#3B82F6'
|
||||||
|
case 'pending':
|
||||||
|
return '#F59E0B'
|
||||||
|
default:
|
||||||
|
return '#6B7280'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFilteredOrders = () => {
|
||||||
|
switch (activeTab) {
|
||||||
|
case '1':
|
||||||
|
return orders.filter(order => order.status === 'pending')
|
||||||
|
case '2':
|
||||||
|
return orders.filter(order => order.status === 'confirmed')
|
||||||
|
case '3':
|
||||||
|
return orders.filter(order => order.status === 'settled')
|
||||||
|
default:
|
||||||
|
return orders
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOrderClick = (order: DistributionOrder) => {
|
||||||
|
Taro.showModal({
|
||||||
|
title: '订单详情',
|
||||||
|
content: `
|
||||||
|
订单号:${order.orderNo}
|
||||||
|
商品:${order.productName}
|
||||||
|
购买人:${order.buyerName}
|
||||||
|
订单金额:¥${order.orderAmount.toFixed(2)}
|
||||||
|
佣金比例:${order.commissionRate}%
|
||||||
|
佣金金额:¥${order.commission.toFixed(2)}
|
||||||
|
下单时间:${order.createTime}
|
||||||
|
${order.settleTime ? `结算时间:${order.settleTime}` : ''}
|
||||||
|
`.trim(),
|
||||||
|
showCancel: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<View className="distribution-orders-page">
|
||||||
|
<View className="loading-container">
|
||||||
|
<View className="text-center text-gray-500">加载中...</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="distribution-orders-page">
|
||||||
|
{/* 统计卡片 */}
|
||||||
|
<View className="stats-card">
|
||||||
|
<View className="stats-grid">
|
||||||
|
<View className="stat-item">
|
||||||
|
<View className="stat-value">¥{stats.totalCommission.toFixed(2)}</View>
|
||||||
|
<View className="stat-label">累计佣金</View>
|
||||||
|
</View>
|
||||||
|
<View className="stat-item">
|
||||||
|
<View className="stat-value">¥{stats.settledCommission.toFixed(2)}</View>
|
||||||
|
<View className="stat-label">已结算</View>
|
||||||
|
</View>
|
||||||
|
<View className="stat-item">
|
||||||
|
<View className="stat-value">¥{stats.pendingCommission.toFixed(2)}</View>
|
||||||
|
<View className="stat-label">待结算</View>
|
||||||
|
</View>
|
||||||
|
<View className="stat-item">
|
||||||
|
<View className="stat-value">{stats.totalOrders}</View>
|
||||||
|
<View className="stat-label">订单数</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 订单列表 */}
|
||||||
|
<View className="orders-container">
|
||||||
|
<Tabs value={activeTab} onChange={(value) => setActiveTab(value)}>
|
||||||
|
<TabPane title="全部" />
|
||||||
|
<TabPane title="待确认" />
|
||||||
|
<TabPane title="已确认" />
|
||||||
|
<TabPane title="已结算" />
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
<View className="orders-list">
|
||||||
|
{getFilteredOrders().length === 0 ? (
|
||||||
|
<View className="empty-container">
|
||||||
|
<Empty description="暂无订单" />
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
getFilteredOrders().map((order) => (
|
||||||
|
<View
|
||||||
|
key={order.id}
|
||||||
|
className="order-item"
|
||||||
|
onClick={() => handleOrderClick(order)}
|
||||||
|
>
|
||||||
|
<View className="order-header">
|
||||||
|
<View className="order-no">订单号:{order.orderNo}</View>
|
||||||
|
<View
|
||||||
|
className="order-status"
|
||||||
|
style={{ color: getStatusColor(order.status) }}
|
||||||
|
>
|
||||||
|
{order.statusText}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="order-content">
|
||||||
|
<Image className="product-image" src={order.productImage} />
|
||||||
|
<View className="order-info">
|
||||||
|
<View className="product-name">{order.productName}</View>
|
||||||
|
<View className="buyer-info">购买人:{order.buyerName}</View>
|
||||||
|
<View className="amount-info">
|
||||||
|
<View className="order-amount">订单金额:¥{order.orderAmount.toFixed(2)}</View>
|
||||||
|
<View className="commission">佣金:¥{order.commission.toFixed(2)}</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="order-time">下单时间:{order.createTime}</View>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DistributionOrders
|
||||||
247
src/dealer/qrcode/index.scss
Normal file
247
src/dealer/qrcode/index.scss
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
.promotion-qrcode-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.user-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
font-size: 20px; // 对应 text-xl
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1f2937;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-code {
|
||||||
|
font-size: 16px; // 对应 text-base
|
||||||
|
color: #6b7280;
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 6px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-container {
|
||||||
|
.qrcode-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.qrcode-header {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 20px; // 对应 text-xl
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1f2937;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 16px; // 对应 text-base
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-wrapper {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.qrcode-loading {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #f9fafb;
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-image-container {
|
||||||
|
.qrcode-placeholder {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
margin: 0 auto 12px;
|
||||||
|
background: white;
|
||||||
|
border: 2px solid #e5e7eb;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.qr-pattern {
|
||||||
|
width: 160px;
|
||||||
|
height: 160px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.qr-corner {
|
||||||
|
position: absolute;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border: 3px solid #1f2937;
|
||||||
|
|
||||||
|
&.top-left {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.top-right {
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
border-left: none;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bottom-left {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
border-right: none;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-dots {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, 1fr);
|
||||||
|
gap: 8px;
|
||||||
|
padding: 40px 20px;
|
||||||
|
|
||||||
|
.qr-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background-color: #1f2937;
|
||||||
|
border-radius: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-tip {
|
||||||
|
font-size: 14px; // 对应 text-sm
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-info {
|
||||||
|
.info-item {
|
||||||
|
text-align: left;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #f9fafb;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-size: 16px; // 对应 text-base
|
||||||
|
color: #6b7280;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-size: 14px; // 对应 text-sm
|
||||||
|
color: #1f2937;
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.secondary {
|
||||||
|
background: white;
|
||||||
|
color: #3b82f6;
|
||||||
|
border: 1px solid #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tertiary {
|
||||||
|
background: #f3f4f6;
|
||||||
|
color: #6b7280;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-tips {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.tips-title {
|
||||||
|
font-size: 20px; // 对应 text-xl
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1f2937;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips-list {
|
||||||
|
.tip-item {
|
||||||
|
font-size: 16px; // 对应 text-base
|
||||||
|
color: #6b7280;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding-left: 8px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 8px;
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
background-color: #3b82f6;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
221
src/dealer/qrcode/index.tsx
Normal file
221
src/dealer/qrcode/index.tsx
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { View, Canvas, Image } from '@tarojs/components'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import { Button } from '@nutui/nutui-react-taro'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
interface UserInfo {
|
||||||
|
id: string
|
||||||
|
nickname: string
|
||||||
|
avatar: string
|
||||||
|
inviteCode: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function PromotionQRCode() {
|
||||||
|
const [userInfo, setUserInfo] = useState<UserInfo>({
|
||||||
|
id: '12345',
|
||||||
|
nickname: '分销达人',
|
||||||
|
avatar: 'https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png',
|
||||||
|
inviteCode: 'INV12345'
|
||||||
|
})
|
||||||
|
const [qrCodeUrl, setQrCodeUrl] = useState('')
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Taro.setNavigationBarTitle({
|
||||||
|
title: '推广二维码'
|
||||||
|
})
|
||||||
|
generateQRCode()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const generateQRCode = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true)
|
||||||
|
// 模拟生成二维码
|
||||||
|
// 实际项目中应该调用后端API生成包含邀请码的二维码
|
||||||
|
const inviteUrl = `https://your-domain.com/invite?code=${userInfo.inviteCode}`
|
||||||
|
|
||||||
|
// 这里使用一个模拟的二维码图片
|
||||||
|
// 实际项目中可以使用二维码生成库或调用API
|
||||||
|
const mockQRCode = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setQrCodeUrl(mockQRCode)
|
||||||
|
setLoading(false)
|
||||||
|
}, 1000)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成二维码失败:', error)
|
||||||
|
setLoading(false)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '生成失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSaveImage = async () => {
|
||||||
|
try {
|
||||||
|
if (!qrCodeUrl) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '二维码未生成',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在实际项目中,这里应该将二维码保存到相册
|
||||||
|
Taro.showModal({
|
||||||
|
title: '保存二维码',
|
||||||
|
content: '是否保存二维码到相册?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
// 实际保存逻辑
|
||||||
|
Taro.showToast({
|
||||||
|
title: '保存成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存图片失败:', error)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '保存失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleShareQRCode = () => {
|
||||||
|
Taro.showActionSheet({
|
||||||
|
itemList: ['分享给朋友', '分享到朋友圈', '复制邀请链接'],
|
||||||
|
success: (res) => {
|
||||||
|
const actions = ['分享给朋友', '分享到朋友圈', '复制邀请链接']
|
||||||
|
const action = actions[res.tapIndex]
|
||||||
|
|
||||||
|
if (action === '复制邀请链接') {
|
||||||
|
const inviteUrl = `https://your-domain.com/invite?code=${userInfo.inviteCode}`
|
||||||
|
Taro.setClipboardData({
|
||||||
|
data: inviteUrl,
|
||||||
|
success: () => {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '链接已复制',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Taro.showToast({
|
||||||
|
title: action,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRefreshQRCode = () => {
|
||||||
|
Taro.showModal({
|
||||||
|
title: '刷新二维码',
|
||||||
|
content: '确定要重新生成二维码吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
generateQRCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="promotion-qrcode-page">
|
||||||
|
{/* 用户信息卡片 */}
|
||||||
|
<View className="user-card">
|
||||||
|
<Image className="user-avatar" src={userInfo.avatar} />
|
||||||
|
<View className="user-info">
|
||||||
|
<View className="user-name">{userInfo.nickname}</View>
|
||||||
|
<View className="invite-code">邀请码:{userInfo.inviteCode}</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 二维码展示区域 */}
|
||||||
|
<View className="qrcode-container">
|
||||||
|
<View className="qrcode-card">
|
||||||
|
<View className="qrcode-header">
|
||||||
|
<View className="title">我的专属推广二维码</View>
|
||||||
|
<View className="subtitle">扫码注册成为我的下级</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="qrcode-wrapper">
|
||||||
|
{loading ? (
|
||||||
|
<View className="qrcode-loading">
|
||||||
|
<View className="loading-text">生成中...</View>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<View className="qrcode-image-container">
|
||||||
|
{/* 实际项目中这里应该显示真实的二维码 */}
|
||||||
|
<View className="qrcode-placeholder">
|
||||||
|
<View className="qr-pattern">
|
||||||
|
<View className="qr-corner top-left"></View>
|
||||||
|
<View className="qr-corner top-right"></View>
|
||||||
|
<View className="qr-corner bottom-left"></View>
|
||||||
|
<View className="qr-dots">
|
||||||
|
{Array.from({ length: 25 }).map((_, index) => (
|
||||||
|
<View key={index} className="qr-dot"></View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="qrcode-tip">长按识别二维码</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="qrcode-info">
|
||||||
|
<View className="info-item">
|
||||||
|
<View className="info-label">邀请链接</View>
|
||||||
|
<View className="info-value">https://your-domain.com/invite?code={userInfo.inviteCode}</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 操作按钮 */}
|
||||||
|
<View className="action-buttons">
|
||||||
|
<Button
|
||||||
|
className="action-btn primary"
|
||||||
|
onClick={handleSaveImage}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
保存到相册
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="action-btn secondary"
|
||||||
|
onClick={handleShareQRCode}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
分享二维码
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="action-btn tertiary"
|
||||||
|
onClick={handleRefreshQRCode}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
刷新二维码
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 使用说明 */}
|
||||||
|
<View className="usage-tips">
|
||||||
|
<View className="tips-title">使用说明</View>
|
||||||
|
<View className="tips-list">
|
||||||
|
<View className="tip-item">1. 分享二维码给好友,好友扫码注册成为您的下级</View>
|
||||||
|
<View className="tip-item">2. 下级用户的消费订单将为您带来佣金收益</View>
|
||||||
|
<View className="tip-item">3. 可以保存二维码图片或复制邀请链接进行推广</View>
|
||||||
|
<View className="tip-item">4. 二维码长期有效,可重复使用</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PromotionQRCode
|
||||||
176
src/dealer/team/index.scss
Normal file
176
src/dealer/team/index.scss
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
.my-team-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-stats {
|
||||||
|
background: white;
|
||||||
|
margin: 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 20px; // 对应 text-xl
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1f2937;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 16px; // 对应 text-base
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-stats {
|
||||||
|
background: white;
|
||||||
|
margin: 0 16px 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.level-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 0;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-info {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.level-title {
|
||||||
|
font-size: 20px; // 对应 text-xl
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-count {
|
||||||
|
font-size: 20px; // 对应 text-xl
|
||||||
|
font-weight: bold;
|
||||||
|
color: #3b82f6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.members-container {
|
||||||
|
margin: 0 16px;
|
||||||
|
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.members-list {
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
.member-item {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-info {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 12px;
|
||||||
|
|
||||||
|
.member-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.member-name {
|
||||||
|
font-size: 20px; // 对应 text-xl,成员名称是重要信息
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-level {
|
||||||
|
font-size: 14px; // 对应 text-sm
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&.level-1 {
|
||||||
|
background-color: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.level-2 {
|
||||||
|
background-color: #10b981;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-stats {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
|
||||||
|
.stat {
|
||||||
|
font-size: 16px; // 对应 text-base
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-time {
|
||||||
|
font-size: 14px; // 对应 text-sm
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-status {
|
||||||
|
font-size: 14px; // 对应 text-sm
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #d1fae5;
|
||||||
|
color: #10b981;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.inactive {
|
||||||
|
background-color: #fee2e2;
|
||||||
|
color: #ef4444;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
244
src/dealer/team/index.tsx
Normal file
244
src/dealer/team/index.tsx
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { View } from '@tarojs/components'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import { Avatar, Empty, Tabs, TabPane } from '@nutui/nutui-react-taro'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
interface TeamMember {
|
||||||
|
id: string
|
||||||
|
nickname: string
|
||||||
|
avatar: string
|
||||||
|
joinTime: string
|
||||||
|
level: number
|
||||||
|
orderCount: number
|
||||||
|
totalCommission: number
|
||||||
|
status: 'active' | 'inactive'
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TeamStats {
|
||||||
|
totalMembers: number
|
||||||
|
activeMembers: number
|
||||||
|
level1Members: number
|
||||||
|
level2Members: number
|
||||||
|
totalCommission: number
|
||||||
|
monthCommission: number
|
||||||
|
}
|
||||||
|
|
||||||
|
function MyTeam() {
|
||||||
|
const [activeTab, setActiveTab] = useState('0')
|
||||||
|
const [members, setMembers] = useState<TeamMember[]>([])
|
||||||
|
const [stats, setStats] = useState<TeamStats>({
|
||||||
|
totalMembers: 0,
|
||||||
|
activeMembers: 0,
|
||||||
|
level1Members: 0,
|
||||||
|
level2Members: 0,
|
||||||
|
totalCommission: 0,
|
||||||
|
monthCommission: 0
|
||||||
|
})
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Taro.setNavigationBarTitle({
|
||||||
|
title: '我的团队'
|
||||||
|
})
|
||||||
|
loadTeamData()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const loadTeamData = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true)
|
||||||
|
// 模拟数据
|
||||||
|
const mockMembers: TeamMember[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
nickname: '张小明',
|
||||||
|
avatar: 'https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png',
|
||||||
|
joinTime: '2024-01-15',
|
||||||
|
level: 1,
|
||||||
|
orderCount: 15,
|
||||||
|
totalCommission: 150.50,
|
||||||
|
status: 'active'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
nickname: '李小红',
|
||||||
|
avatar: 'https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png',
|
||||||
|
joinTime: '2024-01-10',
|
||||||
|
level: 1,
|
||||||
|
orderCount: 8,
|
||||||
|
totalCommission: 89.20,
|
||||||
|
status: 'active'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
nickname: '王小华',
|
||||||
|
avatar: 'https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png',
|
||||||
|
joinTime: '2024-01-08',
|
||||||
|
level: 2,
|
||||||
|
orderCount: 3,
|
||||||
|
totalCommission: 25.80,
|
||||||
|
status: 'inactive'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
nickname: '赵小刚',
|
||||||
|
avatar: 'https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png',
|
||||||
|
joinTime: '2024-01-05',
|
||||||
|
level: 2,
|
||||||
|
orderCount: 12,
|
||||||
|
totalCommission: 98.60,
|
||||||
|
status: 'active'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 计算统计数据
|
||||||
|
const totalMembers = mockMembers.length
|
||||||
|
const activeMembers = mockMembers.filter(m => m.status === 'active').length
|
||||||
|
const level1Members = mockMembers.filter(m => m.level === 1).length
|
||||||
|
const level2Members = mockMembers.filter(m => m.level === 2).length
|
||||||
|
const totalCommission = mockMembers.reduce((sum, m) => sum + m.totalCommission, 0)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setMembers(mockMembers)
|
||||||
|
setStats({
|
||||||
|
totalMembers,
|
||||||
|
activeMembers,
|
||||||
|
level1Members,
|
||||||
|
level2Members,
|
||||||
|
totalCommission,
|
||||||
|
monthCommission: totalCommission * 0.3 // 模拟本月佣金
|
||||||
|
})
|
||||||
|
setLoading(false)
|
||||||
|
}, 1000)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载团队数据失败:', error)
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFilteredMembers = () => {
|
||||||
|
switch (activeTab) {
|
||||||
|
case '1':
|
||||||
|
return members.filter(member => member.level === 1)
|
||||||
|
case '2':
|
||||||
|
return members.filter(member => member.level === 2)
|
||||||
|
case '3':
|
||||||
|
return members.filter(member => member.status === 'active')
|
||||||
|
default:
|
||||||
|
return members
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMemberClick = (member: TeamMember) => {
|
||||||
|
Taro.showModal({
|
||||||
|
title: '成员详情',
|
||||||
|
content: `
|
||||||
|
昵称:${member.nickname}
|
||||||
|
加入时间:${member.joinTime}
|
||||||
|
等级:${member.level}级下线
|
||||||
|
订单数量:${member.orderCount}
|
||||||
|
累计佣金:¥${member.totalCommission.toFixed(2)}
|
||||||
|
状态:${member.status === 'active' ? '活跃' : '不活跃'}
|
||||||
|
`.trim(),
|
||||||
|
showCancel: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<View className="my-team-page">
|
||||||
|
<View className="loading-container">
|
||||||
|
<View className="text-center text-gray-500">加载中...</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="my-team-page">
|
||||||
|
{/* 团队统计 */}
|
||||||
|
<View className="team-stats">
|
||||||
|
<View className="stats-grid">
|
||||||
|
<View className="stat-item">
|
||||||
|
<View className="stat-value">{stats.totalMembers}</View>
|
||||||
|
<View className="stat-label">团队总人数</View>
|
||||||
|
</View>
|
||||||
|
<View className="stat-item">
|
||||||
|
<View className="stat-value">{stats.activeMembers}</View>
|
||||||
|
<View className="stat-label">活跃成员</View>
|
||||||
|
</View>
|
||||||
|
<View className="stat-item">
|
||||||
|
<View className="stat-value">¥{stats.totalCommission.toFixed(2)}</View>
|
||||||
|
<View className="stat-label">累计佣金</View>
|
||||||
|
</View>
|
||||||
|
<View className="stat-item">
|
||||||
|
<View className="stat-value">¥{stats.monthCommission.toFixed(2)}</View>
|
||||||
|
<View className="stat-label">本月佣金</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 等级统计 */}
|
||||||
|
<View className="level-stats">
|
||||||
|
<View className="level-item">
|
||||||
|
<View className="level-info">
|
||||||
|
<View className="level-title">一级下线</View>
|
||||||
|
<View className="level-count">{stats.level1Members}人</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="level-item">
|
||||||
|
<View className="level-info">
|
||||||
|
<View className="level-title">二级下线</View>
|
||||||
|
<View className="level-count">{stats.level2Members}人</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 成员列表 */}
|
||||||
|
<View className="members-container">
|
||||||
|
<Tabs value={activeTab} onChange={(value) => setActiveTab(value)}>
|
||||||
|
<TabPane title="全部" />
|
||||||
|
<TabPane title="一级" />
|
||||||
|
<TabPane title="二级" />
|
||||||
|
<TabPane title="活跃" />
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
<View className="members-list">
|
||||||
|
{getFilteredMembers().length === 0 ? (
|
||||||
|
<View className="empty-container">
|
||||||
|
<Empty description="暂无团队成员" />
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
getFilteredMembers().map((member) => (
|
||||||
|
<View
|
||||||
|
key={member.id}
|
||||||
|
className="member-item"
|
||||||
|
onClick={() => handleMemberClick(member)}
|
||||||
|
>
|
||||||
|
<Avatar size="50" src={member.avatar} shape="round" />
|
||||||
|
<View className="member-info">
|
||||||
|
<View className="member-header">
|
||||||
|
<View className="member-name">{member.nickname}</View>
|
||||||
|
<View className={`member-level level-${member.level}`}>
|
||||||
|
{member.level}级
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="member-stats">
|
||||||
|
<View className="stat">订单:{member.orderCount}</View>
|
||||||
|
<View className="stat">佣金:¥{member.totalCommission.toFixed(2)}</View>
|
||||||
|
</View>
|
||||||
|
<View className="member-time">加入时间:{member.joinTime}</View>
|
||||||
|
</View>
|
||||||
|
<View className={`member-status ${member.status}`}>
|
||||||
|
{member.status === 'active' ? '活跃' : '不活跃'}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyTeam
|
||||||
75
src/dealer/withdraw/index.scss
Normal file
75
src/dealer/withdraw/index.scss
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
.withdraw-detail-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.records-list {
|
||||||
|
.record-item {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.amount {
|
||||||
|
font-size: 20px; // 对应 text-xl,重要金额
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-size: 14px; // 对应 text-sm
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-info {
|
||||||
|
.time {
|
||||||
|
font-size: 16px; // 对应 text-base
|
||||||
|
color: #6b7280;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remark {
|
||||||
|
font-size: 16px; // 对应 text-base
|
||||||
|
color: #ef4444;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding: 8px;
|
||||||
|
background-color: #fef2f2;
|
||||||
|
border-radius: 6px;
|
||||||
|
border-left: 3px solid #ef4444;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
146
src/dealer/withdraw/index.tsx
Normal file
146
src/dealer/withdraw/index.tsx
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { View } from '@tarojs/components'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import { Empty } from '@nutui/nutui-react-taro'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
interface WithdrawRecord {
|
||||||
|
id: string
|
||||||
|
amount: number
|
||||||
|
status: 'pending' | 'success' | 'failed'
|
||||||
|
statusText: string
|
||||||
|
createTime: string
|
||||||
|
completeTime?: string
|
||||||
|
remark?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function WithdrawDetail() {
|
||||||
|
const [records, setRecords] = useState<WithdrawRecord[]>([])
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Taro.setNavigationBarTitle({
|
||||||
|
title: '提现明细'
|
||||||
|
})
|
||||||
|
loadWithdrawRecords()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const loadWithdrawRecords = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true)
|
||||||
|
// 模拟数据,实际应该调用API
|
||||||
|
const mockData: WithdrawRecord[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
amount: 100.00,
|
||||||
|
status: 'success',
|
||||||
|
statusText: '提现成功',
|
||||||
|
createTime: '2024-01-15 14:30:00',
|
||||||
|
completeTime: '2024-01-15 16:45:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
amount: 50.00,
|
||||||
|
status: 'pending',
|
||||||
|
statusText: '处理中',
|
||||||
|
createTime: '2024-01-10 09:20:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
amount: 200.00,
|
||||||
|
status: 'failed',
|
||||||
|
statusText: '提现失败',
|
||||||
|
createTime: '2024-01-05 11:15:00',
|
||||||
|
remark: '银行卡信息有误'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setRecords(mockData)
|
||||||
|
setLoading(false)
|
||||||
|
}, 1000)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载提现记录失败:', error)
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusColor = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'success':
|
||||||
|
return '#10B981'
|
||||||
|
case 'pending':
|
||||||
|
return '#F59E0B'
|
||||||
|
case 'failed':
|
||||||
|
return '#EF4444'
|
||||||
|
default:
|
||||||
|
return '#6B7280'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRecordClick = (record: WithdrawRecord) => {
|
||||||
|
const content = `
|
||||||
|
提现金额:¥${record.amount.toFixed(2)}
|
||||||
|
申请时间:${record.createTime}
|
||||||
|
${record.completeTime ? `完成时间:${record.completeTime}` : ''}
|
||||||
|
${record.remark ? `备注:${record.remark}` : ''}
|
||||||
|
`.trim()
|
||||||
|
|
||||||
|
Taro.showModal({
|
||||||
|
title: '提现详情',
|
||||||
|
content,
|
||||||
|
showCancel: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<View className="withdraw-detail-page">
|
||||||
|
<View className="loading-container">
|
||||||
|
<View className="text-center text-gray-500">加载中...</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="withdraw-detail-page">
|
||||||
|
{records.length === 0 ? (
|
||||||
|
<View className="empty-container">
|
||||||
|
<Empty description="暂无提现记录" />
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<View className="records-list">
|
||||||
|
{records.map((record) => (
|
||||||
|
<View
|
||||||
|
key={record.id}
|
||||||
|
className="record-item"
|
||||||
|
onClick={() => handleRecordClick(record)}
|
||||||
|
>
|
||||||
|
<View className="record-header">
|
||||||
|
<View className="amount">¥{record.amount.toFixed(2)}</View>
|
||||||
|
<View
|
||||||
|
className="status"
|
||||||
|
style={{ color: getStatusColor(record.status) }}
|
||||||
|
>
|
||||||
|
{record.statusText}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="record-info">
|
||||||
|
<View className="time">申请时间:{record.createTime}</View>
|
||||||
|
{record.completeTime && (
|
||||||
|
<View className="time">完成时间:{record.completeTime}</View>
|
||||||
|
)}
|
||||||
|
{record.remark && (
|
||||||
|
<View className="remark">备注:{record.remark}</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WithdrawDetail
|
||||||
@@ -32,11 +32,9 @@ const UserCell = () => {
|
|||||||
backgroundImage: 'linear-gradient(to right bottom, #54a799, #177b73)',
|
backgroundImage: 'linear-gradient(to right bottom, #54a799, #177b73)',
|
||||||
}}
|
}}
|
||||||
title={
|
title={
|
||||||
<div style={{display: 'inline-flex', alignItems: 'center'}}>
|
<div style={{display: 'inline-flex', alignItems: 'center'}} onClick={() => navTo('/dealer/index', true)}>
|
||||||
<Reward className={'text-orange-100 '} size={16}/>
|
<Reward className={'text-orange-100 '} size={16}/>
|
||||||
<div>
|
|
||||||
<span style={{fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>开通会员</span>
|
<span style={{fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>开通会员</span>
|
||||||
</div>
|
|
||||||
<span className={'text-white opacity-80 pl-3'}>享优惠</span>
|
<span className={'text-white opacity-80 pl-3'}>享优惠</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user