Merge branch 'main' of http://1.117.67.95:3000/team/ycc-proxy-webview
This commit is contained in:
6
.env
6
.env
@@ -7,6 +7,12 @@ VITE_INQUIRE_AES_KEY=ff83609b2b24fc73196aac3d3dfb874f
|
|||||||
|
|
||||||
VITE_WECHAT_APP_ID=wx442ee1ac1ee75917
|
VITE_WECHAT_APP_ID=wx442ee1ac1ee75917
|
||||||
|
|
||||||
|
# 阿里云滑块验证码配置
|
||||||
|
# 从阿里云验证码控制台获取 SceneId
|
||||||
|
VITE_ALIYUN_CAPTCHA_SCENE_ID=wynt39to
|
||||||
|
# 是否启用加密模式(true/false),需要在阿里云控制台开启加密模式
|
||||||
|
# 注意:根据代码逻辑,设置为 true 表示禁用加密,设置为 false 表示启用加密
|
||||||
|
VITE_ALIYUN_CAPTCHA_ENCRYPTED=true
|
||||||
|
|
||||||
VITE_CHAT_AES_KEY=qw5w6SFE2D1jmxyd
|
VITE_CHAT_AES_KEY=qw5w6SFE2D1jmxyd
|
||||||
VITE_CHAT_AES_IV=345GDFED433223DF
|
VITE_CHAT_AES_IV=345GDFED433223DF
|
||||||
|
|||||||
@@ -109,6 +109,12 @@
|
|||||||
delete window.wx;
|
delete window.wx;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- 阿里云滑块验证码 -->
|
||||||
|
<script>
|
||||||
|
window.AliyunCaptchaConfig = { region: "cn", prefix: "12zxnj" };
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js"></script>
|
||||||
|
|
||||||
<!-- 预加载关键资源 -->
|
<!-- 预加载关键资源 -->
|
||||||
<link rel="preconnect" href="https://www.zhinengcha.cn" />
|
<link rel="preconnect" href="https://www.zhinengcha.cn" />
|
||||||
<link rel="preconnect" href="https://res.wx.qq.com" />
|
<link rel="preconnect" href="https://res.wx.qq.com" />
|
||||||
@@ -207,6 +213,8 @@
|
|||||||
<div class="loading-text">加载中</div>
|
<div class="loading-text">加载中</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
<!-- 阿里云验证码容器 -->
|
||||||
|
<div id="captcha-element"></div>
|
||||||
|
|
||||||
<script type="module" src="/src/main.js"></script>
|
<script type="module" src="/src/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
1
lEbH141J7d.txt
Normal file
1
lEbH141J7d.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
f07fc7c629885231180fb79885dba876
|
||||||
1
src/auto-imports.d.ts
vendored
1
src/auto-imports.d.ts
vendored
@@ -119,6 +119,7 @@ declare global {
|
|||||||
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
|
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
|
||||||
const useAgent: typeof import('./composables/useAgent.js')['useAgent']
|
const useAgent: typeof import('./composables/useAgent.js')['useAgent']
|
||||||
const useAgentStore: typeof import('./stores/agentStore.js')['useAgentStore']
|
const useAgentStore: typeof import('./stores/agentStore.js')['useAgentStore']
|
||||||
|
const useAliyunCaptcha: typeof import('./composables/useAliyunCaptcha.js')['default']
|
||||||
const useAnimate: typeof import('@vueuse/core')['useAnimate']
|
const useAnimate: typeof import('@vueuse/core')['useAnimate']
|
||||||
const useApiFetch: typeof import('./composables/useApiFetch.js')['default']
|
const useApiFetch: typeof import('./composables/useApiFetch.js')['default']
|
||||||
const useAppStore: typeof import('./stores/appStore.js')['useAppStore']
|
const useAppStore: typeof import('./stores/appStore.js')['useAppStore']
|
||||||
|
|||||||
@@ -126,7 +126,9 @@
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const show = defineModel("show");
|
const show = defineModel("show");
|
||||||
import { useCascaderAreaData } from "@vant/area-data";
|
import { useCascaderAreaData } from "@vant/area-data";
|
||||||
import { showToast } from "vant"; // 引入 showToast 方法
|
import { showToast } from "vant";
|
||||||
|
import useApiFetch from "@/composables/useApiFetch";
|
||||||
|
import { useAliyunCaptcha } from "@/composables/useAliyunCaptcha";
|
||||||
const emit = defineEmits(); // 确保 emit 可以正确使用
|
const emit = defineEmits(); // 确保 emit 可以正确使用
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isSelf: {
|
isSelf: {
|
||||||
@@ -148,6 +150,7 @@ const form = ref({
|
|||||||
const showCascader = ref(false);
|
const showCascader = ref(false);
|
||||||
const cascaderValue = ref("");
|
const cascaderValue = ref("");
|
||||||
const options = useCascaderAreaData();
|
const options = useCascaderAreaData();
|
||||||
|
const { runWithCaptcha } = useAliyunCaptcha();
|
||||||
const loadingSms = ref(false); // 控制验证码按钮的loading状态
|
const loadingSms = ref(false); // 控制验证码按钮的loading状态
|
||||||
const isCountingDown = ref(false);
|
const isCountingDown = ref(false);
|
||||||
const isAgreed = ref(false);
|
const isAgreed = ref(false);
|
||||||
@@ -172,21 +175,20 @@ const getSmsCode = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadingSms.value = true;
|
loadingSms.value = true;
|
||||||
|
runWithCaptcha(
|
||||||
const { data, error } = await useApiFetch("auth/sendSms")
|
(captchaVerifyParam) => useApiFetch("auth/sendSms")
|
||||||
.post({ mobile: form.value.mobile, actionType: "agentApply" })
|
.post({ mobile: form.value.mobile, actionType: "agentApply", captchaVerifyParam })
|
||||||
.json();
|
.json(),
|
||||||
|
(result) => {
|
||||||
loadingSms.value = false;
|
loadingSms.value = false;
|
||||||
|
if (result && result.code === 200) {
|
||||||
if (data.value && !error.value) {
|
|
||||||
if (data.value.code === 200) {
|
|
||||||
showToast({ message: "获取成功" });
|
showToast({ message: "获取成功" });
|
||||||
startCountdown(); // 启动倒计时
|
startCountdown();
|
||||||
} else {
|
} else if (result) {
|
||||||
showToast(data.value.msg);
|
showToast(result.msg || "发送失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
let timer = null;
|
let timer = null;
|
||||||
|
|
||||||
|
|||||||
@@ -703,6 +703,7 @@ const featureMap = {
|
|||||||
name: "谛听多维报告",
|
name: "谛听多维报告",
|
||||||
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/index.vue")),
|
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/index.vue")),
|
||||||
},
|
},
|
||||||
|
|
||||||
// 谛听多维报告拆分模块
|
// 谛听多维报告拆分模块
|
||||||
DWBG8B4D_Overview: {
|
DWBG8B4D_Overview: {
|
||||||
name: "报告概览",
|
name: "报告概览",
|
||||||
@@ -724,10 +725,10 @@ const featureMap = {
|
|||||||
name: "逾期风险综述",
|
name: "逾期风险综述",
|
||||||
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/OverdueRiskSection.vue")),
|
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/OverdueRiskSection.vue")),
|
||||||
},
|
},
|
||||||
// DWBG8B4D_CourtInfo: {
|
DWBG8B4D_CourtInfo: {
|
||||||
// name: "法院曝光台信息",
|
name: "法院曝光台信息",
|
||||||
// component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/MultCourtInfoSection.vue")),
|
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/MultCourtInfoSection.vue")),
|
||||||
// },
|
},
|
||||||
DWBG8B4D_LoanEvaluation: {
|
DWBG8B4D_LoanEvaluation: {
|
||||||
name: "借贷评估",
|
name: "借贷评估",
|
||||||
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/LoanEvaluationSection.vue")),
|
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/LoanEvaluationSection.vue")),
|
||||||
@@ -813,10 +814,10 @@ const featureMap = {
|
|||||||
name: "关联风险监督",
|
name: "关联风险监督",
|
||||||
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/RiskSupervisionSection.vue")),
|
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/RiskSupervisionSection.vue")),
|
||||||
},
|
},
|
||||||
// DWBG6A2C_CourtRiskInfo: {
|
DWBG6A2C_CourtRiskInfo: {
|
||||||
// name: "法院风险信息",
|
name: "法院风险信息",
|
||||||
// component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/CourtRiskInfoSection.vue")),
|
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/CourtRiskInfoSection.vue")),
|
||||||
// },
|
},
|
||||||
// 贷款风险报告
|
// 贷款风险报告
|
||||||
JRZQ5E9F: {
|
JRZQ5E9F: {
|
||||||
name: "贷款风险评估",
|
name: "贷款风险评估",
|
||||||
@@ -1039,6 +1040,7 @@ const featureRiskLevels = {
|
|||||||
'DWBG8B4D_LeasingRisk': 18,
|
'DWBG8B4D_LeasingRisk': 18,
|
||||||
'DWBG8B4D_RiskSupervision': 25,
|
'DWBG8B4D_RiskSupervision': 25,
|
||||||
'DWBG8B4D_RiskWarningTab': 30,
|
'DWBG8B4D_RiskWarningTab': 30,
|
||||||
|
'DWBG8B4D_CourtInfo':33,
|
||||||
|
|
||||||
// 司南报告子模块
|
// 司南报告子模块
|
||||||
'DWBG6A2C_StandLiveInfo': 10,
|
'DWBG6A2C_StandLiveInfo': 10,
|
||||||
@@ -1053,6 +1055,7 @@ const featureRiskLevels = {
|
|||||||
'DWBG6A2C_CreditDetail': 15,
|
'DWBG6A2C_CreditDetail': 15,
|
||||||
'DWBG6A2C_RentalBehavior': 15,
|
'DWBG6A2C_RentalBehavior': 15,
|
||||||
'DWBG6A2C_RiskSupervision': 25,
|
'DWBG6A2C_RiskSupervision': 25,
|
||||||
|
'DWBG6A2C_CourtRiskInfo':39,
|
||||||
|
|
||||||
// 贷款风险评估子模块
|
// 贷款风险评估子模块
|
||||||
'CJRZQ5E9F_RiskOverview': 25,
|
'CJRZQ5E9F_RiskOverview': 25,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useAgentStore } from "@/stores/agentStore";
|
|||||||
import { useUserStore } from "@/stores/userStore";
|
import { useUserStore } from "@/stores/userStore";
|
||||||
import { showToast } from "vant";
|
import { showToast } from "vant";
|
||||||
import useApiFetch from "@/composables/useApiFetch";
|
import useApiFetch from "@/composables/useApiFetch";
|
||||||
|
import { useAliyunCaptcha } from "@/composables/useAliyunCaptcha";
|
||||||
import { registerByInviteCode } from "@/api/agent";
|
import { registerByInviteCode } from "@/api/agent";
|
||||||
|
|
||||||
const emit = defineEmits(['register-success'])
|
const emit = defineEmits(['register-success'])
|
||||||
@@ -14,6 +15,7 @@ const route = useRoute();
|
|||||||
const dialogStore = useDialogStore();
|
const dialogStore = useDialogStore();
|
||||||
const agentStore = useAgentStore();
|
const agentStore = useAgentStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const { runWithCaptcha } = useAliyunCaptcha();
|
||||||
const phoneNumber = ref("");
|
const phoneNumber = ref("");
|
||||||
const verificationCode = ref("");
|
const verificationCode = ref("");
|
||||||
const inviteCode = ref("");
|
const inviteCode = ref("");
|
||||||
@@ -80,25 +82,25 @@ async function sendVerificationCode() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const actionType = hasAccount.value ? "bindMobile" : "agentApply";
|
const actionType = hasAccount.value ? "bindMobile" : "agentApply";
|
||||||
const { data, error } = await useApiFetch("auth/sendSms")
|
runWithCaptcha(
|
||||||
.post({ mobile: phoneNumber.value, actionType })
|
(captchaVerifyParam) => useApiFetch("auth/sendSms")
|
||||||
.json();
|
.post({ mobile: phoneNumber.value, actionType, captchaVerifyParam })
|
||||||
|
.json(),
|
||||||
if (data.value && !error.value) {
|
(result) => {
|
||||||
if (data.value.code === 200) {
|
if (result && result.code === 200) {
|
||||||
showToast({ message: "获取成功" });
|
showToast({ message: "获取成功" });
|
||||||
startCountdown();
|
startCountdown();
|
||||||
// 聚焦到验证码输入框
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const verificationCodeInput = document.getElementById('registerVerificationCode');
|
const verificationCodeInput = document.getElementById('registerVerificationCode');
|
||||||
if (verificationCodeInput) {
|
if (verificationCodeInput) {
|
||||||
verificationCodeInput.focus();
|
verificationCodeInput.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else if (result) {
|
||||||
showToast(data.value.msg);
|
showToast(result.msg || "发送失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startCountdown() {
|
function startCountdown() {
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, nextTick } from "vue";
|
import { ref, computed, nextTick } from "vue";
|
||||||
import { useDialogStore } from "@/stores/dialogStore";
|
import { useDialogStore } from "@/stores/dialogStore";
|
||||||
|
import { showToast } from "vant";
|
||||||
|
import useApiFetch from "@/composables/useApiFetch";
|
||||||
|
import { useAliyunCaptcha } from "@/composables/useAliyunCaptcha";
|
||||||
|
|
||||||
const emit = defineEmits(['bind-success'])
|
const emit = defineEmits(['bind-success'])
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const dialogStore = useDialogStore();
|
const dialogStore = useDialogStore();
|
||||||
const agentStore = useAgentStore();
|
const agentStore = useAgentStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const { runWithCaptcha } = useAliyunCaptcha();
|
||||||
const phoneNumber = ref("");
|
const phoneNumber = ref("");
|
||||||
const verificationCode = ref("");
|
const verificationCode = ref("");
|
||||||
const isCountingDown = ref(false);
|
const isCountingDown = ref(false);
|
||||||
@@ -36,25 +40,25 @@ async function sendVerificationCode() {
|
|||||||
showToast({ message: "请输入有效的手机号" });
|
showToast({ message: "请输入有效的手机号" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { data, error } = await useApiFetch("auth/sendSms")
|
runWithCaptcha(
|
||||||
.post({ mobile: phoneNumber.value, actionType: "bindMobile" })
|
(captchaVerifyParam) => useApiFetch("auth/sendSms")
|
||||||
.json();
|
.post({ mobile: phoneNumber.value, actionType: "bindMobile", captchaVerifyParam })
|
||||||
|
.json(),
|
||||||
if (data.value && !error.value) {
|
(result) => {
|
||||||
if (data.value.code === 200) {
|
if (result && result.code === 200) {
|
||||||
showToast({ message: "获取成功" });
|
showToast({ message: "获取成功" });
|
||||||
startCountdown();
|
startCountdown();
|
||||||
// 聚焦到验证码输入框
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const verificationCodeInput = document.getElementById('bindPhoneVerificationCode');
|
const verificationCodeInput = document.getElementById('bindPhoneVerificationCode');
|
||||||
if (verificationCodeInput) {
|
if (verificationCodeInput) {
|
||||||
verificationCodeInput.focus();
|
verificationCodeInput.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else if (result) {
|
||||||
showToast(data.value.msg);
|
showToast(result.msg || "发送失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startCountdown() {
|
function startCountdown() {
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ import { useUserStore } from "@/stores/userStore";
|
|||||||
import { useDialogStore } from "@/stores/dialogStore";
|
import { useDialogStore } from "@/stores/dialogStore";
|
||||||
import { useEnv } from "@/composables/useEnv";
|
import { useEnv } from "@/composables/useEnv";
|
||||||
import { showConfirmDialog } from "vant";
|
import { showConfirmDialog } from "vant";
|
||||||
|
import { useAliyunCaptcha } from "@/composables/useAliyunCaptcha";
|
||||||
|
|
||||||
import Payment from "@/components/Payment.vue";
|
import Payment from "@/components/Payment.vue";
|
||||||
import BindPhoneOnlyDialog from "@/components/BindPhoneOnlyDialog.vue";
|
import BindPhoneOnlyDialog from "@/components/BindPhoneOnlyDialog.vue";
|
||||||
@@ -200,6 +201,7 @@ const dialogStore = useDialogStore();
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { isWeChat } = useEnv();
|
const { isWeChat } = useEnv();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
const { runWithCaptcha } = useAliyunCaptcha();
|
||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const showPayment = ref(false);
|
const showPayment = ref(false);
|
||||||
@@ -440,11 +442,14 @@ async function sendVerificationCode() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, error } = await useApiFetch("/auth/sendSms")
|
// 使用阿里云滑块验证码保护发送短信接口
|
||||||
.post({ mobile: formData.mobile, actionType: "query" })
|
runWithCaptcha(
|
||||||
.json();
|
(captchaVerifyParam) => useApiFetch("/auth/sendSms")
|
||||||
|
.post({ mobile: formData.mobile, actionType: "query", captchaVerifyParam })
|
||||||
if (!error.value && data.value.code === 200) {
|
.json(),
|
||||||
|
(result) => {
|
||||||
|
// result 已经是解包后的响应数据(data.value)
|
||||||
|
if (result && result.code === 200) {
|
||||||
showToast({ message: "验证码发送成功", type: "success" });
|
showToast({ message: "验证码发送成功", type: "success" });
|
||||||
startCountdown();
|
startCountdown();
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@@ -453,10 +458,12 @@ async function sendVerificationCode() {
|
|||||||
verificationCodeInput.focus();
|
verificationCodeInput.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else if (result) {
|
||||||
showToast({ message: "验证码发送失败,请重试" });
|
showToast({ message: result.msg || "验证码发送失败,请重试" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let timer = null;
|
let timer = null;
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,14 @@ import { useAgentStore } from "@/stores/agentStore";
|
|||||||
import { useUserStore } from "@/stores/userStore";
|
import { useUserStore } from "@/stores/userStore";
|
||||||
import { showToast } from "vant";
|
import { showToast } from "vant";
|
||||||
import { realNameAuth } from "@/api/agent";
|
import { realNameAuth } from "@/api/agent";
|
||||||
|
import useApiFetch from "@/composables/useApiFetch";
|
||||||
|
import { useAliyunCaptcha } from "@/composables/useAliyunCaptcha";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const dialogStore = useDialogStore();
|
const dialogStore = useDialogStore();
|
||||||
const agentStore = useAgentStore();
|
const agentStore = useAgentStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const { runWithCaptcha } = useAliyunCaptcha();
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const realName = ref("");
|
const realName = ref("");
|
||||||
const idCard = ref("");
|
const idCard = ref("");
|
||||||
@@ -52,25 +55,26 @@ const canSubmit = computed(() => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 发送验证码
|
// 发送验证码(使用阿里云滑块验证码防刷)
|
||||||
async function sendVerificationCode() {
|
async function sendVerificationCode() {
|
||||||
if (isCountingDown.value || !isPhoneNumberValid.value) return;
|
if (isCountingDown.value || !isPhoneNumberValid.value) return;
|
||||||
if (!isPhoneNumberValid.value) {
|
if (!isPhoneNumberValid.value) {
|
||||||
showToast({ message: "请输入有效的手机号" });
|
showToast({ message: "请输入有效的手机号" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { data, error } = await useApiFetch("auth/sendSms")
|
runWithCaptcha(
|
||||||
.post({ mobile: phoneNumber.value, actionType: "realName" })
|
(captchaVerifyParam) => useApiFetch("auth/sendSms")
|
||||||
.json();
|
.post({ mobile: phoneNumber.value, actionType: "realName", captchaVerifyParam })
|
||||||
|
.json(),
|
||||||
if (data.value && !error.value) {
|
(result) => {
|
||||||
if (data.value.code === 200) {
|
if (result && result.code === 200) {
|
||||||
showToast({ message: "获取成功" });
|
showToast({ message: "获取成功" });
|
||||||
startCountdown();
|
startCountdown();
|
||||||
} else {
|
} else if (result) {
|
||||||
showToast(data.value.msg);
|
showToast(result.msg || "发送失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startCountdown() {
|
function startCountdown() {
|
||||||
|
|||||||
172
src/composables/useAliyunCaptcha.js
Normal file
172
src/composables/useAliyunCaptcha.js
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
import { showToast, showLoadingToast, closeToast } from "vant";
|
||||||
|
import useApiFetch from "@/composables/useApiFetch";
|
||||||
|
|
||||||
|
// 阿里云验证码场景 ID
|
||||||
|
const ALIYUN_CAPTCHA_SCENE_ID = "wynt39to";
|
||||||
|
// 是否启用加密模式(通过环境变量控制,非加密模式时前端不调用后端获取 EncryptedSceneId)
|
||||||
|
const ENABLE_ENCRYPTED =
|
||||||
|
import.meta.env.VITE_ALIYUN_CAPTCHA_ENCRYPTED === "false";
|
||||||
|
|
||||||
|
let captchaInitialised = false;
|
||||||
|
/** 首次初始化后,SDK 会异步调用 getInstance,用此 Promise 在实例就绪后再 show */
|
||||||
|
let captchaReadyPromise = null;
|
||||||
|
let captchaReadyResolve = null;
|
||||||
|
|
||||||
|
async function ensureCaptchaInit() {
|
||||||
|
if (captchaInitialised || typeof window === "undefined") return;
|
||||||
|
if (typeof window.initAliyunCaptcha !== "function") return;
|
||||||
|
|
||||||
|
captchaInitialised = true;
|
||||||
|
window.captcha = null;
|
||||||
|
window.__lastBizResponse = null;
|
||||||
|
window.__onCaptchaBizSuccess = null;
|
||||||
|
captchaReadyPromise = new Promise((resolve) => {
|
||||||
|
captchaReadyResolve = resolve;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 非加密模式:仅传 SceneId,不调用后端接口
|
||||||
|
if (!ENABLE_ENCRYPTED) {
|
||||||
|
window.initAliyunCaptcha({
|
||||||
|
SceneId: ALIYUN_CAPTCHA_SCENE_ID,
|
||||||
|
mode: "popup",
|
||||||
|
element: "#captcha-element",
|
||||||
|
getInstance(instance) {
|
||||||
|
window.captcha = instance;
|
||||||
|
if (typeof captchaReadyResolve === "function") {
|
||||||
|
captchaReadyResolve();
|
||||||
|
captchaReadyResolve = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
captchaVerifyCallback(param) {
|
||||||
|
return typeof window.__captchaVerifyCallback === "function"
|
||||||
|
? window.__captchaVerifyCallback(param)
|
||||||
|
: Promise.resolve({
|
||||||
|
captchaResult: false,
|
||||||
|
bizResult: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onBizResultCallback(bizResult) {
|
||||||
|
if (typeof window.__onBizResultCallback === "function") {
|
||||||
|
window.__onBizResultCallback(bizResult);
|
||||||
|
}
|
||||||
|
window.__lastBizResponse = null;
|
||||||
|
window.__onCaptchaBizSuccess = null;
|
||||||
|
},
|
||||||
|
slideStyle: { width: 360, height: 40 },
|
||||||
|
language: "cn",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密模式:先从后端获取 EncryptedSceneId,再初始化
|
||||||
|
const { data, error } = await useApiFetch("/captcha/encryptedSceneId")
|
||||||
|
.post()
|
||||||
|
.json();
|
||||||
|
const resp = data?.value;
|
||||||
|
const encryptedSceneId = resp?.data?.encryptedSceneId;
|
||||||
|
if (error?.value || !encryptedSceneId) {
|
||||||
|
showToast({ message: "获取验证码参数失败,请稍后重试" });
|
||||||
|
captchaInitialised = false;
|
||||||
|
captchaReadyPromise = null;
|
||||||
|
captchaReadyResolve = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.initAliyunCaptcha({
|
||||||
|
SceneId: ALIYUN_CAPTCHA_SCENE_ID,
|
||||||
|
EncryptedSceneId: encryptedSceneId,
|
||||||
|
mode: "popup",
|
||||||
|
element: "#captcha-element",
|
||||||
|
getInstance(instance) {
|
||||||
|
window.captcha = instance;
|
||||||
|
if (typeof captchaReadyResolve === "function") {
|
||||||
|
captchaReadyResolve();
|
||||||
|
captchaReadyResolve = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
captchaVerifyCallback(param) {
|
||||||
|
return typeof window.__captchaVerifyCallback === "function"
|
||||||
|
? window.__captchaVerifyCallback(param)
|
||||||
|
: Promise.resolve({ captchaResult: false, bizResult: false });
|
||||||
|
},
|
||||||
|
onBizResultCallback(bizResult) {
|
||||||
|
if (typeof window.__onBizResultCallback === "function") {
|
||||||
|
window.__onBizResultCallback(bizResult);
|
||||||
|
}
|
||||||
|
window.__lastBizResponse = null;
|
||||||
|
window.__onCaptchaBizSuccess = null;
|
||||||
|
},
|
||||||
|
slideStyle: { width: 360, height: 40 },
|
||||||
|
language: "cn",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 阿里云滑块验证码通用封装。
|
||||||
|
* 依赖 index.html 中已加载的 AliyunCaptcha.js;初始化在首次调起时执行。
|
||||||
|
*
|
||||||
|
* @param { (captchaVerifyParam: string) => Promise<{ data: Ref, error: Ref }> } bizVerify - 业务请求函数,接收滑块参数,返回 useApiFetch 的 { data, error }
|
||||||
|
* @param { (res: any) => void } [onSuccess] - 业务成功回调(code===200 时调用,传入接口返回的 data.value)
|
||||||
|
*/
|
||||||
|
export function useAliyunCaptcha() {
|
||||||
|
/**
|
||||||
|
* 先弹出滑块,通过后执行 bizVerify(captchaVerifyParam),再根据结果调用 onSuccess。
|
||||||
|
*/
|
||||||
|
async function runWithCaptcha(bizVerify, onSuccess) {
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
showToast({ message: "验证码仅支持浏览器环境" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loading = showLoadingToast({
|
||||||
|
message: "安全验证加载中...",
|
||||||
|
forbidClick: true,
|
||||||
|
duration: 0,
|
||||||
|
loadingType: "spinner",
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
window.__captchaVerifyCallback = async (captchaVerifyParam) => {
|
||||||
|
window.__lastBizResponse = null;
|
||||||
|
const { data, error } = await bizVerify(captchaVerifyParam);
|
||||||
|
const result = data?.value ?? data;
|
||||||
|
if (error?.value || !result) {
|
||||||
|
return { captchaResult: false, bizResult: false };
|
||||||
|
}
|
||||||
|
window.__lastBizResponse = result;
|
||||||
|
const captchaOk = result.captchaVerifyResult !== false;
|
||||||
|
const bizOk = result.code === 200;
|
||||||
|
return { captchaResult: captchaOk, bizResult: bizOk };
|
||||||
|
};
|
||||||
|
|
||||||
|
window.__onBizResultCallback = (bizResult) => {
|
||||||
|
if (
|
||||||
|
bizResult === true &&
|
||||||
|
window.__lastBizResponse &&
|
||||||
|
typeof window.__onCaptchaBizSuccess === "function"
|
||||||
|
) {
|
||||||
|
window.__onCaptchaBizSuccess(window.__lastBizResponse);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await ensureCaptchaInit();
|
||||||
|
|
||||||
|
// 首次初始化时 SDK 会异步调用 getInstance,需等待实例就绪后再 show
|
||||||
|
if (captchaReadyPromise) {
|
||||||
|
await captchaReadyPromise;
|
||||||
|
captchaReadyPromise = null;
|
||||||
|
}
|
||||||
|
if (!window.captcha) {
|
||||||
|
showToast({ message: "验证码未加载,请刷新页面重试" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.__onCaptchaBizSuccess = onSuccess;
|
||||||
|
window.captcha.show();
|
||||||
|
} finally {
|
||||||
|
closeToast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { runWithCaptcha };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useAliyunCaptcha;
|
||||||
@@ -18,6 +18,10 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 税后收入 -->
|
||||||
|
<div v-if="item.status !== 3" class="text-xs text-red-500 mt-1">
|
||||||
|
税后收入:{{ (item.amount * 0.94).toFixed(2) }}
|
||||||
|
</div>
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex items-center justify-between mb-2">
|
||||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium"
|
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium"
|
||||||
:class="getReportTypeStyle(item.product_name)">
|
:class="getReportTypeStyle(item.product_name)">
|
||||||
|
|||||||
@@ -24,6 +24,10 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 税后收入 -->
|
||||||
|
<div v-if="item.status !== 3" class="text-xs text-red-500 mt-1">
|
||||||
|
税后收入:{{ (item.amount * 0.94).toFixed(2) }}
|
||||||
|
</div>
|
||||||
<!-- 推广返佣显示返佣类型 tag -->
|
<!-- 推广返佣显示返佣类型 tag -->
|
||||||
<!-- <div class="flex items-center mb-2" v-if="activeTab === 'promote' && item.rebate_type">
|
<!-- <div class="flex items-center mb-2" v-if="activeTab === 'promote' && item.rebate_type">
|
||||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium"
|
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium"
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import { useUserStore } from '@/stores/userStore'
|
|||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { mobileCodeLogin } from '@/api/user'
|
import { mobileCodeLogin } from '@/api/user'
|
||||||
import useApiFetch from '@/composables/useApiFetch'
|
import useApiFetch from '@/composables/useApiFetch'
|
||||||
|
import { useAliyunCaptcha } from '@/composables/useAliyunCaptcha'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const agentStore = useAgentStore()
|
const agentStore = useAgentStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
const { runWithCaptcha } = useAliyunCaptcha()
|
||||||
|
|
||||||
const phoneNumber = ref('')
|
const phoneNumber = ref('')
|
||||||
const verificationCode = ref('')
|
const verificationCode = ref('')
|
||||||
@@ -34,12 +36,14 @@ async function sendVerificationCode() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, error } = await useApiFetch('auth/sendSms')
|
// 使用阿里云滑块验证码保护发送短信接口
|
||||||
.post({ mobile: phoneNumber.value, actionType: 'login' })
|
runWithCaptcha(
|
||||||
.json()
|
(captchaVerifyParam) => useApiFetch('auth/sendSms')
|
||||||
|
.post({ mobile: phoneNumber.value, actionType: 'login', captchaVerifyParam })
|
||||||
if (data.value && !error.value) {
|
.json(),
|
||||||
if (data.value.code === 200) {
|
(result) => {
|
||||||
|
// result 已经是解包后的响应数据(data.value)
|
||||||
|
if (result && result.code === 200) {
|
||||||
showToast({ message: "获取成功" });
|
showToast({ message: "获取成功" });
|
||||||
startCountdown()
|
startCountdown()
|
||||||
// 聚焦到验证码输入框
|
// 聚焦到验证码输入框
|
||||||
@@ -49,10 +53,11 @@ async function sendVerificationCode() {
|
|||||||
verificationCodeInput.focus();
|
verificationCodeInput.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else if (result) {
|
||||||
showToast(data.value.msg)
|
showToast(result.msg || "发送失败")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function startCountdown() {
|
function startCountdown() {
|
||||||
|
|||||||
@@ -55,13 +55,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 右侧资产信息(仅代理显示) -->
|
<!-- 右侧资产信息(仅代理显示) -->
|
||||||
<div v-if="isAgent" class="text-right">
|
<div v-if="isAgent" class="text-right">
|
||||||
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">余额</div>
|
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">税后收入</div>
|
||||||
<div class="text-2xl font-bold"
|
<div class="text-2xl font-bold"
|
||||||
:style="(revenueData?.balance || 0) < 0 ? 'color: #ef4444;' : 'color: var(--van-theme-primary);'">
|
:style="afterTax(revenueData?.balance) < 0 ? 'color: #ef4444;' : 'color: var(--van-theme-primary);'">
|
||||||
¥ {{ (revenueData?.balance || 0).toFixed(2) }}
|
¥ {{ afterTax(revenueData?.balance).toFixed(2) }}
|
||||||
</div>
|
</div>
|
||||||
<!-- 负数余额提示 -->
|
<!-- 负数余额提示 -->
|
||||||
<div v-if="(revenueData?.balance || 0) < 0" class="text-xs mt-1" style="color: #ef4444;">
|
<div v-if="afterTax(revenueData?.balance) < 0" class="text-xs mt-1" style="color: #ef4444;">
|
||||||
账户存在欠款,需补足后才能提现
|
账户存在欠款,需补足后才能提现
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,21 +71,21 @@
|
|||||||
<div class="grid grid-cols-3 gap-3 mb-4 pt-4 border-t"
|
<div class="grid grid-cols-3 gap-3 mb-4 pt-4 border-t"
|
||||||
style="border-color: var(--van-border-color);">
|
style="border-color: var(--van-border-color);">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">累计收益</div>
|
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">累计收益(税后)</div>
|
||||||
<div class="text-base font-semibold" style="color: var(--van-text-color);">
|
<div class="text-base font-semibold" style="color: var(--van-text-color);">
|
||||||
¥ {{ (revenueData?.total_earnings || 0).toFixed(2) }}
|
¥ {{ afterTax(revenueData?.total_earnings).toFixed(2) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">风险保障金</div>
|
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">风险保障金</div>
|
||||||
<div class="text-base font-semibold" style="color: var(--van-text-color);">
|
<div class="text-base font-semibold" style="color: var(--van-text-color);">
|
||||||
¥ {{ (revenueData?.frozen_balance || 0).toFixed(2) }}
|
¥ {{ afterTax(revenueData?.frozen_balance || 0).toFixed(2) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">累计提现</div>
|
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">累计提现</div>
|
||||||
<div class="text-base font-semibold" style="color: var(--van-text-color);">
|
<div class="text-base font-semibold" style="color: var(--van-text-color);">
|
||||||
¥ {{ (revenueData?.withdrawn_amount || 0).toFixed(2) }}
|
¥ {{ afterTax(revenueData?.withdrawn_amount).toFixed(2) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -116,12 +116,12 @@
|
|||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<van-icon name="balance-list" class="text-lg mr-2"
|
<van-icon name="balance-list" class="text-lg mr-2"
|
||||||
style="color: var(--color-warning);" />
|
style="color: var(--color-warning);" />
|
||||||
<span class="text-base font-bold" style="color: var(--van-text-color);">我的推广收益</span>
|
<span class="text-base font-bold" style="color: var(--van-text-color);">我的推广收益(税后)</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center mb-3">
|
<div class="text-center mb-3">
|
||||||
<div class="text-xl font-bold" style="color: var(--color-warning);">
|
<div class="text-xl font-bold" style="color: var(--color-warning);">
|
||||||
¥ {{ (revenueData?.commission_total || 0).toFixed(2) }}
|
¥ {{ afterTax(revenueData?.commission_total).toFixed(2) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm mt-0.5" style="color: var(--van-text-color-2);">累计总收益</div>
|
<div class="text-sm mt-0.5" style="color: var(--van-text-color-2);">累计总收益</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -129,13 +129,13 @@
|
|||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">今日</div>
|
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">今日</div>
|
||||||
<div class="text-base font-semibold" style="color: var(--color-warning);">
|
<div class="text-base font-semibold" style="color: var(--color-warning);">
|
||||||
¥ {{ (revenueData?.commission_today || 0).toFixed(2) }}
|
¥ {{ afterTax(revenueData?.commission_today).toFixed(2) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">本月</div>
|
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">本月</div>
|
||||||
<div class="text-base font-semibold" style="color: var(--color-warning);">
|
<div class="text-base font-semibold" style="color: var(--color-warning);">
|
||||||
¥ {{ (revenueData?.commission_month || 0).toFixed(2) }}
|
¥ {{ afterTax(revenueData?.commission_month).toFixed(2) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -151,12 +151,12 @@
|
|||||||
<div class="flex items-center justify-between mb-3">
|
<div class="flex items-center justify-between mb-3">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<van-icon name="gift-o" class="text-lg mr-2" style="color: var(--color-success);" />
|
<van-icon name="gift-o" class="text-lg mr-2" style="color: var(--color-success);" />
|
||||||
<span class="text-base font-bold" style="color: var(--van-text-color);">下级推广收益</span>
|
<span class="text-base font-bold" style="color: var(--van-text-color);">下级推广收益(税后)</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center mb-3">
|
<div class="text-center mb-3">
|
||||||
<div class="text-xl font-bold" style="color: var(--color-success);">
|
<div class="text-xl font-bold" style="color: var(--color-success);">
|
||||||
¥ {{ (revenueData?.rebate_total || 0).toFixed(2) }}
|
¥ {{ afterTax(revenueData?.rebate_total).toFixed(2) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm mt-0.5" style="color: var(--van-text-color-2);">累计总收益</div>
|
<div class="text-sm mt-0.5" style="color: var(--van-text-color-2);">累计总收益</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -164,13 +164,13 @@
|
|||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">今日</div>
|
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">今日</div>
|
||||||
<div class="text-base font-semibold" style="color: var(--color-success);">
|
<div class="text-base font-semibold" style="color: var(--color-success);">
|
||||||
¥ {{ (revenueData?.rebate_today || 0).toFixed(2) }}
|
¥ {{ afterTax(revenueData?.rebate_today).toFixed(2) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">本月</div>
|
<div class="text-sm mb-1" style="color: var(--van-text-color-2);">本月</div>
|
||||||
<div class="text-base font-semibold" style="color: var(--color-success);">
|
<div class="text-base font-semibold" style="color: var(--color-success);">
|
||||||
¥ {{ (revenueData?.rebate_month || 0).toFixed(2) }}
|
¥ {{ afterTax(revenueData?.rebate_month).toFixed(2) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -289,6 +289,10 @@ const { userName, userAvatar, isLoggedIn, mobile } = storeToRefs(userStore);
|
|||||||
const { isWeChat } = useEnv();
|
const { isWeChat } = useEnv();
|
||||||
const revenueData = ref(null);
|
const revenueData = ref(null);
|
||||||
|
|
||||||
|
// 税率常量(6%)
|
||||||
|
const TAX_RATE = 0.06;
|
||||||
|
const afterTax = (amount) => (amount || 0) * (1 - TAX_RATE);
|
||||||
|
|
||||||
// 等级名称映射(数字等级)
|
// 等级名称映射(数字等级)
|
||||||
const levelNamesMap = {
|
const levelNamesMap = {
|
||||||
1: "普通代理",
|
1: "普通代理",
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import { useAgentStore } from '@/stores/agentStore'
|
|||||||
import { useUserStore } from '@/stores/userStore'
|
import { useUserStore } from '@/stores/userStore'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import useApiFetch from '@/composables/useApiFetch'
|
import useApiFetch from '@/composables/useApiFetch'
|
||||||
|
import { useAliyunCaptcha } from '@/composables/useAliyunCaptcha'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const agentStore = useAgentStore()
|
const agentStore = useAgentStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
const { runWithCaptcha } = useAliyunCaptcha()
|
||||||
|
|
||||||
const phoneNumber = ref('')
|
const phoneNumber = ref('')
|
||||||
const verificationCode = ref('')
|
const verificationCode = ref('')
|
||||||
@@ -69,25 +71,26 @@ async function sendVerificationCode() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, error } = await useApiFetch('auth/sendSms')
|
// 使用阿里云滑块验证码保护发送短信接口
|
||||||
.post({ mobile: phoneNumber.value, actionType: 'agentApply' })
|
runWithCaptcha(
|
||||||
.json()
|
(captchaVerifyParam) => useApiFetch('auth/sendSms')
|
||||||
|
.post({ mobile: phoneNumber.value, actionType: 'agentApply', captchaVerifyParam })
|
||||||
if (data.value && !error.value) {
|
.json(),
|
||||||
if (data.value.code === 200) {
|
(result) => {
|
||||||
|
if (result && result.code === 200) {
|
||||||
showToast({ message: "获取成功" });
|
showToast({ message: "获取成功" });
|
||||||
startCountdown()
|
startCountdown();
|
||||||
// 聚焦到验证码输入框
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const verificationCodeInput = document.getElementById('verificationCode');
|
const verificationCodeInput = document.getElementById('verificationCode');
|
||||||
if (verificationCodeInput) {
|
if (verificationCodeInput) {
|
||||||
verificationCodeInput.focus();
|
verificationCodeInput.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else if (result) {
|
||||||
showToast(data.value.msg)
|
showToast(result.msg || "发送失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startCountdown() {
|
function startCountdown() {
|
||||||
|
|||||||
@@ -105,34 +105,34 @@
|
|||||||
<!-- 金额提示 -->
|
<!-- 金额提示 -->
|
||||||
<div class="text-sm mb-2 space-y-1" style="color: var(--van-text-color);">
|
<div class="text-sm mb-2 space-y-1" style="color: var(--van-text-color);">
|
||||||
<div>
|
<div>
|
||||||
可提现金额:<span class="font-semibold"
|
可提现金额(税后):<span class="font-semibold"
|
||||||
:style="(availableAmount || 0) < 0 ? 'color: #ef4444;' : 'color: var(--van-theme-primary);'">
|
:style="afterTax(availableAmount) < 0 ? 'color: #ef4444;' : 'color: var(--van-theme-primary);'">
|
||||||
¥{{ (availableAmount || 0).toFixed(2) }}
|
¥{{ afterTax(availableAmount).toFixed(2) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="alipayMonthQuota > 0" class="text-sm" style="color: var(--van-text-color-2);">
|
<div v-if="alipayMonthQuota > 0" class="text-sm" style="color: var(--van-text-color-2);">
|
||||||
本月支付宝提现额度:
|
本月支付宝提现额度:
|
||||||
<span class="font-semibold">¥{{ alipayMonthQuota.toFixed(2) }}</span>
|
<span class="font-semibold">¥{{ afterTax(alipayMonthQuota).toFixed(2) }}</span>
|
||||||
,已用:
|
,已用:
|
||||||
<span class="font-semibold">¥{{ alipayMonthUsed.toFixed(2) }}</span>
|
<span class="font-semibold">¥{{ afterTax(alipayMonthUsed).toFixed(2) }}</span>
|
||||||
,剩余:
|
,剩余:
|
||||||
<span class="font-semibold"
|
<span class="font-semibold"
|
||||||
:style="alipayMonthRemain <= 0 ? 'color: #ef4444;' : 'color: var(--van-theme-primary);'">
|
:style="afterTax(alipayMonthRemain) <= 0 ? 'color: #ef4444;' : 'color: var(--van-theme-primary);'">
|
||||||
¥{{ alipayMonthRemain.toFixed(2) }}
|
¥{{ afterTax(alipayMonthRemain).toFixed(2) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="alipayMonthRemain <= 0 && alipayMonthQuota > 0" class="text-sm text-red-500">
|
<div v-if="afterTax(alipayMonthRemain) <= 0 && alipayMonthQuota > 0" class="text-sm text-red-500">
|
||||||
本月支付宝提现额度已用完,请使用银行卡提现。
|
本月支付宝提现额度已用完,请使用银行卡提现。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 负数余额提示 -->
|
<!-- 负数余额提示 -->
|
||||||
<div v-if="(availableAmount || 0) < 0" class="mb-4 p-3 rounded-lg bg-red-50 border border-red-200">
|
<div v-if="afterTax(availableAmount) < 0" class="mb-4 p-3 rounded-lg bg-red-50 border border-red-200">
|
||||||
<div class="flex items-start gap-2">
|
<div class="flex items-start gap-2">
|
||||||
<van-icon name="warning-o" class="text-red-500 mt-0.5" />
|
<van-icon name="warning-o" class="text-red-500 mt-0.5" />
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="text-sm font-medium text-red-700 mb-1">账户存在欠款</div>
|
<div class="text-sm font-medium text-red-700 mb-1">账户存在欠款</div>
|
||||||
<div class="text-sm text-red-600">
|
<div class="text-sm text-red-600">
|
||||||
您的账户余额为负数,存在欠款金额 ¥{{ Math.abs(availableAmount || 0).toFixed(2) }}。
|
您的账户余额为负数,存在欠款金额 ¥{{ Math.abs(afterTax(availableAmount)).toFixed(2) }}。
|
||||||
请先通过后续订单收益补足欠款后,才能申请提现。
|
请先通过后续订单收益补足欠款后,才能申请提现。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -256,19 +256,19 @@
|
|||||||
|
|
||||||
<!-- 金额提示 -->
|
<!-- 金额提示 -->
|
||||||
<div class="text-sm mb-2" style="color: var(--van-text-color);">
|
<div class="text-sm mb-2" style="color: var(--van-text-color);">
|
||||||
可提现金额:<span class="font-semibold"
|
可提现金额(税后):<span class="font-semibold"
|
||||||
:style="(availableAmount || 0) < 0 ? 'color: #ef4444;' : 'color: var(--van-theme-primary);'">
|
:style="afterTax(availableAmount) < 0 ? 'color: #ef4444;' : 'color: var(--van-theme-primary);'">
|
||||||
¥{{ (availableAmount || 0).toFixed(2) }}
|
¥{{ afterTax(availableAmount).toFixed(2) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 负数余额提示 -->
|
<!-- 负数余额提示 -->
|
||||||
<div v-if="(availableAmount || 0) < 0" class="mb-4 p-3 rounded-lg bg-red-50 border border-red-200">
|
<div v-if="afterTax(availableAmount) < 0" class="mb-4 p-3 rounded-lg bg-red-50 border border-red-200">
|
||||||
<div class="flex items-start gap-2">
|
<div class="flex items-start gap-2">
|
||||||
<van-icon name="warning-o" class="text-red-500 mt-0.5" />
|
<van-icon name="warning-o" class="text-red-500 mt-0.5" />
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="text-sm font-medium text-red-700 mb-1">账户存在欠款</div>
|
<div class="text-sm font-medium text-red-700 mb-1">账户存在欠款</div>
|
||||||
<div class="text-sm text-red-600">
|
<div class="text-sm text-red-600">
|
||||||
您的账户余额为负数,存在欠款金额 ¥{{ Math.abs(availableAmount || 0).toFixed(2) }}。
|
您的账户余额为负数,存在欠款金额 ¥{{ Math.abs(afterTax(availableAmount)).toFixed(2) }}。
|
||||||
请先通过后续订单收益补足欠款后,才能申请提现。
|
请先通过后续订单收益补足欠款后,才能申请提现。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -304,11 +304,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 提交按钮 -->
|
<!-- 提交按钮 -->
|
||||||
<van-button type="primary" block :loading="isSubmitting" :disabled="(availableAmount || 0) < 0"
|
<van-button type="primary" block :loading="isSubmitting" :disabled="afterTax(availableAmount) < 0"
|
||||||
class="text-white rounded-xl shadow-lg h-12 font-bold text-base"
|
class="text-white rounded-xl shadow-lg h-12 font-bold text-base"
|
||||||
style="background: linear-gradient(135deg, var(--van-theme-primary), var(--van-theme-primary-dark));"
|
style="background: linear-gradient(135deg, var(--van-theme-primary), var(--van-theme-primary-dark));"
|
||||||
@click="handleSubmit">
|
@click="handleSubmit">
|
||||||
{{ (availableAmount || 0) < 0 ? '账户存在欠款,无法提现' : '立即提现' }} </van-button>
|
{{ afterTax(availableAmount) < 0 ? '账户存在欠款,无法提现' : '立即提现' }} </van-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 税务确认弹窗 -->
|
<!-- 税务确认弹窗 -->
|
||||||
@@ -323,12 +323,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 金额详情 -->
|
<!-- 金额详情 -->
|
||||||
<div class="rounded-xl p-4 space-y-3" style="background-color: var(--van-background-color-light);">
|
<div class="rounded-xl p-4" style="background-color: var(--van-background-color-light);">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<span style="color: var(--van-text-color-2);">提现金额:</span>
|
<span style="color: var(--van-text-color-2);">提现到账金额:</span>
|
||||||
<span class="font-semibold" style="color: var(--van-text-color);">¥{{ amount }}</span>
|
<span class="font-bold text-lg" style="color: var(--van-theme-primary);">¥{{ amount }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center">
|
<!-- <div class="flex justify-between items-center">
|
||||||
<span style="color: var(--van-text-color-2);">预估税费:</span>
|
<span style="color: var(--van-text-color-2);">预估税费:</span>
|
||||||
<span class="font-semibold text-red-500">
|
<span class="font-semibold text-red-500">
|
||||||
-¥{{ estimatedTaxAmount.toFixed(2) }}
|
-¥{{ estimatedTaxAmount.toFixed(2) }}
|
||||||
@@ -340,7 +340,7 @@
|
|||||||
<span class="font-bold text-lg" style="color: var(--van-theme-primary);">¥{{
|
<span class="font-bold text-lg" style="color: var(--van-theme-primary);">¥{{
|
||||||
estimatedActualAmount.toFixed(2) }}</span>
|
estimatedActualAmount.toFixed(2) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 提示信息 -->
|
<!-- 提示信息 -->
|
||||||
@@ -348,10 +348,11 @@
|
|||||||
<p class="text-blue-600 font-medium mb-2">
|
<p class="text-blue-600 font-medium mb-2">
|
||||||
税收说明
|
税收说明
|
||||||
</p>
|
</p>
|
||||||
<div class="text-gray-600 space-y-1">
|
<div class="text-gray-600">
|
||||||
<p>• 提现金额:¥{{ amount }}</p>
|
<p>当前税率:6%</p>
|
||||||
|
<!-- <p>• 提现金额:¥{{ amount }}</p>
|
||||||
<p>• 税率:6%</p>
|
<p>• 税率:6%</p>
|
||||||
<p class="text-blue-600">• 税收计算:¥{{ amount }} × 6% = ¥{{ taxAmount.toFixed(2) }}</p>
|
<p class="text-blue-600">• 税收计算:¥{{ amount }} × 6% = ¥{{ taxAmount.toFixed(2) }}</p> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -457,6 +458,14 @@
|
|||||||
|
|
||||||
const agentStore = useAgentStore();
|
const agentStore = useAgentStore();
|
||||||
const dialogStore = useDialogStore();
|
const dialogStore = useDialogStore();
|
||||||
|
|
||||||
|
// 税率常量(6%)
|
||||||
|
const TAX_RATE = 0.06;
|
||||||
|
// 税后金额 = 税前金额 * (1 - TAX_RATE)
|
||||||
|
const afterTax = (amount) => (amount || 0) * (1 - TAX_RATE);
|
||||||
|
// 税前金额 = 税后金额 / (1 - TAX_RATE)
|
||||||
|
const beforeTax = (afterTaxAmount) => (afterTaxAmount || 0) / (1 - TAX_RATE);
|
||||||
|
|
||||||
// Tab 切换
|
// Tab 切换
|
||||||
const activeTab = ref("alipay");
|
const activeTab = ref("alipay");
|
||||||
// 状态管理
|
// 状态管理
|
||||||
@@ -602,6 +611,9 @@
|
|||||||
// 表单验证
|
// 表单验证
|
||||||
const validateAmount = (val) => {
|
const validateAmount = (val) => {
|
||||||
const num = Number(val);
|
const num = Number(val);
|
||||||
|
// 税前金额(用于与余额比较)
|
||||||
|
const preTaxAmount = beforeTax(num);
|
||||||
|
|
||||||
// 如果余额为负数,不允许提现
|
// 如果余额为负数,不允许提现
|
||||||
if (availableAmount.value < 0) {
|
if (availableAmount.value < 0) {
|
||||||
return false;
|
return false;
|
||||||
@@ -609,14 +621,18 @@
|
|||||||
|
|
||||||
if (activeTab.value === 'alipay') {
|
if (activeTab.value === 'alipay') {
|
||||||
// 支付宝金额需同时满足余额与额度限制
|
// 支付宝金额需同时满足余额与额度限制
|
||||||
if (alipayMonthRemain.value <= 0) {
|
// 税后额度剩余
|
||||||
|
const afterTaxRemain = afterTax(alipayMonthRemain.value);
|
||||||
|
if (afterTaxRemain <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return num >= 50 && num <= availableAmount.value && num <= alipayMonthRemain.value;
|
// 税后金额 >= 50,税前金额 <= 余额,税后金额 <= 税后额度剩余
|
||||||
|
return num >= 50 && preTaxAmount <= availableAmount.value && num <= afterTaxRemain;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 银行卡提现只受余额限制
|
// 银行卡提现只受余额限制
|
||||||
return num >= 50 && num <= availableAmount.value;
|
// 税后金额 >= 50,税前金额 <= 余额
|
||||||
|
return num >= 50 && preTaxAmount <= availableAmount.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
@@ -664,19 +680,23 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amountNum > availableAmount.value) {
|
// 税前金额(用于与余额比较)
|
||||||
|
const preTaxAmount = beforeTax(amountNum);
|
||||||
|
|
||||||
|
if (preTaxAmount > availableAmount.value) {
|
||||||
showToast("超过可提现金额");
|
showToast("超过可提现金额");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 支付宝额度检查
|
// 支付宝额度检查
|
||||||
if (activeTab.value === 'alipay') {
|
if (activeTab.value === 'alipay') {
|
||||||
if (alipayMonthRemain.value <= 0) {
|
const afterTaxRemain = afterTax(alipayMonthRemain.value);
|
||||||
|
if (afterTaxRemain <= 0) {
|
||||||
showToast("本月支付宝提现额度已用完,请使用银行卡提现");
|
showToast("本月支付宝提现额度已用完,请使用银行卡提现");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (amountNum > alipayMonthRemain.value) {
|
if (amountNum > afterTaxRemain) {
|
||||||
showToast(`本月支付宝最高可提现 ${alipayMonthRemain.value.toFixed(2)} 元,请调整提现金额或使用银行卡提现`);
|
showToast(`本月支付宝最高可提现 ${afterTaxRemain.toFixed(2)} 元,请调整提现金额或使用银行卡提现`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -711,9 +731,12 @@
|
|||||||
isSubmitting.value = true;
|
isSubmitting.value = true;
|
||||||
try {
|
try {
|
||||||
const withdrawalType = activeTab.value === 'alipay' ? 1 : 2;
|
const withdrawalType = activeTab.value === 'alipay' ? 1 : 2;
|
||||||
|
// 用户输入的是税后金额,传给后端需要逆推为税前金额
|
||||||
|
const preTaxWithdrawAmount = beforeTax(Number(amount.value));
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
withdrawal_type: withdrawalType,
|
withdrawal_type: withdrawalType,
|
||||||
amount: Number(amount.value),
|
amount: preTaxWithdrawAmount, // 传税前金额给后端
|
||||||
payee_name: realName.value,
|
payee_name: realName.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -756,14 +779,14 @@
|
|||||||
if (status.value === 2) resetPage();
|
if (status.value === 2) resetPage();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// 填充最大金额
|
// 填充最大金额(税后金额)
|
||||||
const fillMaxAmount = () => {
|
const fillMaxAmount = () => {
|
||||||
if (activeTab.value === 'alipay') {
|
if (activeTab.value === 'alipay') {
|
||||||
const maxByQuota = alipayMonthRemain.value || 0;
|
const maxByQuotaAfterTax = afterTax(alipayMonthRemain.value) || 0;
|
||||||
const maxByBalance = availableAmount.value || 0;
|
const maxByBalanceAfterTax = afterTax(availableAmount.value) || 0;
|
||||||
amount.value = Math.min(maxByQuota, maxByBalance);
|
amount.value = Math.min(maxByQuotaAfterTax, maxByBalanceAfterTax);
|
||||||
} else {
|
} else {
|
||||||
amount.value = availableAmount.value;
|
amount.value = afterTax(availableAmount.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ export default defineConfig({
|
|||||||
strictPort: true, // 如果端口被占用则抛出错误而不是使用下一个可用端口
|
strictPort: true, // 如果端口被占用则抛出错误而不是使用下一个可用端口
|
||||||
proxy: {
|
proxy: {
|
||||||
"/api/v1": {
|
"/api/v1": {
|
||||||
target: "http://127.0.0.1:8888", // 本地接口地址
|
// target: "http://127.0.0.1:8888", // 本地接口地址
|
||||||
// target: "https://www.onecha.cn", // 修改为带 www 的域名,避免 301 重定向
|
target: "https://www.onecha.cn", // 修改为带 www 的域名,避免 301 重定向
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path, // 可选:确保路径不被修改
|
rewrite: (path) => path, // 可选:确保路径不被修改
|
||||||
},
|
},
|
||||||
|
|||||||
99
vite.config.js.timestamp-1771994358985-793c3aa69da34.mjs
Normal file
99
vite.config.js.timestamp-1771994358985-793c3aa69da34.mjs
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user