This commit is contained in:
Mrx
2026-02-25 11:46:19 +08:00
6 changed files with 354 additions and 161 deletions

View File

@@ -1,21 +1,33 @@
<!DOCTYPE html> <!doctype html>
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> <link
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> rel="apple-touch-icon"
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> sizes="180x180"
<link rel="manifest" href="/site.webmanifest"> href="/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
/>
<link rel="manifest" href="/site.webmanifest" />
<meta <meta
name="viewport" name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=3, user-scalable=no" content="width=device-width, initial-scale=1, maximum-scale=3, user-scalable=no"
/> />
<!-- 基础SEO信息 --> <!-- 基础SEO信息 -->
<title> <title>天远查官网_企业与婚姻关联风险核验_综合履约背景核验</title>
天远查官网_企业与婚姻关联风险核验_综合履约背景核验
</title>
<meta <meta
name="description" name="description"
content="天远查官网(TianYuanCha)聚合官方公示数据,专注于商业安全与资产背调。提供企业工商画像、婚姻状态关联风险、司法涉诉筛查及配偶债务核验。数据实时同步,助您精准规避投资、交易及家庭结合中的经济与法律风险。" content="天远查官网(TianYuanCha)聚合官方公示数据,专注于商业安全与资产背调。提供企业工商画像、婚姻状态关联风险、司法涉诉筛查及配偶债务核验。数据实时同步,助您精准规避投资、交易及家庭结合中的经济与法律风险。"
@@ -28,21 +40,33 @@
<meta name="robots" content="index, follow" /> <meta name="robots" content="index, follow" />
<meta name="googlebot" content="index, follow" /> <meta name="googlebot" content="index, follow" />
<meta name="baidu-site-verification" content="" /> <meta name="baidu-site-verification" content="" />
<!-- Open Graph / Facebook --> <!-- Open Graph / Facebook -->
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="https://www.tianyuancha.cn/" /> <meta property="og:url" content="https://www.zhinengcha.cn/" />
<meta property="og:title" content="天远查官网_企业与婚姻关联风险核验_综合履约背景核验" /> <meta
<meta property="og:description" content="天远查官网(TianYuanCha)聚合官方公示数据,专注于商业安全与资产背调。提供企业工商画像、婚姻状态关联风险、司法涉诉筛查及配偶债务核验。数据实时同步,助您精准规避投资、交易及家庭结合中的经济与法律风险。" /> property="og:title"
content="天远查官网_企业与婚姻关联风险核验_综合履约背景核验"
/>
<meta
property="og:description"
content="天远查官网(TianYuanCha)聚合官方公示数据,专注于商业安全与资产背调。提供企业工商画像、婚姻状态关联风险、司法涉诉筛查及配偶债务核验。数据实时同步,助您精准规避投资、交易及家庭结合中的经济与法律风险。"
/>
<meta property="og:site_name" content="天远查" /> <meta property="og:site_name" content="天远查" />
<meta property="og:locale" content="zh_CN" /> <meta property="og:locale" content="zh_CN" />
<!-- Twitter --> <!-- Twitter -->
<meta property="twitter:card" content="summary" /> <meta property="twitter:card" content="summary" />
<meta property="twitter:url" content="https://www.tianyuancha.cn/" /> <meta property="twitter:url" content="https://www.zhinengcha.cn/" />
<meta property="twitter:title" content="天远查官网_企业与婚姻关联风险核验_综合履约背景核验" /> <meta
<meta property="twitter:description" content="天远查官网(TianYuanCha)聚合官方公示数据,专注于商业安全与资产背调。提供企业工商画像、婚姻状态关联风险、司法涉诉筛查及配偶债务核验。数据实时同步,助您精准规避投资、交易及家庭结合中的经济与法律风险。" /> property="twitter:title"
content="天远查官网_企业与婚姻关联风险核验_综合履约背景核验"
/>
<meta
property="twitter:description"
content="天远查官网(TianYuanCha)聚合官方公示数据,专注于商业安全与资产背调。提供企业工商画像、婚姻状态关联风险、司法涉诉筛查及配偶债务核验。数据实时同步,助您精准规避投资、交易及家庭结合中的经济与法律风险。"
/>
<!-- 其他重要meta标签 --> <!-- 其他重要meta标签 -->
<meta name="theme-color" content="#3498db" /> <meta name="theme-color" content="#3498db" />
<meta name="msapplication-TileColor" content="#3498db" /> <meta name="msapplication-TileColor" content="#3498db" />
@@ -50,31 +74,31 @@
<meta name="apple-mobile-web-app-title" content="天远查" /> <meta name="apple-mobile-web-app-title" content="天远查" />
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" /> <meta name="apple-mobile-web-app-status-bar-style" content="default" />
<!-- 结构化数据 --> <!-- 结构化数据 -->
<script type="application/ld+json"> <script type="application/ld+json">
{ {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "WebSite", "@type": "WebSite",
"name": "天远查", "name": "天远查",
"url": "https://www.tianyuancha.cn/", "url": "https://www.zhinengcha.cn/",
"description": "专业大数据风险报告查询与代理平台,支持个人信用查询、小微企业风控、贷前风险背调等多场景报告应用", "description": "专业大数据风险报告查询与代理平台,支持个人信用查询、小微企业风控、贷前风险背调等多场景报告应用",
"potentialAction": { "potentialAction": {
"@type": "SearchAction", "@type": "SearchAction",
"target": "https://www.tianyuancha.cn/search?q={search_term_string}", "target": "https://www.zhinengcha.cn/search?q={search_term_string}",
"query-input": "required name=search_term_string" "query-input": "required name=search_term_string"
}
} }
}
</script> </script>
<script type="application/ld+json"> <script type="application/ld+json">
{ {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "Organization", "@type": "Organization",
"name": "天远查", "name": "天远查",
"url": "https://www.tianyuancha.cn/", "url": "https://www.zhinengcha.cn/",
"description": "专业大数据风险报告查询与代理平台,支持个人和企业多场景风控应用" "description": "专业大数据风险报告查询与代理平台,支持个人和企业多场景风控应用"
} }
</script> </script>
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
@@ -82,13 +106,19 @@
window.jWeixin = window.wx; window.jWeixin = window.wx;
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.tianyuancha.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" />
<link rel="dns-prefetch" href="https://www.tianyuancha.cn"> <link rel="dns-prefetch" href="https://www.zhinengcha.cn" />
<link rel="dns-prefetch" href="https://res.wx.qq.com"> <link rel="dns-prefetch" href="https://res.wx.qq.com" />
<style> <style>
/* 基础样式 */ /* 基础样式 */
html { html {
@@ -181,6 +211,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" style="display: none;"></div>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
</body> </body>

View File

@@ -117,6 +117,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')['useAliyunCaptcha']
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 useArrayDifference: typeof import('@vueuse/core')['useArrayDifference'] const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']

View File

@@ -461,6 +461,7 @@ import { useDialogStore } from "@/stores/dialogStore";
import { useEnv } from "@/composables/useEnv"; import { useEnv } from "@/composables/useEnv";
import { showConfirmDialog, showToast, DatePicker } from "vant"; import { showConfirmDialog, showToast, DatePicker } from "vant";
import { useInquireForm } from "@/composables/useInquireForm"; import { useInquireForm } from "@/composables/useInquireForm";
import { useAliyunCaptcha } from "@/composables/useAliyunCaptcha";
import Payment from "@/components/Payment.vue"; import Payment from "@/components/Payment.vue";
import BindPhoneDialog from "@/components/BindPhoneDialog.vue"; import BindPhoneDialog from "@/components/BindPhoneDialog.vue";
@@ -557,6 +558,7 @@ const featureData = computed(() => props.featureData || {});
// 使用通用查询表单 composable根据 feature 自动决定字段) // 使用通用查询表单 composable根据 feature 自动决定字段)
const { formData, isPhoneNumberValid, isIdCardValid, isHasInput, buildRequestPayload } = useInquireForm(feature); const { formData, isPhoneNumberValid, isIdCardValid, isHasInput, buildRequestPayload } = useInquireForm(feature);
const { runWithCaptcha } = useAliyunCaptcha();
const userTypeOptions = [ const userTypeOptions = [
{ text: "ETC开户人", value: "1" }, { text: "ETC开户人", value: "1" },
@@ -998,69 +1000,77 @@ function handleSubmit() {
if (!userStore.mobile) { if (!userStore.mobile) {
pendingPayment.value = true; pendingPayment.value = true;
dialogStore.openBindPhone(); dialogStore.openBindPhone();
} else { } else if (isHasInput("verificationCode")) {
submitRequest(); submitRequest();
} else {
// 无短信验证码时,查询前先过滑块
runWithCaptcha(
(captchaVerifyParam) => doQueryPost(captchaVerifyParam),
(res) => {
if (res?.code === 200 && res?.data) handleQueryResult(res);
}
);
} }
} }
async function submitRequest() { function handleQueryResult(res) {
// 根据当前 feature 与字段配置组装请求体 queryId.value = res.data.id;
if (props.type === "promotion") {
localStorage.setItem("token", res.data.accessToken);
localStorage.setItem("refreshAfter", res.data.refreshAfter);
localStorage.setItem("accessExpire", res.data.accessExpire);
}
showPayment.value = true;
emit("submit-success", res.data);
}
function doQueryPost(captchaVerifyParam) {
const req = buildRequestPayload(); const req = buildRequestPayload();
const reqStr = JSON.stringify(req); const reqStr = JSON.stringify(req);
const encodeData = aesEncrypt(reqStr, "ff83609b2b24fc73196aac3d3dfb874f"); const encodeData = aesEncrypt(reqStr, "ff83609b2b24fc73196aac3d3dfb874f");
let apiUrl = "";
let apiUrl = ''; const requestData = { data: encodeData };
let requestData = { data: encodeData }; if (captchaVerifyParam) requestData.captchaVerifyParam = captchaVerifyParam;
if (props.type === "promotion") {
if (props.type === 'promotion') {
apiUrl = `/query/service_agent/${props.feature}`; apiUrl = `/query/service_agent/${props.feature}`;
requestData.agent_identifier = props.linkIdentifier; requestData.agent_identifier = props.linkIdentifier;
} else { } else {
apiUrl = `/query/service/${props.feature}`; apiUrl = `/query/service/${props.feature}`;
} }
return useApiFetch(apiUrl).post(requestData).json();
}
const { data, error } = await useApiFetch(apiUrl) async function submitRequest() {
.post(requestData) const { data, error } = await doQueryPost();
.json(); if (data.value?.code === 200 && data.value?.data) {
handleQueryResult(data.value);
if (data.value.code === 200) {
queryId.value = data.value.data.id;
// 推广查询需要保存token
if (props.type === 'promotion') {
localStorage.setItem("token", data.value.data.accessToken);
localStorage.setItem("refreshAfter", data.value.data.refreshAfter);
localStorage.setItem("accessExpire", data.value.data.accessExpire);
}
showPayment.value = true;
emit('submit-success', data.value.data);
} }
} }
async function sendVerificationCode() { 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;
} }
runWithCaptcha(
const { data, error } = await useApiFetch("/auth/sendSms") (captchaVerifyParam) =>
.post({ mobile: formData.mobile, actionType: "query" }) useApiFetch("/auth/sendSms")
.json(); .post({ mobile: formData.mobile, actionType: "query", captchaVerifyParam })
.json(),
if (!error.value && data.value.code === 200) { (res) => {
showToast({ message: "验证码发送成功", type: "success" }); if (res?.code === 200) {
startCountdown(); showToast({ message: "验证码发送成功", type: "success" });
nextTick(() => { startCountdown();
const verificationCodeInput = document.getElementById('verificationCode'); nextTick(() => {
if (verificationCodeInput) { const el = document.getElementById("verificationCode");
verificationCodeInput.focus(); if (el) el.focus();
});
} else {
showToast({ message: res?.msg || "验证码发送失败,请重试" });
} }
}); }
} else { );
showToast({ message: "验证码发送失败,请重试" });
}
} }
let timer = null; let timer = null;

