Files
mini-programs/src/user_pages/withdrawal/index.tsx
2025-10-17 11:53:29 +08:00

395 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
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 React, { useState, useEffect, useRef } from "react";
import { View, Text, Input, Button, Image } from "@tarojs/components";
import Taro, { useDidShow } from "@tarojs/taro";
import httpService from "@/services/httpService";
import "./index.scss";
import { CommonPopup } from "@/components";
import { useKeyboardHeight } from "@/store/keyboardStore";
import img from "@/config/images";
interface WalletInfo {
balance: string;
frozen_balance?: string;
total_balance?: string;
total_income?: string;
total_withdraw?: string;
}
const Withdrawal: React.FC = () => {
// 获取当前页面的配置
const currentPage = Taro.getCurrentInstance();
const pageConfig = currentPage.page?.config;
const pageTitle = pageConfig?.navigationBarTitleText;
const inputRef = useRef(null);
// 使用全局键盘状态
const {
keyboardHeight,
isKeyboardVisible,
addListener,
initializeKeyboardListener,
} = useKeyboardHeight();
// 使用全局键盘状态监听
useEffect(() => {
// 初始化全局键盘监听器
initializeKeyboardListener();
// 添加本地监听器
const removeListener = addListener((height, visible) => {
console.log("AiImportPopup 收到键盘变化:", height, visible);
});
return () => {
removeListener();
};
}, [initializeKeyboardListener, addListener]);
const [showTips, setShowTips] = useState(false);
const [tipsText, setTipsText] = useState<string>("");
const [inputValue, setInputValue] = useState<string>("0.00");
const [walletInfo, setWalletInfo] = useState<WalletInfo>({
balance: "0.00",
});
const [isFocus, setIsFocus] = useState(false);
const [show_withdraw_popup, set_show_withdraw_popup] = useState(false);
const [password, setPassword] = useState<string[]>(new Array(6).fill(""));
const [mapErrorCodes, setMapErrorCodes] = useState({});
useDidShow(() => {
load_wallet_data();
getWithdrawErrorCodes();
});
useEffect(() => {
if (show_withdraw_popup && inputRef.current) {
inputRef.current.focus();
}
}, [show_withdraw_popup]);
useEffect(() => {
if (show_withdraw_popup) {
setIsFocus(true);
} else {
setPassword(new Array(6).fill(""));
setIsFocus(false);
}
}, [show_withdraw_popup]);
const validateWithdrawAmount = (amount: string) => {
if (Number(amount) > Number(walletInfo.balance)) {
setShowTips(true);
setTipsText("输入金额超过钱包余额");
} else if (Number(amount) > 200) {
setShowTips(true);
setTipsText("单笔提现金额不能超过 200元");
} else {
setShowTips(false);
}
};
const withdrawAll = () => {
setInputValue(walletInfo.balance);
validateWithdrawAmount(walletInfo.balance);
};
const handleInput = (e: any) => {
console.log(e);
const value = e.detail.value;
setInputValue(value);
validateWithdrawAmount(value);
};
// 加载钱包数据
const load_wallet_data = async () => {
try {
const response = await httpService.post("/wallet/balance");
const {
balance,
frozen_balance,
total_balance,
total_income,
total_withdraw,
} = response.data;
setWalletInfo({
balance,
frozen_balance,
total_balance,
total_income,
total_withdraw,
});
} catch (error: any) {
console.error("加载钱包数据失败:", error);
let errorMessage = "加载失败,请重试";
if (
error &&
error.response &&
error.response.data &&
error.response.data.message
) {
errorMessage = error.response.data.message;
} else if (error && error.data && error.data.message) {
errorMessage = error.data.message;
}
Taro.showToast({
title: errorMessage,
icon: "error",
duration: 2000,
});
}
};
const getWithdrawErrorCodes = async () => {
try {
const response = await httpService.post("/wallet/error_codes");
if (response.code === 0) {
const {
withdraw_errors: { WEIXIN_ERRORS },
} = response.data;
const mapErrorCodes = {};
for (const key in WEIXIN_ERRORS) {
const { code } = WEIXIN_ERRORS[key];
mapErrorCodes[code] = WEIXIN_ERRORS[key];
}
setMapErrorCodes(mapErrorCodes);
}
} catch (error: any) {
console.error("获取提现错误码失败:", error);
}
};
const handleWithdraw = async () => {
set_show_withdraw_popup(true);
};
const submit_withdraw = async (payment_password: string) => {
try {
// 先调用后端接口获取提现参数
const response = await httpService.post(
"/wallet/withdraw",
{
amount: parseFloat(inputValue),
transfer_remark: "用户申请提现",
payment_password,
},
{ showToast: false }
);
const { data } = response;
// 根据后端返回的数据结构解析参数
const { mch_id, app_id, package_info, open_id } = data;
console.log("/wallet/withdraw:", data);
set_show_withdraw_popup(false);
// 调用微信商户转账接口
(Taro as any).requestMerchantTransfer({
mchId: mch_id,
appId: app_id,
package: package_info,
openId: open_id,
success: (res) => {
console.log("微信转账成功:", res);
Taro.showToast({
title: "提现成功",
icon: "success",
duration: 2000,
});
// 关闭弹窗并重置状态
set_show_withdraw_popup(false);
setInputValue("0.00");
// 重新加载数据
load_wallet_data();
},
fail: (res) => {
console.log("微信转账失败:", res);
},
});
} catch (error: any) {
const { code, message } = error;
if (message === "支付密码错误") {
set_show_withdraw_popup(false);
Taro.showModal({
content: "支付密码错误,请重试",
cancelText: "忘记密码",
confirmText: "重试",
cancelColor: "#000",
confirmColor: "#fff",
}).then((res) => {
if (res.confirm) {
set_show_withdraw_popup(true);
} else if (res.cancel) {
Taro.navigateTo({
url: "/user_pages/validPhone/index",
});
}
});
return;
} else if (mapErrorCodes[code]) {
const { message, description } = mapErrorCodes[code];
Taro.showModal({
title: description,
content: message,
showCancel: false,
confirmText: "确认",
});
return;
} else {
Taro.showToast({
title: message,
icon: "none",
duration: 2000,
});
}
}
};
const handlePasswordInput = (e: any) => {
const value = e.detail.value;
const [one = "", two = "", three = "", four = "", five = "", six = ""] =
value.split("");
setPassword([one, two, three, four, five, six]);
if (value.length === 6) {
const timer = setTimeout(() => {
submit_withdraw(value);
clearTimeout(timer);
}, 100);
}
};
const handlePopupClick = (e) => {
e.preventDefault();
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
inputRef.current!.focus();
};
return (
<View className="withdrawal-page">
{/* 导航栏 */}
<View className="custom-navbar">
<View className="detail-navigator">
<View
className="detail-navigator-back"
onClick={() => {
Taro.navigateBack();
}}
>
<Image
className="detail-navigator-back-icon"
src={img.ICON_NAVIGATOR_BACK}
/>
<Text>{pageTitle}</Text>
</View>
</View>
</View>
<View className="withdrawal-container">
<Text className="title-text"></Text>
<View className="input-container">
<Text className="symbol">¥</Text>
<Input
type="digit"
placeholder="0.00"
cursorColor="#000"
value={inputValue}
onInput={handleInput}
/>
{!showTips && Number(inputValue) !== 0 && (
<Button className="btn" onClick={handleWithdraw}>
</Button>
)}
</View>
<View className="btn-container">
<View>
<Text>{`我的余额:¥${walletInfo.balance}`}</Text>
{/* <Text>可提现余额¥5000.00</Text> */}
</View>
<Text className="btn" onClick={withdrawAll}>
</Text>
</View>
{showTips && <View className="tips-text">{tipsText}</View>}
</View>
<View className="tips-container">
<View className="title-text"></View>
<View className="tips-text">
<Text>
1.
</Text>
<Text>
2.
</Text>
<Text>
3.
</Text>
<Text>
4.
</Text>
<Text>
5.
</Text>
<Text>
6.
</Text>
<Text>7. </Text>
</View>
</View>
<View className="tips-container">
<View className="title-text"></View>
<View className="tips-text">
<Text>1. </Text>
<Text>2. </Text>
<Text>
3.
</Text>
<Text>4. 使</Text>
</View>
</View>
{/* 提现输入密码弹窗 */}
<CommonPopup
showHeader={true}
visible={show_withdraw_popup}
onClose={() => set_show_withdraw_popup(false)}
title="提现"
className="withdraw_popup"
hideFooter={true}
style={{
bottom: isKeyboardVisible ? `${keyboardHeight}px` : undefined,
}}
>
<View
className="popup_content"
onTouchForceChange={handlePopupClick}
onTouchStart={handlePopupClick}
onTouchMove={handlePopupClick}
onTouchEnd={handlePopupClick}
>
<View className="popup_text">{`¥${inputValue}`}</View>
<View className="password_container">
{password.map((item, index) => (
<View key={index} className="password_item">
<Text className="password_text">{item}</Text>
</View>
))}
</View>
<Input
holdKeyboard={true}
ref={inputRef}
focus={isFocus}
type="number"
adjustPosition={false}
style={{ width: "0", height: "0", opacity: "0" }}
value={password.filter((item) => item !== "").join("")}
maxlength={6}
onInput={handlePasswordInput}
onBlur={() => {
set_show_withdraw_popup(false);
}}
/>
</View>
</CommonPopup>
</View>
);
};
export default Withdrawal;