This commit is contained in:
jeremygan2021
2026-02-11 01:31:21 +08:00
parent 61afc52ac2
commit 2d090cd0f4
97 changed files with 3661 additions and 4 deletions

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '分销中心'
})

View File

@@ -0,0 +1,68 @@
.page-container {
min-height: 100vh;
background-color: #f7f8fa;
padding: 20px;
}
.header-card {
background: linear-gradient(135deg, #00b96b, #009456);
border-radius: 12px;
padding: 30px 20px;
color: #fff;
text-align: center;
box-shadow: 0 4px 12px rgba(0, 185, 107, 0.3);
margin-bottom: 20px;
.label { font-size: 14px; opacity: 0.8; display: block; margin-bottom: 10px; }
.amount { font-size: 40px; font-weight: bold; display: block; margin-bottom: 20px; }
.btn-withdraw {
background: #fff;
color: #00b96b;
border-radius: 20px;
font-size: 14px;
padding: 0 30px;
height: 40px;
line-height: 40px;
display: inline-block;
}
}
.stats-grid {
display: flex;
background: #fff;
border-radius: 12px;
padding: 20px 0;
margin-bottom: 20px;
.item {
flex: 1;
text-align: center;
border-right: 1px solid #eee;
&:last-child { border-right: none; }
.val { font-size: 18px; font-weight: bold; color: #333; display: block; margin-bottom: 5px; }
.lbl { font-size: 12px; color: #999; }
}
}
.menu-list {
background: #fff;
border-radius: 12px;
padding: 0 20px;
.menu-item {
display: flex;
justify-content: space-between;
align-items: center;
height: 60px;
border-bottom: 1px solid #f5f5f5;
font-size: 16px;
color: #333;
&:last-child { border-bottom: none; }
.arrow { color: #ccc; }
}
}

View File

@@ -0,0 +1,76 @@
import { View, Text, Button } from '@tarojs/components'
import Taro, { useDidShow } from '@tarojs/taro'
import { useState } from 'react'
import { distributorInfo } from '../../api'
import './index.scss'
export default function DistributorIndex() {
const [info, setInfo] = useState<any>(null)
const [loading, setLoading] = useState(true)
useDidShow(() => {
fetchInfo()
})
const fetchInfo = async () => {
try {
const res = await distributorInfo()
setInfo(res)
} catch (err: any) {
if (err.statusCode === 404) {
// Not registered
Taro.redirectTo({ url: '/subpackages/distributor/register' })
} else {
Taro.showToast({ title: '加载失败', icon: 'none' })
}
} finally {
setLoading(false)
}
}
const goInvite = () => Taro.navigateTo({ url: '/subpackages/distributor/invite' })
const goWithdraw = () => Taro.navigateTo({ url: '/subpackages/distributor/withdraw' })
if (loading) return <View>Loading...</View>
if (!info) return <View>Error</View>
return (
<View className='page-container'>
<View className='header-card'>
<Text className='label'></Text>
<Text className='amount'>¥{info.withdrawable_balance}</Text>
<Button className='btn-withdraw' onClick={goWithdraw}></Button>
</View>
<View className='stats-grid'>
<View className='item'>
<Text className='val'>¥{info.total_earnings}</Text>
<Text className='lbl'></Text>
</View>
<View className='item'>
<Text className='val'>Lv.{info.level}</Text>
<Text className='lbl'></Text>
</View>
<View className='item'>
<Text className='val'>{(Number(info.commission_rate) * 100).toFixed(1)}%</Text>
<Text className='lbl'></Text>
</View>
</View>
<View className='menu-list'>
<View className='menu-item' onClick={goInvite}>
<Text>广</Text>
<Text className='arrow'>></Text>
</View>
<View className='menu-item'>
<Text></Text>
<Text className='arrow'>></Text>
</View>
<View className='menu-item'>
<Text></Text>
<Text className='arrow'>></Text>
</View>
</View>
</View>
)
}

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '推广邀请'
})

View File

@@ -0,0 +1,43 @@
.page-container {
padding: 30px;
background-color: #f8f8f8;
min-height: 100vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
}
.qr-card {
background: #fff;
border-radius: 16px;
padding: 40px;
width: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
margin-top: 40px;
.qr-img {
width: 400px;
height: 400px;
background: #eee;
margin-bottom: 30px;
}
.tip {
color: #666;
font-size: 28px;
text-align: center;
line-height: 1.5;
}
}
.btn-save {
margin-top: 60px;
width: 100%;
background: #07c160;
color: #fff;
}

View File

@@ -0,0 +1,57 @@
import { View, Text, Image, Button } from '@tarojs/components'
import Taro, { useLoad } from '@tarojs/taro'
import { useState } from 'react'
import { distributorInvite } from '../../api'
import './invite.scss'
export default function Invite() {
const [qrCode, setQrCode] = useState('')
const [loading, setLoading] = useState(true)
useLoad(() => {
fetchQr()
})
const fetchQr = async () => {
try {
const res: any = await distributorInvite()
setQrCode(res.qr_code_url)
} catch (err) {
console.error(err)
Taro.showToast({ title: '获取二维码失败', icon: 'none' })
} finally {
setLoading(false)
}
}
const saveImage = () => {
if (!qrCode) return
Taro.downloadFile({
url: qrCode,
success: (res) => {
Taro.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => Taro.showToast({ title: '已保存', icon: 'success' }),
fail: () => Taro.showToast({ title: '保存失败', icon: 'none' })
})
}
})
}
return (
<View className='page-container'>
<View className='qr-card'>
{loading ? (
<View className='qr-img' style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Text>Loading...</Text>
</View>
) : (
<Image src={qrCode} className='qr-img' mode='aspectFit' />
)}
<Text className='tip'>{'\n'}广</Text>
</View>
<Button className='btn-save' onClick={saveImage} disabled={!qrCode}></Button>
</View>
)
}

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '申请分销员'
})