View File

@@ -3,10 +3,12 @@ import { ref, computed, nextTick } from 'vue'
import { showToast } from 'vant' import { showToast } from 'vant'
import { useDialogStore } from '@/stores/dialogStore' import { useDialogStore } from '@/stores/dialogStore'
import { useUserStore } from '@/stores/userStore' import { useUserStore } from '@/stores/userStore'
import { useAliyunCaptcha } from '@/composables/useAliyunCaptcha'
const emit = defineEmits(['login-success']) const emit = defineEmits(['login-success'])
const dialogStore = useDialogStore() const dialogStore = useDialogStore()
const userStore = useUserStore() const userStore = useUserStore()
const { runWithCaptcha } = useAliyunCaptcha()
const phoneNumber = ref('') const phoneNumber = ref('')
const verificationCode = ref('') const verificationCode = ref('')
@@ -35,31 +37,30 @@ const canLogin = computed(() => {
} }
}) })
async function sendVerificationCode() { 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: 'login' }) (captchaVerifyParam) =>
.json() useApiFetch('auth/sendSms')
.post({ mobile: phoneNumber.value, actionType: 'login', captchaVerifyParam })
if (data.value && !error.value) { .json(),
if (data.value.code === 200) { (res) => {
showToast({ message: "获取成功" }); if (res?.code === 200) {
startCountdown() showToast({ message: "获取成功" });
// 聚焦到验证码输入框 startCountdown();
nextTick(() => { nextTick(() => {
const verificationCodeInput = document.getElementById('verificationCode'); const el = document.getElementById('verificationCode');
if (verificationCodeInput) { if (el) el.focus();
verificationCodeInput.focus(); });
} } else {
}); showToast(res?.msg || "获取失败");
} else { }
showToast(data.value.msg)
} }
} );
} }
function startCountdown() { function startCountdown() {
@@ -95,31 +96,17 @@ async function handleLogin() {
showToast({ message: "请先同意用户协议" }); showToast({ message: "请先同意用户协议" });
return return
} }
// 直接执行登录逻辑
await performLogin()
}
// 执行实际的登录逻辑
async function performLogin() {
const { data, error } = await useApiFetch('/user/mobileCodeLogin') const { data, error } = await useApiFetch('/user/mobileCodeLogin')
.post({ mobile: phoneNumber.value, code: verificationCode.value }) .post({ mobile: phoneNumber.value, code: verificationCode.value })
.json() .json();
if (data.value && !error.value && data.value.code === 200 && data.value.data) {
if (data.value && !error.value) { localStorage.setItem('token', data.value.data.accessToken);
if (data.value.code === 200) { localStorage.setItem('refreshAfter', data.value.data.refreshAfter);
localStorage.setItem('token', data.value.data.accessToken) localStorage.setItem('accessExpire', data.value.data.accessExpire);
localStorage.setItem('refreshAfter', data.value.data.refreshAfter) await userStore.fetchUserInfo();
localStorage.setItem('accessExpire', data.value.data.accessExpire) showToast({ message: "登录成功" });
closeDialog();
// 更新用户信息 emit('login-success');
await userStore.fetchUserInfo()
showToast({ message: "登录成功" });
closeDialog();
emit('login-success');
} else {
showToast(data.value.msg);
}
} }
} }

