add
This commit is contained in:
1
src/auto-imports.d.ts
vendored
1
src/auto-imports.d.ts
vendored
@@ -117,6 +117,7 @@ declare global {
|
||||
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
|
||||
const useAgent: typeof import('./composables/useAgent.js')['useAgent']
|
||||
const useAgentStore: typeof import('./stores/agentStore.js')['useAgentStore']
|
||||
const useAliyunCaptcha: typeof import('./composables/useAliyunCaptcha.js')['default']
|
||||
const useAnimate: typeof import('@vueuse/core')['useAnimate']
|
||||
const useApiFetch: typeof import('./composables/useApiFetch.js')['default']
|
||||
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
|
||||
|
||||
@@ -121,8 +121,11 @@
|
||||
const router = useRouter();
|
||||
const show = defineModel("show");
|
||||
import { useCascaderAreaData } from "@vant/area-data";
|
||||
import { useAliyunCaptcha } from "@/composables/useAliyunCaptcha";
|
||||
import { showToast } from "vant"; // 引入 showToast 方法
|
||||
const emit = defineEmits(); // 确保 emit 可以正确使用
|
||||
|
||||
const { runWithCaptcha } = useAliyunCaptcha();
|
||||
const props = defineProps({
|
||||
ancestor: {
|
||||
type: String,
|
||||
@@ -169,22 +172,20 @@ const getSmsCode = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
loadingSms.value = true;
|
||||
|
||||
const { data, error } = await useApiFetch("auth/sendSms")
|
||||
.post({ mobile: form.value.mobile, actionType: "agentApply" })
|
||||
.json();
|
||||
|
||||
loadingSms.value = false;
|
||||
|
||||
if (data.value && !error.value) {
|
||||
if (data.value.code === 200) {
|
||||
showToast({ message: "获取成功" });
|
||||
startCountdown(); // 启动倒计时
|
||||
} else {
|
||||
showToast(data.value.msg);
|
||||
runWithCaptcha(
|
||||
(captchaVerifyParam) =>
|
||||
useApiFetch("auth/sendSms")
|
||||
.post({ mobile: form.value.mobile, actionType: "agentApply", captchaVerifyParam })
|
||||
.json(),
|
||||
(res) => {
|
||||
if (res.code === 200) {
|
||||
showToast({ message: "获取成功" });
|
||||
startCountdown();
|
||||
} else {
|
||||
showToast(res.msg || "获取失败,请重试");
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
let timer = null;
|
||||
|
||||
|
||||
@@ -282,8 +282,11 @@ import { useRoute, useRouter } from "vue-router";
|
||||
import { useUserStore } from "@/stores/userStore";
|
||||
import { useDialogStore } from "@/stores/dialogStore";
|
||||
import { useEnv } from "@/composables/useEnv";
|
||||
import { useAliyunCaptcha } from "@/composables/useAliyunCaptcha";
|
||||
import { showConfirmDialog } from "vant";
|
||||
|
||||
const { runWithCaptcha } = useAliyunCaptcha();
|
||||
|
||||
import Payment from "@/components/Payment.vue";
|
||||
import BindPhoneDialog from "@/components/BindPhoneDialog.vue";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
@@ -631,22 +634,26 @@ async function sendVerificationCode() {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data, error } = await useApiFetch("/auth/sendSms")
|
||||
.post({ mobile: formData.mobile, actionType: "query" })
|
||||
.json();
|
||||
|
||||
if (!error.value && data.value.code === 200) {
|
||||
showToast({ message: "验证码发送成功", type: "success" });
|
||||
startCountdown();
|
||||
nextTick(() => {
|
||||
const verificationCodeInput = document.getElementById('verificationCode');
|
||||
if (verificationCodeInput) {
|
||||
verificationCodeInput.focus();
|
||||
runWithCaptcha(
|
||||
(captchaVerifyParam) =>
|
||||
useApiFetch("/auth/sendSms")
|
||||
.post({ mobile: formData.mobile, actionType: "query", captchaVerifyParam })
|
||||
.json(),
|
||||
(res) => {
|
||||
if (res.code === 200) {
|
||||
showToast({ message: "验证码发送成功", type: "success" });
|
||||
startCountdown();
|
||||
nextTick(() => {
|
||||
const verificationCodeInput = document.getElementById('verificationCode');
|
||||
if (verificationCodeInput) {
|
||||
verificationCodeInput.focus();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showToast({ message: res.msg || "验证码发送失败,请重试" });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showToast({ message: "验证码发送失败,请重试" });
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let timer = null;
|
||||
|
||||
155
src/composables/useAliyunCaptcha.js
Normal file
155
src/composables/useAliyunCaptcha.js
Normal file
@@ -0,0 +1,155 @@
|
||||
import { showToast, showLoadingToast, closeToast } from "vant";
|
||||
import useApiFetch from "@/composables/useApiFetch";
|
||||
|
||||
const ALIYUN_CAPTCHA_SCENE_ID = "wynt39to";
|
||||
const ENABLE_ENCRYPTED = false;
|
||||
|
||||
let captchaInitialised = false;
|
||||
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;
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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",
|
||||
});
|
||||
}
|
||||
|
||||
export function useAliyunCaptcha() {
|
||||
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();
|
||||
|
||||
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;
|
||||
@@ -1,7 +1,9 @@
|
||||
<script setup>
|
||||
import { ref, computed, onUnmounted, nextTick } from 'vue'
|
||||
import { showToast } from 'vant'
|
||||
import ClickCaptcha from '@/components/ClickCaptcha.vue'
|
||||
import { useAliyunCaptcha } from '@/composables/useAliyunCaptcha'
|
||||
|
||||
const { runWithCaptcha } = useAliyunCaptcha()
|
||||
|
||||
const router = useRouter()
|
||||
const phoneNumber = ref('')
|
||||
@@ -13,15 +15,6 @@ const isCountingDown = ref(false)
|
||||
const countdown = ref(60)
|
||||
let timer = null
|
||||
|
||||
// 验证组件状态
|
||||
const showCaptcha = ref(false)
|
||||
const captchaVerified = ref(false)
|
||||
|
||||
// 聚焦状态变量
|
||||
const phoneFocused = ref(false)
|
||||
const codeFocused = ref(false)
|
||||
const passwordFocused = ref(false)
|
||||
|
||||
const isPhoneNumberValid = computed(() => {
|
||||
return /^1[3-9]\d{9}$/.test(phoneNumber.value)
|
||||
})
|
||||
@@ -41,25 +34,27 @@ async function sendVerificationCode() {
|
||||
showToast({ message: "请输入有效的手机号" });
|
||||
return
|
||||
}
|
||||
const { data, error } = await useApiFetch('auth/sendSms')
|
||||
.post({ mobile: phoneNumber.value, actionType: 'login' })
|
||||
.json()
|
||||
|
||||
if (data.value && !error.value) {
|
||||
if (data.value.code === 200) {
|
||||
showToast({ message: "获取成功" });
|
||||
startCountdown()
|
||||
// 聚焦到验证码输入框
|
||||
nextTick(() => {
|
||||
const verificationCodeInput = document.getElementById('verificationCode');
|
||||
if (verificationCodeInput) {
|
||||
verificationCodeInput.focus();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showToast(data.value.msg)
|
||||
runWithCaptcha(
|
||||
(captchaVerifyParam) =>
|
||||
useApiFetch('auth/sendSms')
|
||||
.post({ mobile: phoneNumber.value, actionType: 'login', captchaVerifyParam })
|
||||
.json(),
|
||||
(res) => {
|
||||
if (res.code === 200) {
|
||||
showToast({ message: "获取成功" });
|
||||
startCountdown()
|
||||
nextTick(() => {
|
||||
const verificationCodeInput = document.getElementById('verificationCode');
|
||||
if (verificationCodeInput) {
|
||||
verificationCodeInput.focus();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showToast(res.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function startCountdown() {
|
||||
@@ -95,24 +90,9 @@ async function handleLogin() {
|
||||
showToast({ message: "请先同意用户协议" });
|
||||
return
|
||||
}
|
||||
// 显示验证组件
|
||||
showCaptcha.value = true
|
||||
}
|
||||
|
||||
// 验证成功回调
|
||||
function handleCaptchaSuccess() {
|
||||
captchaVerified.value = true
|
||||
showCaptcha.value = false
|
||||
// 执行实际的登录逻辑
|
||||
performLogin()
|
||||
}
|
||||
|
||||
// 验证关闭回调
|
||||
function handleCaptchaClose() {
|
||||
showCaptcha.value = false
|
||||
}
|
||||
|
||||
// 执行实际的登录逻辑
|
||||
async function performLogin() {
|
||||
const { data, error } = await useApiFetch('/user/mobileCodeLogin')
|
||||
.post({ mobile: phoneNumber.value, code: verificationCode.value })
|
||||
@@ -200,9 +180,6 @@ const onClickLeft = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 点击验证组件 -->
|
||||
<ClickCaptcha :visible="showCaptcha" @success="handleCaptchaSuccess" @close="handleCaptchaClose" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user