- add new API request for fetching points top up agreement content - update recharge footer with agreement checkbox and protocol link - add agreement popup modal to display full agreement text - add payment pre-validation to enforce agreement acceptance before payment - handle loading and error states for agreement retrieval - adjust recharge footer layout to accommodate new UI elements
295 lines
7.3 KiB
Vue
295 lines
7.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"
|
|
/>
|
|
|
|
<CustomAmountCard
|
|
v-model="customAmount"
|
|
:selected="rechargeMode === 'custom'"
|
|
@select="handleSelectCustomAmount"
|
|
/>
|
|
|
|
<RechargeFooter
|
|
:price="payPrice"
|
|
:loading="paying"
|
|
:agreed="agreementAccepted"
|
|
:agreement-ready="agreementReady"
|
|
@update:agreed="agreementAccepted = $event"
|
|
@view-agreement="handleViewAgreement"
|
|
@pay="handlePay"
|
|
/>
|
|
</view>
|
|
|
|
<AgreePopup
|
|
:visible="agreementVisible"
|
|
title="积分充值协议"
|
|
:agreement="agreementContent"
|
|
@close="agreementVisible = false"
|
|
/>
|
|
</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 {
|
|
getPointsTopUpAgreement,
|
|
getRechargeOptions,
|
|
rechargeCredit,
|
|
} from "@/request/api/AigcApi.js";
|
|
import AgreePopup from "@/pages/login/components/AgreePopup/index.vue";
|
|
import BalanceCard from "./components/BalanceCard/index.vue";
|
|
import CustomAmountCard from "./components/CustomAmountCard/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 customAmount = ref("");
|
|
const rechargeMode = ref("package");
|
|
const paying = ref(false);
|
|
const agreementAccepted = ref(false);
|
|
const agreementContent = ref("");
|
|
const agreementLoading = ref(false);
|
|
const agreementVisible = ref(false);
|
|
|
|
const agreementReady = computed(() => Boolean(agreementContent.value.trim()));
|
|
|
|
const getRechargeOptionKey = (option) => {
|
|
if (!option) return "";
|
|
|
|
return `${option.rechargeAmount ?? ""}-${option.rechargePoints ?? ""}`;
|
|
};
|
|
|
|
const selectedRechargeKey = computed(() => {
|
|
if (rechargeMode.value !== "package") return "";
|
|
|
|
return getRechargeOptionKey(selectedRechargeOption.value);
|
|
});
|
|
|
|
const payPrice = computed(() => {
|
|
const amount =
|
|
rechargeMode.value === "custom"
|
|
? Number.parseInt(customAmount.value || "0", 10)
|
|
: 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;
|
|
if (rechargeMode.value === "package") {
|
|
selectedRechargeOption.value = res.data[0] || null;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("获取积分充值档位失败", error);
|
|
}
|
|
};
|
|
|
|
const fetchPointsTopUpAgreement = async (showError = false) => {
|
|
if (agreementLoading.value) return false;
|
|
|
|
agreementLoading.value = true;
|
|
try {
|
|
const res = await getPointsTopUpAgreement();
|
|
const content = typeof res?.data === "string" ? res.data.trim() : "";
|
|
if (res?.code === 0 && content) {
|
|
agreementContent.value = content;
|
|
return true;
|
|
}
|
|
|
|
agreementContent.value = "";
|
|
agreementAccepted.value = false;
|
|
if (showError) {
|
|
showToast(res?.msg || "协议加载失败,请重试");
|
|
}
|
|
} catch (error) {
|
|
console.error("获取积分充值协议失败", error);
|
|
agreementContent.value = "";
|
|
agreementAccepted.value = false;
|
|
if (showError) {
|
|
showToast("协议加载失败,请重试");
|
|
}
|
|
} finally {
|
|
agreementLoading.value = false;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
onLoad(() => {
|
|
agreementAccepted.value = false;
|
|
fetchRechargeOptions();
|
|
fetchPointsTopUpAgreement();
|
|
});
|
|
|
|
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) => {
|
|
rechargeMode.value = "package";
|
|
selectedRechargeOption.value = option;
|
|
};
|
|
|
|
const handleSelectCustomAmount = () => {
|
|
rechargeMode.value = "custom";
|
|
selectedRechargeOption.value = null;
|
|
};
|
|
|
|
const handleViewAgreement = async () => {
|
|
if (agreementReady.value) {
|
|
agreementVisible.value = true;
|
|
return;
|
|
}
|
|
|
|
if (agreementLoading.value) {
|
|
showToast("协议加载中,请稍候");
|
|
return;
|
|
}
|
|
|
|
const loaded = await fetchPointsTopUpAgreement(true);
|
|
if (loaded) {
|
|
agreementVisible.value = true;
|
|
}
|
|
};
|
|
|
|
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 (rechargeMode.value === "package" && !selectedRechargeOption.value) {
|
|
showToast("请选择充值档位");
|
|
return;
|
|
}
|
|
|
|
const rechargeAmount = payPrice.value;
|
|
if (!Number.isInteger(rechargeAmount) || rechargeAmount <= 0) {
|
|
const message = rechargeMode.value === "custom" ? "请输入正确的充值金额" : "充值档位金额错误,请重试";
|
|
showToast(message);
|
|
return;
|
|
}
|
|
|
|
if (agreementLoading.value) {
|
|
showToast("协议加载中,请稍候");
|
|
return;
|
|
}
|
|
|
|
if (!agreementReady.value) {
|
|
showToast("协议加载失败,请重试");
|
|
return;
|
|
}
|
|
|
|
if (!agreementAccepted.value) {
|
|
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>
|