305 lines
11 KiB
Vue
305 lines
11 KiB
Vue
|
<script setup>
|
|||
|
import { ref, reactive, computed, onMounted, onUnmounted } from "vue";
|
|||
|
import { aesEncrypt } from '@/utils/crypto'
|
|||
|
import { useRoute } from "vue-router";
|
|||
|
import { useWebView } from "@/composables/useWebView";
|
|||
|
import Authorization from "@/components/Authorization.vue";
|
|||
|
import Payment from "@/components/Payment.vue";
|
|||
|
|
|||
|
const route = useRoute();
|
|||
|
useWebView()
|
|||
|
|
|||
|
const showAuthorizationPopup = ref(false)
|
|||
|
const authorization = ref(false)
|
|||
|
const showPayment = ref(false)
|
|||
|
const queryId = ref(null)
|
|||
|
const name = ref("");
|
|||
|
const idCard = ref("");
|
|||
|
const mobile = ref("");
|
|||
|
const entName = ref("");
|
|||
|
const entCode = ref("");
|
|||
|
const verificationCode = ref("");
|
|||
|
const agreeToTerms = ref(false);
|
|||
|
const isCountingDown = ref(false);
|
|||
|
const countdown = ref(60);
|
|||
|
const feature = ref(route.params.feature);
|
|||
|
const featureData = ref({});
|
|||
|
|
|||
|
onMounted(() => {
|
|||
|
getProduct()
|
|||
|
initAuthorization()
|
|||
|
});
|
|||
|
async function getProduct() {
|
|||
|
const { data, error } = await useApiFetch(`/product/en/${feature.value}`)
|
|||
|
.get()
|
|||
|
.json();
|
|||
|
|
|||
|
if (data.value) {
|
|||
|
featureData.value = data.value.data;
|
|||
|
}
|
|||
|
}
|
|||
|
function initAuthorization() {
|
|||
|
if (noAuthorization.includes(feature.value)) {
|
|||
|
authorization.value = true
|
|||
|
}
|
|||
|
}
|
|||
|
const isPhoneNumberValid = computed(() => {
|
|||
|
return /^1[3-9]\d{9}$/.test(mobile.value);
|
|||
|
});
|
|||
|
const isIdCardValid = computed(() => /^\d{17}[\dX]$/i.test(idCard.value));
|
|||
|
const isCreditCodeValid = computed(() => /^.{18}$/.test(entCode.value));
|
|||
|
|
|||
|
|
|||
|
function handleSubmit() {
|
|||
|
if (!agreeToTerms.value) {
|
|||
|
showToast({ message: "请阅读并同意用户协议和隐私政策" });
|
|||
|
return;
|
|||
|
}
|
|||
|
if (
|
|||
|
!validateField('name', name.value, v => v, '请输入姓名') ||
|
|||
|
!validateField('mobile', mobile.value, v => isPhoneNumberValid.value, '请输入有效的手机号') ||
|
|||
|
!validateField('idCard', idCard.value, v => isIdCardValid.value, '请输入有效的身份证号码') ||
|
|||
|
!validateField('verificationCode', verificationCode.value, v => v, '请输入验证码') ||
|
|||
|
!validateField('entName', entName.value, v => v, '请输入企业名称') ||
|
|||
|
!validateField('entCode', entCode.value, v => isCreditCodeValid.value, '请输入统一社会信用代码')
|
|||
|
) {
|
|||
|
return;
|
|||
|
}
|
|||
|
submitRequest()
|
|||
|
}
|
|||
|
const validateField = (field, value, validationFn, errorMessage) => {
|
|||
|
if (isHasInput(field) && !validationFn(value)) {
|
|||
|
showToast({ message: errorMessage });
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
const defaultInput = ["name", "mobile", "idCard", "verificationCode"]
|
|||
|
const specialProduct = {
|
|||
|
"toc_EnterpriseLawsuit": ["entName", "entCode", "mobile", "verificationCode"]
|
|||
|
}
|
|||
|
const noAuthorization = ["toc_EnterpriseLawsuit"]
|
|||
|
const isHasInput = (input) => {
|
|||
|
if (specialProduct[feature.value]) {
|
|||
|
return specialProduct[feature.value].includes(input)
|
|||
|
} else {
|
|||
|
return defaultInput.includes(input)
|
|||
|
}
|
|||
|
}
|
|||
|
async function submitRequest() {
|
|||
|
const req = {}
|
|||
|
if (isHasInput('name')) {
|
|||
|
req.name = name.value
|
|||
|
}
|
|||
|
if (isHasInput('id_card')) {
|
|||
|
req.id_card = idCard.value
|
|||
|
}
|
|||
|
if (isHasInput('mobile')) {
|
|||
|
req.mobile = mobile.value
|
|||
|
}
|
|||
|
if (isHasInput('verificationCode')) {
|
|||
|
req.code = verificationCode.value
|
|||
|
}
|
|||
|
if (isHasInput('entName')) {
|
|||
|
req.ent_name = entName.value
|
|||
|
}
|
|||
|
if (isHasInput('entCode')) {
|
|||
|
req.ent_code = entCode.value
|
|||
|
}
|
|||
|
const reqStr = JSON.stringify(req)
|
|||
|
const encodeData = aesEncrypt(reqStr, 'ff83609b2b24fc73196aac3d3dfb874f')
|
|||
|
const { data, error } = await useApiFetch(`/query/service/${feature.value}`)
|
|||
|
.post({ data: encodeData })
|
|||
|
.json();
|
|||
|
if (data.value.code === 200) {
|
|||
|
queryId.value = data.value.data.id
|
|||
|
if (authorization.value) {
|
|||
|
showPayment.value = true
|
|||
|
} else {
|
|||
|
showAuthorizationPopup.value = true
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
async function sendVerificationCode() {
|
|||
|
if (isCountingDown.value || !isPhoneNumberValid.value)
|
|||
|
return
|
|||
|
if (!isPhoneNumberValid.value) {
|
|||
|
showToast({ message: "请输入有效的手机号" });
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
const { data, error } = await useApiFetch("/auth/sendSms")
|
|||
|
.post({ mobile: mobile.value, actionType: "query" })
|
|||
|
.json();
|
|||
|
|
|||
|
if (!error.value && data.value.code === 200) {
|
|||
|
showToast({ message: "验证码发送成功", type: "success" });
|
|||
|
startCountdown();
|
|||
|
} else {
|
|||
|
showToast({ message: "验证码发送失败,请重试" });
|
|||
|
}
|
|||
|
}
|
|||
|
let timer = null;
|
|||
|
|
|||
|
function startCountdown() {
|
|||
|
isCountingDown.value = true;
|
|||
|
countdown.value = 60;
|
|||
|
timer = setInterval(() => {
|
|||
|
if (countdown.value > 0) {
|
|||
|
countdown.value--;
|
|||
|
} else {
|
|||
|
clearInterval(timer);
|
|||
|
isCountingDown.value = false;
|
|||
|
}
|
|||
|
}, 1000);
|
|||
|
}
|
|||
|
function toUserAgreement() {
|
|||
|
uni.navigateTo({
|
|||
|
url: '/pages/userAgreement'
|
|||
|
})
|
|||
|
}
|
|||
|
function toPrivacyPolicy() {
|
|||
|
uni.navigateTo({
|
|||
|
url: '/pages/privacyPolicy'
|
|||
|
})
|
|||
|
}
|
|||
|
// 用户同意
|
|||
|
const agreed = () => {
|
|||
|
showAuthorizationPopup.value = false
|
|||
|
authorization.value = true
|
|||
|
showPayment.value = true
|
|||
|
};
|
|||
|
|
|||
|
// 用户取消
|
|||
|
const cancel = () => {
|
|||
|
showAuthorizationPopup.value = false
|
|||
|
};
|
|||
|
const toExample = () => {
|
|||
|
uni.navigateTo({
|
|||
|
url: '/pages/example'
|
|||
|
})
|
|||
|
};
|
|||
|
onUnmounted(() => {
|
|||
|
if (timer) {
|
|||
|
clearInterval(timer);
|
|||
|
}
|
|||
|
});
|
|||
|
</script>
|
|||
|
|
|||
|
<template>
|
|||
|
<div class="inquire-bg min-h-screen p-6">
|
|||
|
<div class="mb-6 text-center text-3xl font-bold text-blue-700">
|
|||
|
{{ featureData.product_name }}
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="card">
|
|||
|
<div class="mb-4 text-lg font-semibold text-gray-800">基本信息</div>
|
|||
|
<div class="mb-4 flex items-center" v-if="isHasInput('name')">
|
|||
|
<label for="name" class="form-label">姓名</label>
|
|||
|
<input v-model="name" id="name" type="text" placeholder="请输入姓名" class="form-input" />
|
|||
|
</div>
|
|||
|
<div class="mb-4 flex items-center" v-if="isHasInput('idCard')">
|
|||
|
<label for="idCard" class="form-label">身份证号</label>
|
|||
|
<input v-model="idCard" id="idCard" type="text" placeholder="请输入身份证号" class="form-input" />
|
|||
|
</div>
|
|||
|
<div class="mb-4 flex items-center" v-if="isHasInput('entName')">
|
|||
|
<label for="entName" class="form-label">企业名称</label>
|
|||
|
<input v-model="entName" id="entName" type="text" placeholder="请输入企业名称" class="form-input" />
|
|||
|
</div>
|
|||
|
<div class="mb-4 flex items-center" v-if="isHasInput('entCode')">
|
|||
|
<label for="entCode" class="form-label">统一社会信用代码</label>
|
|||
|
<input v-model="entCode" id="entCode" type="text" placeholder="请输入统一社会信用代码" class="form-input" />
|
|||
|
</div>
|
|||
|
<div class="mb-4 flex items-center" v-if="isHasInput('mobile')">
|
|||
|
<label for="mobile" class="form-label">手机号</label>
|
|||
|
<input v-model="mobile" id="mobile" type="tel" placeholder="请输入手机号" class="form-input" />
|
|||
|
</div>
|
|||
|
<div class="mb-4 flex items-center" v-if="isHasInput('verificationCode')">
|
|||
|
<label for="verificationCode" class="form-label">验证码</label>
|
|||
|
<div class="flex-1 flex items-center">
|
|||
|
<input v-model="verificationCode" id="verificationCode" type="text" placeholder="请输入验证码"
|
|||
|
class="form-input flex-1" />
|
|||
|
<button class="ml-2 px-4 py-2 text-sm text-blue-500 disabled:text-gray-400"
|
|||
|
:disabled="isCountingDown || !isPhoneNumberValid" @click="sendVerificationCode">
|
|||
|
{{ isCountingDown ? `${countdown}s重新获取` : '获取验证码' }}
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="mb-4 flex items-center">
|
|||
|
<input type="checkbox" v-model="agreeToTerms" />
|
|||
|
<span class="ml-2 text-xs text-gray-400">
|
|||
|
我已阅读并同意
|
|||
|
<span @click="toUserAgreement" class="text-blue-500 underline">用户协议</span>和
|
|||
|
<span @click="toPrivacyPolicy" class="text-blue-500 underline">隐私政策</span>
|
|||
|
</span>
|
|||
|
</div>
|
|||
|
<div class="flex items-center">
|
|||
|
<button class="w-24 rounded-l-xl bg-blue-400 py-2 text-white" @click="toExample">
|
|||
|
示例报告
|
|||
|
</button>
|
|||
|
<button class="flex-1 rounded-r-xl bg-blue-500 py-2 text-white" @click="handleSubmit">
|
|||
|
立即查询
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="card mt-4">
|
|||
|
<div class="mb-6 text-xl text-gray-800 font-bold">
|
|||
|
{{ featureData.product_name }}
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="mb-4 text-gray-600 leading-relaxed">
|
|||
|
{{ featureData.description }}
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="mb-6 flex items-center justify-between">
|
|||
|
<div class="text-lg text-gray-500">
|
|||
|
价格:
|
|||
|
</div>
|
|||
|
<div class="text-lg text-blue-600 font-semibold">
|
|||
|
¥{{ featureData.sell_price }}
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="mb-4 text-lg text-gray-800 font-semibold">
|
|||
|
报告主要内容
|
|||
|
</div>
|
|||
|
<div class="grid grid-cols-2 gap-4">
|
|||
|
<div v-for="(feature, index) in featureData.features" :key="feature.id"
|
|||
|
class="rounded-lg py-2 text-center text-sm text-gray-700 font-medium" :class="[
|
|||
|
(Math.floor(index / 2) + (index % 2)) % 2 === 0
|
|||
|
? 'bg-gradient-to-r from-blue-200 via-blue-200 to-blue-100'
|
|||
|
: 'bg-gradient-to-r from-sky-200 via-sky-200 to-sky-100',
|
|||
|
]">
|
|||
|
{{ feature.name }}
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<!-- 底部弹出 -->
|
|||
|
<van-popup v-model:show="showAuthorizationPopup" position="bottom" :style="{ height: '80%' }">
|
|||
|
<Authorization :style="{ height: '100%' }" :name="name" :id-card="idCard" :mobile="mobile" @agreed="agreed"
|
|||
|
@cancel="cancel" />
|
|||
|
</van-popup>
|
|||
|
<Payment v-model="showPayment" :data="featureData" :id="queryId" @close="showPayment = false" />
|
|||
|
</template>
|
|||
|
|
|||
|
<style scoped>
|
|||
|
.form-label {
|
|||
|
@apply w-20 text-sm font-medium text-gray-700 flex-shrink-0;
|
|||
|
}
|
|||
|
|
|||
|
.form-input {
|
|||
|
@apply w-full border-b border-gray-200 px-2 py-2 focus:outline-none;
|
|||
|
}
|
|||
|
|
|||
|
.inquire-bg {
|
|||
|
background: url("@/assets/images/inquire_banner_2.png") no-repeat;
|
|||
|
background-position: center;
|
|||
|
background-size: cover;
|
|||
|
}
|
|||
|
</style>
|