Files
YGChatCS/src/pages-aigc/recharge/recharge.vue
duanshuwen 29bf6afe55 feat(recharge): add recharge package selection flow
- add new getRechargeOptions API endpoint in AigcApi
- replace CustomAmountCard with RechargePackageList component
- implement fetching and selection logic for recharge packages
- update UI to use correct recharge data fields
- add validation for selected recharge option
2026-07-13 10:27:03 +08:00

183 lines
4.3 KiB
Vue

<template>
<view class="aigc-recharge-page">
<TopNavBar
title="积分充值"
title-align="left"
background="transparent"
title-color="#172033"
back-icon-color="#172033"
:align-with-menu-button="true"
:z-index="3"
@back="handleBack"
/>
<view class="aigc-recharge-content">
<BalanceCard
:points="balancePoints"
@ledger="handleOpenLedger"
/>
<RechargePackageList
:packages="rechargeOptions"
:selected-key="selectedRechargeKey"
@select="handleSelectRechargeOption"
/>
<RechargeFooter
:price="payPrice"
:loading="paying"
@pay="handlePay"
/>
</view>
</view>
</template>
<script setup>
import { computed, ref } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import TopNavBar from "@/components/TopNavBar/index.vue";
import { useCurrentCredit } from "@/pages-aigc/hooks/useCurrentCredit.js";
import { getRechargeOptions, rechargeCredit } from "@/request/api/AigcApi.js";
import BalanceCard from "./components/BalanceCard/index.vue";
import RechargeFooter from "./components/RechargeFooter/index.vue";
import RechargePackageList from "./components/RechargePackageList/index.vue";
const { pointBalance: balancePoints, fetchCurrentCredit } = useCurrentCredit();
const rechargeOptions = ref([]);
const selectedRechargeOption = ref(null);
const paying = ref(false);
const getRechargeOptionKey = (option) => {
if (!option) return "";
return `${option.rechargeAmount ?? ""}-${option.rechargePoints ?? ""}`;
};
const selectedRechargeKey = computed(() => getRechargeOptionKey(selectedRechargeOption.value));
const payPrice = computed(() => {
const amount = Number(selectedRechargeOption.value?.rechargeAmount);
return Number.isFinite(amount) ? amount : 0;
});
const showToast = (title) => {
uni.showToast({
title,
icon: "none",
});
};
const fetchRechargeOptions = async () => {
try {
const res = await getRechargeOptions();
if (res?.code === 0 && Array.isArray(res.data)) {
rechargeOptions.value = res.data;
selectedRechargeOption.value = res.data[0] || null;
}
} catch (error) {
console.error("获取积分充值档位失败", error);
}
};
onLoad(() => {
fetchRechargeOptions();
});
onShow(() => {
fetchCurrentCredit();
});
const handleBack = () => {
const pages = getCurrentPages();
if (pages.length > 1) {
uni.navigateBack();
}
};
const handleOpenLedger = () => {
uni.navigateTo({
url: "/pages-aigc/pointsDetails/pointsDetails",
fail: () => showToast("积分明细"),
});
};
const handleSelectRechargeOption = (option) => {
selectedRechargeOption.value = option;
};
const requestWechatPay = (payData) => {
const { nonceStr, packageVal, paySign, signType, timeStamp } = payData || {};
if (!nonceStr || !packageVal || !paySign || !signType || !timeStamp) {
showToast("支付参数错误,请重试");
return;
}
// #ifdef MP-WEIXIN
uni.requestPayment({
provider: "wxpay",
timeStamp: String(timeStamp),
nonceStr: String(nonceStr),
package: String(packageVal),
signType: String(signType),
paySign: String(paySign),
success: () => {
uni.showToast({
title: "支付成功",
icon: "success",
});
fetchCurrentCredit();
},
fail: () => {
showToast("支付失败,请重试");
},
});
// #endif
};
const handlePay = async () => {
if (paying.value) return;
if (!selectedRechargeOption.value) {
showToast("请选择充值档位");
return;
}
const rechargeAmount = payPrice.value;
if (!Number.isInteger(rechargeAmount) || rechargeAmount <= 0) {
showToast("充值档位金额错误,请重试");
return;
}
paying.value = true;
uni.showLoading({ title: "正在发起支付..." });
try {
const res = await rechargeCredit({
rechargeAmount,
payWay: "0",
paySource: "1",
});
uni.hideLoading();
if (res?.code === 0 && res.data) {
requestWechatPay(res.data);
return;
}
showToast(res?.msg || "充值下单失败,请重试");
} catch (error) {
uni.hideLoading();
console.error("充值下单失败", error);
showToast("充值下单失败,请重试");
} finally {
paying.value = false;
}
};
</script>
<style scoped lang="scss">
@import "./styles/index.scss";
</style>