Files
xinlong-shop-taro/src_bak/pages/user/withdraw/index.tsx
赵忠林 1fa58040f3 feat(user): 新增收货地址管理及售后申请页面
- 新增地址类型定义,增强前端地址数据结构
- 新增地址编辑页面,支持地址智能识别和定位选点功能
- 地址编辑支持省市区选择及默认地址设置
- 新增地址列表页面,支持地址展示、删除、编辑和选择功能
- 实现售后申请页面,支持选择售后类型和退款原因
- 售后申请支持商品选择、退款金额计算和凭证上传
- 新增售后详情页面,支持售后状态展示及申请取消
- 优化页面加载和用户交互体验,增加错误提示和权限处理
2026-07-01 12:11:56 +08:00

262 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { View, Text, Input } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { useState, useEffect } from 'react';
import { getWithdrawConfig, getWithdrawStats, applyWithdraw } from '@/api/shop/shopWithdrawRecord';
import PayModal from '@/components/business/PayModal';
import './index.scss';
definePageConfig({
navigationBarTitleText: '提现',
});
export default function WithdrawPage() {
const [config, setConfig] = useState<any>({});
const [stats, setStats] = useState<any>({});
const [amount, setAmount] = useState('');
const [showPayModal, setShowPayModal] = useState(false);
const [submitting, setSubmitting] = useState(false);
useEffect(() => {
loadConfig();
loadStats();
}, []);
const loadConfig = async () => {
try {
const res: any = await getWithdrawConfig();
if (res.code === 200 || res.code === 0) {
setConfig(res.data || {});
}
} catch (error) {
console.error('加载配置失败', error);
}
};
const loadStats = async () => {
try {
const res: any = await getWithdrawStats();
if (res.code === 200 || res.code === 0) {
setStats(res.data || {});
}
} catch (error) {
console.error('加载统计失败', error);
}
};
// 从配置或默认值读取
const minAmount = config.withdraw_min_amount || '10';
const maxAmount = config.withdraw_max_amount || '200';
const dailyLimit = config.withdraw_daily_limit || '2000';
const dailyCount = config.withdraw_daily_count || 3;
const timeStart = config.withdraw_time_start || '';
const timeEnd = config.withdraw_time_end || '';
const feeRate = config.withdraw_fee_rate || '0';
const arrivalTime = config.arrival_time || '';
const handleWithdraw = () => {
if (!amount || parseFloat(amount) <= 0) {
Taro.showToast({ title: '请输入提现金额', icon: 'none' });
return;
}
const amountNum = parseFloat(amount);
if (amountNum < parseFloat(minAmount)) {
Taro.showToast({ title: `最低提现金额为${minAmount}`, icon: 'none' });
return;
}
if (amountNum > parseFloat(maxAmount)) {
Taro.showToast({ title: `单次最大提现金额为${maxAmount}`, icon: 'none' });
return;
}
setShowPayModal(true);
};
const handlePayConfirm = async (payType: number) => {
setShowPayModal(false);
if (payType !== 0) return;
setSubmitting(true);
try {
const res: any = await applyWithdraw({ amount: parseFloat(amount) });
if (res.code === 200 || res.code === 0) {
Taro.showToast({ title: '提现申请已提交', icon: 'success' });
setTimeout(() => {
Taro.navigateBack();
}, 1500);
} else {
Taro.showToast({ title: res.message || '提交失败', icon: 'none' });
}
} catch (error) {
console.error('提现申请失败', error);
Taro.showToast({ title: '提交失败', icon: 'none' });
} finally {
setSubmitting(false);
}
};
const handleAllWithdraw = () => {
const available = stats.withdrawingTotal || stats.balance || '0.00';
setAmount(available);
};
const availableBalance = stats.withdrawingTotal || stats.balance || '0.00';
return (
<View className="withdraw-page">
{/* 余额卡片 */}
<View className="balance-card">
<View className="balance-header">
<Text className="balance-label"></Text>
</View>
<View className="balance-amount-row">
<Text className="currency-symbol-lg">¥</Text>
<Text className="balance-number">{availableBalance}</Text>
</View>
<View className="balance-tips">
<Text className="balance-tip-text"></Text>
</View>
</View>
{/* 提现输入 */}
<View className="withdraw-card">
<View className="card-title"></View>
<View className="amount-input-wrap">
<Text className="currency-symbol">¥</Text>
<Input
className="amount-input"
type="digit"
placeholder={`最低 ${minAmount}`}
value={amount}
onInput={(e) => setAmount(e.detail.value)}
/>
<Text
className="all-withdraw"
onClick={handleAllWithdraw}
>
</Text>
</View>
{feeRate && parseFloat(feeRate) > 0 ? (
<View className="fee-tips">
<Text className="fee-icon"></Text>
<Text> {feeRate}% </Text>
</View>
) : (
<View className="fee-tips fee-free">
<Text className="fee-icon"></Text>
<Text></Text>
</View>
)}
</View>
{/* ====== 提现规则展示(核心审核项)====== */}
<View className="rules-section">
{/* 标题栏 */}
<View className="rules-header">
<Text className="rules-header-icon">📋</Text>
<Text className="rules-header-title"></Text>
</View>
{/* 规则网格 - 两列布局更醒目 */}
<View className="rules-grid">
<View className="rule-cell">
<Text className="rule-cell-label"></Text>
<Text className="rule-cell-value">{minAmount} </Text>
</View>
<View className="rule-cell">
<Text className="rule-cell-label"></Text>
<Text className="rule-cell-value">{maxAmount} /</Text>
</View>
<View className="rule-cell">
<Text className="rule-cell-label"></Text>
<Text className="rule-cell-value">{dailyLimit} /</Text>
</View>
<View className="rule-cell">
<Text className="rule-cell-label"></Text>
<Text className="rule-cell-value">{dailyCount} /</Text>
</View>
<View className="rule-cell">
<Text className="rule-cell-label"></Text>
<Text className="rule-cell-value">{timeStart && timeEnd ? `${timeStart}-${timeEnd}` : '全天可提'}</Text>
</View>
<View className="rule-cell">
<Text className="rule-cell-label"></Text>
<Text className="rule-cell-value">{arrivalTime || '1-3个工作日'}</Text>
</View>
<View className="rule-cell rule-cell-wide">
<Text className="rule-cell-label"></Text>
<Text className="rule-cell-value">
{feeRate && parseFloat(feeRate) > 0 ? `${feeRate}%` : '免手续费'}
</Text>
</View>
</View>
{/* 详细规则文字 */}
<View className="rules-detail">
<View className="detail-item">
<Text className="detail-bullet"></Text>
<Text className="detail-text"> ¥{minAmount} ~ ¥{maxAmount}</Text>
</View>
<View className="detail-item">
<Text className="detail-bullet"></Text>
<Text className="detail-text"> {dailyCount} ¥{dailyLimit}</Text>
</View>
{timeStart && timeEnd ? (
<View className="detail-item">
<Text className="detail-bullet"></Text>
<Text className="detail-text"> {timeStart} {timeEnd} </Text>
</View>
) : (
<View className="detail-item">
<Text className="detail-bullet"></Text>
<Text className="detail-text"> 24 </Text>
</View>
)}
<View className="detail-item">
<Text className="detail-bullet"></Text>
<Text className="detail-text"> {arrivalTime || '1-3个工作日'} </Text>
</View>
{feeRate && parseFloat(feeRate) > 0 ? (
<View className="detail-item">
<Text className="detail-bullet"></Text>
<Text className="detail-text"> {feeRate}% </Text>
</View>
) : (
<View className="detail-item">
<Text className="detail-bullet"></Text>
<Text className="detail-text"></Text>
</View>
)}
<View className="detail-item">
<Text className="detail-bullet"></Text>
<Text className="detail-text">退</Text>
</View>
</View>
</View>
{/* 底部按钮 */}
<View className="bottom-area">
<View
className={`submit-btn ${amount && !submitting ? '' : 'disabled'}`}
onClick={handleWithdraw}
>
{submitting ? '提交中...' : '确认提现'}
</View>
<View className="submit-disclaimer">
<Text className="disclaimer-text"></Text>
</View>
</View>
<PayModal
visible={showPayModal}
amount={parseFloat(amount) || 0}
onConfirm={handlePayConfirm}
onClose={() => setShowPayModal(false)}
/>
</View>
);
}