View File

@@ -0,0 +1,42 @@
.page-container {
min-height: 100vh;
background-color: #f7f8fa;
padding: 40px 20px;
display: flex;
justify-content: center;
align-items: center;
}
.card {
background: #fff;
border-radius: 12px;
padding: 40px 30px;
width: 100%;
text-align: center;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
.title {
font-size: 24px;
font-weight: bold;
color: #333;
display: block;
margin-bottom: 10px;
}
.desc {
font-size: 14px;
color: #999;
display: block;
margin-bottom: 40px;
}
.btn-register {
background: #00b96b;
color: #fff;
border-radius: 22px;
height: 44px;
line-height: 44px;
font-size: 16px;
border: none;
}
}

View File

@@ -0,0 +1,32 @@
import { View, Button, Text } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { distributorRegister } from '../../api'
import './register.scss'
export default function Register() {
const handleRegister = async () => {
try {
await distributorRegister({})
Taro.showToast({ title: '申请已提交', icon: 'success' })
setTimeout(() => {
Taro.redirectTo({ url: '/subpackages/distributor/index' })
}, 1500)
} catch (err: any) {
if (err.data?.error === 'Already registered') {
Taro.redirectTo({ url: '/subpackages/distributor/index' })
} else {
Taro.showToast({ title: '申请失败', icon: 'none' })
}
}
}
return (
<View className='page-container'>
<View className='card'>
<Text className='title'></Text>
<Text className='desc'></Text>
<Button className='btn-register' onClick={handleRegister}></Button>
</View>
</View>
)
}

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '申请提现'
})

View File

@@ -0,0 +1,57 @@
.page-container {
padding: 30px;
background-color: #f8f8f8;
min-height: 100vh;
box-sizing: border-box;
}
.card {
background: #fff;
border-radius: 16px;
padding: 40px;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
.label {
font-size: 28px;
color: #333;
margin-bottom: 20px;
display: block;
}
.input-box {
display: flex;
align-items: center;
border-bottom: 1px solid #eee;
padding-bottom: 20px;
margin-bottom: 20px;
.symbol {
font-size: 48px;
font-weight: bold;
margin-right: 20px;
}
.input {
flex: 1;
height: 60px;
font-size: 48px;
font-weight: bold;
}
}
.balance-tip {
font-size: 24px;
color: #999;
.all {
color: #576b95;
margin-left: 10px;
}
}
.btn-submit {
margin-top: 60px;
background: #07c160;
color: #fff;
}
}

View File

@@ -0,0 +1,73 @@
import { View, Text, Button, Input } from '@tarojs/components'
import Taro, { useLoad } from '@tarojs/taro'
import { useState } from 'react'
import { distributorInfo, distributorWithdraw } from '../../api'
import './withdraw.scss'
export default function Withdraw() {
const [balance, setBalance] = useState(0)
const [amount, setAmount] = useState('')
const [loading, setLoading] = useState(false)
useLoad(() => {
fetchInfo()
})
const fetchInfo = async () => {
try {
const res: any = await distributorInfo()
setBalance(Number(res.withdrawable_balance))
} catch (err) {
console.error(err)
}
}
const handleWithdraw = async () => {
const val = Number(amount)
if (!val || val <= 0) {
Taro.showToast({ title: '请输入有效金额', icon: 'none' })
return
}
if (val > balance) {
Taro.showToast({ title: '余额不足', icon: 'none' })
return
}
setLoading(true)
try {
await distributorWithdraw(val)
Taro.showToast({ title: '申请已提交', icon: 'success' })
setTimeout(() => {
Taro.navigateBack()
}, 1500)
} catch (err) {
Taro.showToast({ title: '提现失败', icon: 'none' })
} finally {
setLoading(false)
}
}
return (
<View className='page-container'>
<View className='card'>
<Text className='label'></Text>
<View className='input-box'>
<Text className='symbol'>¥</Text>
<Input
className='input'
type='digit'
value={amount}
onInput={(e) => setAmount(e.detail.value)}
placeholder='0.00'
/>
</View>
<View className='balance-tip'>
<Text> ¥{balance.toFixed(2)}</Text>
<Text className='all' onClick={() => setAmount(balance.toString())}></Text>
</View>
<Button className='btn-submit' onClick={handleWithdraw} loading={loading}></Button>
</View>
</View>
)
}