mi
This commit is contained in:
3
miniprogram/src/subpackages/distributor/index.config.ts
Normal file
3
miniprogram/src/subpackages/distributor/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '分销中心'
|
||||
})
|
||||
68
miniprogram/src/subpackages/distributor/index.scss
Normal file
68
miniprogram/src/subpackages/distributor/index.scss
Normal 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; }
|
||||
}
|
||||
}
|
||||
76
miniprogram/src/subpackages/distributor/index.tsx
Normal file
76
miniprogram/src/subpackages/distributor/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
3
miniprogram/src/subpackages/distributor/invite.config.ts
Normal file
3
miniprogram/src/subpackages/distributor/invite.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '推广邀请'
|
||||
})
|
||||
43
miniprogram/src/subpackages/distributor/invite.scss
Normal file
43
miniprogram/src/subpackages/distributor/invite.scss
Normal 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;
|
||||
}
|
||||
57
miniprogram/src/subpackages/distributor/invite.tsx
Normal file
57
miniprogram/src/subpackages/distributor/invite.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '申请分销员'
|
||||
})
|
||||
42
miniprogram/src/subpackages/distributor/register.scss
Normal file
42
miniprogram/src/subpackages/distributor/register.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
32
miniprogram/src/subpackages/distributor/register.tsx
Normal file
32
miniprogram/src/subpackages/distributor/register.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '申请提现'
|
||||
})
|
||||
57
miniprogram/src/subpackages/distributor/withdraw.scss
Normal file
57
miniprogram/src/subpackages/distributor/withdraw.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
73
miniprogram/src/subpackages/distributor/withdraw.tsx
Normal file
73
miniprogram/src/subpackages/distributor/withdraw.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user