View File

@@ -0,0 +1,170 @@
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 };
}

View File

@@ -1,8 +1,10 @@
<script setup> <script setup>
import { ref, computed, onUnmounted, nextTick } from 'vue' import { ref, computed, onUnmounted, nextTick } from 'vue'
import { showToast } from 'vant' import { showToast } from 'vant'
import { useAliyunCaptcha } from '@/composables/useAliyunCaptcha'
const router = useRouter() const router = useRouter()
const { runWithCaptcha } = useAliyunCaptcha()
const phoneNumber = ref('') const phoneNumber = ref('')
const verificationCode = ref('') const verificationCode = ref('')
const password = ref('') const password = ref('')
@@ -30,31 +32,30 @@ const canLogin = computed(() => {
} }
}) })
async function sendVerificationCode() { 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: 'login' }) (captchaVerifyParam) =>
.json() useApiFetch('auth/sendSms')
.post({ mobile: phoneNumber.value, actionType: 'login', captchaVerifyParam })
if (data.value && !error.value) { .json(),
if (data.value.code === 200) { (res) => {
showToast({ message: "获取成功" }); if (res?.code === 200) {
startCountdown() showToast({ message: "获取成功" });
// 聚焦到验证码输入框 startCountdown();
nextTick(() => { nextTick(() => {
const verificationCodeInput = document.getElementById('verificationCode'); const el = document.getElementById('verificationCode');
if (verificationCodeInput) { if (el) el.focus();
verificationCodeInput.focus(); });
} } else {
}); showToast(res?.msg || "获取失败");
} else { }
showToast(data.value.msg)
} }
} );
} }
function startCountdown() { function startCountdown() {
@@ -90,25 +91,17 @@ async function handleLogin() {
showToast({ message: "请先同意用户协议" }); showToast({ message: "请先同意用户协议" });
return return
} }
// 直接执行登录逻辑
await performLogin()
}
// 执行实际的登录逻辑
async function performLogin() {
const { data, error } = await useApiFetch('/user/mobileCodeLogin') const { data, error } = await useApiFetch('/user/mobileCodeLogin')
.post({ mobile: phoneNumber.value, code: verificationCode.value }) .post({ mobile: phoneNumber.value, code: verificationCode.value })
.json() .json();
if (data.value && !error.value && data.value.code === 200 && data.value.data) {
if (data.value && !error.value) { localStorage.setItem('token', data.value.data.accessToken);
if (data.value.code === 200) { localStorage.setItem('refreshAfter', data.value.data.refreshAfter);
localStorage.setItem('token', data.value.data.accessToken) localStorage.setItem('accessExpire', data.value.data.accessExpire);
localStorage.setItem('refreshAfter', data.value.data.refreshAfter) window.location.href = '/';
localStorage.setItem('accessExpire', data.value.data.accessExpire)
window.location.href = '/'
}
} }
} }
function toUserAgreement() { function toUserAgreement() {
router.push(`/userAgreement`) router.push(`/userAgreement`)
} }