f
This commit is contained in:
@@ -187,50 +187,89 @@ watch(
|
||||
|
||||
<template>
|
||||
<wd-popup v-model="show" destroy-on-close round position="bottom">
|
||||
<view class="h-12 flex items-center justify-center font-semibold"
|
||||
style="background-color: var(--van-theme-primary-light); color: var(--van-theme-primary);">
|
||||
<view
|
||||
class="h-12 flex items-center justify-center font-semibold"
|
||||
style="background-color: var(--color-primary-light); color: var(--color-primary);"
|
||||
>
|
||||
成为代理
|
||||
</view>
|
||||
<view v-if="ancestor" class="my-2 text-center text-xs" style="color: var(--van-text-color-2);">
|
||||
<view v-if="ancestor" class="my-2 text-center text-xs" style="color: var(--color-text-secondary);">
|
||||
{{ maskName(ancestor) }}邀您成为赤眉代理方
|
||||
</view>
|
||||
<view class="p-4">
|
||||
<wd-col-picker v-model="region" class="agent-form-field" label-width="42px" label="地区" placeholder="请选择地区"
|
||||
:columns="columns" :column-change="handleColumnChange" :display-format="displayFormat" :align-right="false"
|
||||
custom-value-class="agent-col-picker-value" @confirm="handleRegionConfirm" />
|
||||
<wd-input v-model="form.mobile" class="agent-form-field" label-width="42px" label="手机号" name="mobile"
|
||||
placeholder="请输入手机号" :align-right="false" :readonly="mobileReadonly" :disabled="mobileReadonly" />
|
||||
<wd-col-picker
|
||||
v-model="region"
|
||||
class="agent-form-field"
|
||||
label-width="42px"
|
||||
label="地区"
|
||||
placeholder="请选择地区"
|
||||
:columns="columns"
|
||||
:column-change="handleColumnChange"
|
||||
:display-format="displayFormat"
|
||||
:align-right="false"
|
||||
custom-value-class="agent-col-picker-value"
|
||||
@confirm="handleRegionConfirm"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="form.mobile"
|
||||
class="agent-form-field"
|
||||
label-width="42px"
|
||||
label="手机号"
|
||||
name="mobile"
|
||||
placeholder="请输入手机号"
|
||||
:align-right="false"
|
||||
:readonly="mobileReadonly"
|
||||
:disabled="mobileReadonly"
|
||||
/>
|
||||
|
||||
<!-- 获取验证码按钮 -->
|
||||
<wd-input v-model="form.code" class="agent-form-field" label-width="42px" label="验证码" name="code"
|
||||
placeholder="请输入验证码" :align-right="false" use-suffix-slot>
|
||||
<wd-input
|
||||
v-model="form.code"
|
||||
class="agent-form-field"
|
||||
label-width="42px"
|
||||
label="验证码"
|
||||
name="code"
|
||||
placeholder="请输入验证码"
|
||||
:align-right="false"
|
||||
use-suffix-slot
|
||||
>
|
||||
<template #suffix>
|
||||
<button class="ml-2 flex-shrink-0 rounded-lg px-2 py-1 text-sm font-bold transition duration-300" :class="isCountingDown || !isPhoneNumberValid
|
||||
? 'cursor-not-allowed bg-gray-300 text-gray-500'
|
||||
: 'text-white hover:opacity-90'" :style="isCountingDown || !isPhoneNumberValid
|
||||
? ''
|
||||
: 'background-color: var(--van-theme-primary);'" :disabled="isCountingDown || !isPhoneNumberValid"
|
||||
@click.stop="getSmsCode">
|
||||
{{
|
||||
isCountingDown ? `${countdown}s重新获取` : '获取验证码'
|
||||
}}
|
||||
</button>
|
||||
<wd-button
|
||||
class="ml-2"
|
||||
size="small"
|
||||
type="primary"
|
||||
plain
|
||||
:disabled="isCountingDown || !isPhoneNumberValid"
|
||||
@click.stop="getSmsCode"
|
||||
>
|
||||
{{ isCountingDown ? `${countdown}s重新获取` : '获取验证码' }}
|
||||
</wd-button>
|
||||
</template>
|
||||
</wd-input>
|
||||
<!-- 同意条款的复选框 -->
|
||||
<view class="p-4">
|
||||
<view class="flex items-start">
|
||||
<wd-checkbox v-model="isAgreed" name="agree" icon-size="16px" class="mr-2 flex-shrink-0" />
|
||||
<view class="text-xs leading-tight" style="color: var(--van-text-color-2);">
|
||||
<view class="text-xs leading-tight" style="color: var(--color-text-secondary);">
|
||||
我已阅读并同意
|
||||
<a class="cursor-pointer hover:underline" style="color: var(--van-theme-primary);"
|
||||
@click="toUserAgreement">《用户协议》</a>
|
||||
<a class="cursor-pointer hover:underline" style="color: var(--van-theme-primary);"
|
||||
@click="toAgentManageAgreement">《推广方管理制度协议》</a>
|
||||
<view class="mt-1 text-xs" style="color: var(--van-text-color-2);">
|
||||
<text
|
||||
class="cursor-pointer hover:underline"
|
||||
style="color: var(--color-primary);"
|
||||
@click="toUserAgreement"
|
||||
>
|
||||
《用户协议》
|
||||
</text>
|
||||
<text
|
||||
class="cursor-pointer hover:underline"
|
||||
style="color: var(--color-primary);"
|
||||
@click="toAgentManageAgreement"
|
||||
>
|
||||
《推广方管理制度协议》
|
||||
</text>
|
||||
<view class="mt-1 text-xs" style="color: var(--color-text-secondary);">
|
||||
点击勾选即代表您同意上述法律文书的相关条款并签署上述法律文书
|
||||
</view>
|
||||
<view class="mt-1 text-xs" style="color: var(--van-text-color-2);">
|
||||
<view class="mt-1 text-xs" style="color: var(--color-text-secondary);">
|
||||
手机号未在本平台注册账号则申请后将自动生成账号
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, ref } from 'vue'
|
||||
import { computed, nextTick, onUnmounted, ref } from 'vue'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
import { setAuthSession } from '@/utils/storage'
|
||||
|
||||
@@ -25,10 +25,6 @@ function showToast(options) {
|
||||
})
|
||||
}
|
||||
|
||||
// 聚焦状态变量
|
||||
const phoneFocused = ref(false)
|
||||
const codeFocused = ref(false)
|
||||
|
||||
const isPhoneNumberValid = computed(() => {
|
||||
return /^1[3-9]\d{9}$/.test(phoneNumber.value)
|
||||
})
|
||||
@@ -120,8 +116,11 @@ function closeDialog() {
|
||||
phoneNumber.value = ''
|
||||
verificationCode.value = ''
|
||||
isAgreed.value = false
|
||||
isCountingDown.value = false
|
||||
countdown.value = 60
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
timer = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +133,12 @@ function toPrivacyPolicy() {
|
||||
closeDialog()
|
||||
router.push(`/privacyPolicy`)
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -164,56 +169,72 @@ function toPrivacyPolicy() {
|
||||
</view>
|
||||
|
||||
<view class="space-y-5">
|
||||
<!-- 手机号输入 -->
|
||||
<view class="input-container bg-blue-300/20" :class="[
|
||||
phoneFocused ? 'focused' : '',
|
||||
]">
|
||||
<input v-model="phoneNumber" class="input-field" type="tel" placeholder="请输入手机号" maxlength="11"
|
||||
@focus="phoneFocused = true" @blur="phoneFocused = false">
|
||||
<view class="form-item">
|
||||
<text class="form-label">
|
||||
手机号
|
||||
</text>
|
||||
<wd-input
|
||||
v-model="phoneNumber"
|
||||
class="field-wd-input"
|
||||
type="number"
|
||||
placeholder="请输入手机号"
|
||||
maxlength="11"
|
||||
no-border
|
||||
clearable
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 验证码输入 -->
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="input-container bg-blue-300/20" :class="[
|
||||
codeFocused ? 'focused' : '',
|
||||
]">
|
||||
<input id="verificationCode" ref="verificationCodeInputRef" v-model="verificationCode"
|
||||
class="input-field" placeholder="请输入验证码" maxlength="6" @focus="codeFocused = true"
|
||||
@blur="codeFocused = false">
|
||||
</view>
|
||||
<button class="ml-2 flex-shrink-0 rounded-lg px-4 py-2 text-sm font-bold transition duration-300" :class="isCountingDown || !isPhoneNumberValid
|
||||
? 'cursor-not-allowed bg-gray-300 text-gray-500'
|
||||
: 'bg-blue-500 text-white hover:bg-blue-600'
|
||||
" @click="sendVerificationCode">
|
||||
{{
|
||||
isCountingDown
|
||||
? `${countdown}s重新获取`
|
||||
: "获取验证码"
|
||||
}}
|
||||
</button>
|
||||
<view class="form-item">
|
||||
<text class="form-label">
|
||||
验证码
|
||||
</text>
|
||||
<wd-input
|
||||
ref="verificationCodeInputRef"
|
||||
v-model="verificationCode"
|
||||
class="field-wd-input"
|
||||
placeholder="请输入验证码"
|
||||
maxlength="6"
|
||||
no-border
|
||||
clearable
|
||||
>
|
||||
<template #suffix>
|
||||
<wd-button
|
||||
size="small"
|
||||
type="primary"
|
||||
plain
|
||||
:disabled="isCountingDown || !isPhoneNumberValid"
|
||||
@click="sendVerificationCode"
|
||||
>
|
||||
{{ isCountingDown ? `${countdown}s重新获取` : '获取验证码' }}
|
||||
</wd-button>
|
||||
</template>
|
||||
</wd-input>
|
||||
</view>
|
||||
|
||||
<!-- 协议同意框 -->
|
||||
<view class="flex items-start space-x-2">
|
||||
<input v-model="isAgreed" type="checkbox" class="mt-1">
|
||||
<text class="text-xs text-gray-400 leading-tight">
|
||||
<view class="agreement-wrapper">
|
||||
<wd-checkbox v-model="isAgreed" shape="square" size="18px" />
|
||||
<text class="agreement-text">
|
||||
绑定手机号即代表您已阅读并同意
|
||||
<a class="cursor-pointer text-blue-400" @click="toUserAgreement">
|
||||
<text class="agreement-link" @click="toUserAgreement">
|
||||
《用户协议》
|
||||
</a>
|
||||
</text>
|
||||
和
|
||||
<a class="cursor-pointer text-blue-400" @click="toPrivacyPolicy">
|
||||
<text class="agreement-link" @click="toPrivacyPolicy">
|
||||
《隐私政策》
|
||||
</a>
|
||||
</text>
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button
|
||||
class="mt-10 w-full rounded-full bg-blue-500 py-3 text-lg text-white font-bold transition duration-300"
|
||||
:class="{ 'opacity-50 cursor-not-allowed': !canBind }" @click="handleBind">
|
||||
<wd-button
|
||||
class="mt-10"
|
||||
block
|
||||
type="primary"
|
||||
:disabled="!canBind"
|
||||
@click="handleBind"
|
||||
>
|
||||
确认绑定
|
||||
</button>
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
@@ -242,22 +263,45 @@ function toPrivacyPolicy() {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
border: 2px solid rgba(125, 211, 252, 0);
|
||||
border-radius: 1rem;
|
||||
transition: duration-200;
|
||||
.form-item {
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 12px;
|
||||
background-color: #fff;
|
||||
padding: 0 0.75rem;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.input-container.focused {
|
||||
border: 2px solid #3b82f6;
|
||||
.form-label {
|
||||
font-size: 0.9375rem;
|
||||
color: #111827;
|
||||
margin-right: 0.75rem;
|
||||
font-weight: 500;
|
||||
min-width: 3.25rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
transition: border-color 0.3s ease;
|
||||
.field-wd-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.agreement-wrapper {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.agreement-text {
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
line-height: 1.5;
|
||||
margin-left: 0.5rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.agreement-link {
|
||||
color: #2563eb;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
<script setup>
|
||||
import * as echarts from 'echarts'
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
score: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
// 根据分数计算风险等级和颜色(分数越高越安全)
|
||||
const riskLevel = computed(() => {
|
||||
const score = props.score
|
||||
if (score >= 75 && score <= 100) {
|
||||
return {
|
||||
level: '无任何风险',
|
||||
color: '#52c41a',
|
||||
gradient: [
|
||||
{ offset: 0, color: '#52c41a' },
|
||||
{ offset: 1, color: '#7fdb42' },
|
||||
],
|
||||
}
|
||||
}
|
||||
else if (score >= 50 && score < 75) {
|
||||
return {
|
||||
level: '风险指数较低',
|
||||
color: '#faad14',
|
||||
gradient: [
|
||||
{ offset: 0, color: '#faad14' },
|
||||
{ offset: 1, color: '#ffc53d' },
|
||||
],
|
||||
}
|
||||
}
|
||||
else if (score >= 25 && score < 50) {
|
||||
return {
|
||||
level: '风险指数较高',
|
||||
color: '#fa8c16',
|
||||
gradient: [
|
||||
{ offset: 0, color: '#fa8c16' },
|
||||
{ offset: 1, color: '#ffa940' },
|
||||
],
|
||||
}
|
||||
}
|
||||
else {
|
||||
return {
|
||||
level: '高风险警告',
|
||||
color: '#f5222d',
|
||||
gradient: [
|
||||
{ offset: 0, color: '#f5222d' },
|
||||
{ offset: 1, color: '#ff4d4f' },
|
||||
],
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 评分解释文本(分数越高越安全)
|
||||
const riskDescription = computed(() => {
|
||||
const score = props.score
|
||||
if (score >= 75 && score <= 100) {
|
||||
return '根据综合分析,当前报告未检测到明显风险因素,各项指标表现正常,总体状况良好。'
|
||||
}
|
||||
else if (score >= 50 && score < 75) {
|
||||
return '根据综合分析,当前报告存在少量风险信号,建议关注相关指标变化,保持警惕。'
|
||||
}
|
||||
else if (score >= 25 && score < 50) {
|
||||
return '根据综合分析,当前报告风险指数较高,多项指标显示异常,建议进一步核实相关情况。'
|
||||
}
|
||||
else {
|
||||
return '根据综合分析,当前报告显示高度风险状态,多项重要指标严重异常,请立即采取相应措施。'
|
||||
}
|
||||
})
|
||||
|
||||
const chartRef = ref(null)
|
||||
let chartInstance = null
|
||||
|
||||
function initChart() {
|
||||
if (!chartRef.value)
|
||||
return
|
||||
|
||||
// 初始化ECharts实例
|
||||
chartInstance = echarts.init(chartRef.value)
|
||||
updateChart()
|
||||
}
|
||||
|
||||
function updateChart() {
|
||||
if (!chartInstance)
|
||||
return
|
||||
|
||||
// 获取当前风险等级信息
|
||||
const risk = riskLevel.value
|
||||
|
||||
// 配置项
|
||||
const option = {
|
||||
series: [
|
||||
{
|
||||
type: 'gauge',
|
||||
startAngle: 180,
|
||||
endAngle: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
radius: '100%',
|
||||
center: ['50%', '80%'],
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, risk.gradient),
|
||||
shadowBlur: 6,
|
||||
shadowColor: risk.color,
|
||||
},
|
||||
progress: {
|
||||
show: true,
|
||||
width: 20,
|
||||
roundCap: true,
|
||||
clip: false,
|
||||
},
|
||||
axisLine: {
|
||||
roundCap: true,
|
||||
lineStyle: {
|
||||
width: 20,
|
||||
color: [
|
||||
[1, new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: `${risk.color}30`, // 使用风险颜色,透明度20%
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: `${risk.color}25`, // 使用风险颜色,透明度10%
|
||||
},
|
||||
])],
|
||||
],
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: true,
|
||||
distance: -30,
|
||||
length: 6,
|
||||
splitNumber: 10, // 每1分一个小刻度
|
||||
lineStyle: {
|
||||
color: risk.color,
|
||||
width: 1,
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
distance: -36,
|
||||
length: 12,
|
||||
splitNumber: 9, // 9个大刻度,100分分成9个区间
|
||||
lineStyle: {
|
||||
color: risk.color,
|
||||
width: 2,
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
anchor: {
|
||||
show: false,
|
||||
},
|
||||
pointer: {
|
||||
icon: 'triangle',
|
||||
iconStyle: {
|
||||
color: risk.color,
|
||||
borderColor: risk.color,
|
||||
borderWidth: 1,
|
||||
},
|
||||
offsetCenter: ['7%', '-67%'],
|
||||
length: '10%',
|
||||
width: 15,
|
||||
},
|
||||
detail: {
|
||||
valueAnimation: true,
|
||||
fontSize: 30,
|
||||
fontWeight: 'bold',
|
||||
color: risk.color,
|
||||
offsetCenter: [0, '-25%'],
|
||||
formatter(value) {
|
||||
return `{value|${value}分}\n{level|${risk.level}}`
|
||||
},
|
||||
rich: {
|
||||
value: {
|
||||
fontSize: 30,
|
||||
fontWeight: 'bold',
|
||||
color: risk.color,
|
||||
padding: [0, 0, 5, 0],
|
||||
},
|
||||
level: {
|
||||
fontSize: 14,
|
||||
fontWeight: 'normal',
|
||||
color: risk.color,
|
||||
padding: [5, 0, 0, 0],
|
||||
},
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: props.score,
|
||||
},
|
||||
],
|
||||
title: {
|
||||
fontSize: 14,
|
||||
color: risk.color,
|
||||
offsetCenter: [0, '10%'],
|
||||
formatter: risk.level,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// 使用配置项设置图表
|
||||
chartInstance.setOption(option)
|
||||
}
|
||||
|
||||
// 监听分数变化
|
||||
watch(
|
||||
() => props.score,
|
||||
() => {
|
||||
updateChart()
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
|
||||
// 处理窗口大小变化
|
||||
window.addEventListener('resize', () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 在组件销毁前清理
|
||||
onUnmounted(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose()
|
||||
chartInstance = null
|
||||
}
|
||||
window.removeEventListener('resize', chartInstance?.resize)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<view class="risk-description">
|
||||
{{ riskDescription }}
|
||||
</view>
|
||||
<view ref="chartRef" :style="{ width: '100%', height: '200px' }" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.risk-description {
|
||||
margin-bottom: 4px;
|
||||
padding: 0 12px;
|
||||
color: #666666;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@@ -36,9 +34,11 @@ function close() {
|
||||
<view v-if="show" class="image-save-guide-overlay">
|
||||
<view class="guide-content" @click.stop>
|
||||
<!-- 关闭按钮 -->
|
||||
<button class="close-button" @click="close">
|
||||
<text class="close-icon">×</text>
|
||||
</button>
|
||||
<wd-button class="close-button" custom-class="image-guide-close-btn" plain @click="close">
|
||||
<text class="close-icon">
|
||||
×
|
||||
</text>
|
||||
</wd-button>
|
||||
|
||||
<!-- 图片区域 -->
|
||||
<view v-if="imageUrl" class="image-container">
|
||||
@@ -97,7 +97,6 @@ function close() {
|
||||
right: 12px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
@@ -108,7 +107,7 @@ function close() {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
:deep(.image-guide-close-btn.wd-button:hover) {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import LoginDialog from '@/components/LoginDialog.vue'
|
||||
import Payment from '@/components/Payment.vue'
|
||||
import SectionTitle from '@/components/SectionTitle.vue'
|
||||
import { useRouter } from '@/composables/uni-router'
|
||||
import { useEnv } from '@/composables/useEnv'
|
||||
import { useAppConfig } from '@/composables/useAppConfig'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
import { useUserStore } from '@/stores/userStore'
|
||||
import { aesEncrypt } from '@/utils/crypto'
|
||||
@@ -69,7 +69,8 @@ function loadProductBackground(productType) {
|
||||
const router = useRouter()
|
||||
const dialogStore = useDialogStore()
|
||||
const userStore = useUserStore()
|
||||
const { isWeChat } = useEnv()
|
||||
const isWeChat = ref(false)
|
||||
const { appConfig, loadAppConfig } = useAppConfig()
|
||||
|
||||
// 响应式数据
|
||||
const showPayment = ref(false)
|
||||
@@ -111,6 +112,7 @@ const buttonText = computed(() => {
|
||||
})
|
||||
|
||||
const hasHeroImage = computed(() => Boolean(productBackground.value))
|
||||
const queryRetentionDaysText = computed(() => `${appConfig.value.query.retention_days}天`)
|
||||
|
||||
function loadTrapezoidBackground() {
|
||||
trapezoidBgImage.value = TRAPEZOID_BACKGROUND_MAP[props.feature] || TRAPEZOID_BACKGROUND_MAP.default
|
||||
@@ -380,6 +382,7 @@ function toHistory() {
|
||||
onMounted(async () => {
|
||||
loadBackgroundImage()
|
||||
loadTrapezoidBackground()
|
||||
await loadAppConfig()
|
||||
})
|
||||
|
||||
// 加载背景图片
|
||||
@@ -427,37 +430,76 @@ watch(feature, async () => {
|
||||
<!-- 表单输入区域 -->
|
||||
<view class="mb-6 space-y-4">
|
||||
<view class="flex items-center border-b border-gray-100 py-3">
|
||||
<text for="name" class="w-20 text-gray-700 font-medium">
|
||||
<text class="w-20 text-gray-700 font-medium">
|
||||
姓名
|
||||
</text>
|
||||
<input id="name" v-model="formData.name" type="text" placeholder="请输入正确的姓名"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick">
|
||||
<wd-input
|
||||
v-model="formData.name"
|
||||
class="inquire-wd-input flex-1"
|
||||
type="text"
|
||||
placeholder="请输入正确的姓名"
|
||||
no-border
|
||||
clearable
|
||||
@focus="handleInputClick"
|
||||
/>
|
||||
</view>
|
||||
<view class="flex items-center border-b border-gray-100 py-3">
|
||||
<text for="idCard" class="w-20 text-gray-700 font-medium">
|
||||
<text class="w-20 text-gray-700 font-medium">
|
||||
身份证号
|
||||
</text>
|
||||
<input id="idCard" v-model="formData.idCard" type="text" placeholder="请输入准确的身份证号"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick">
|
||||
<wd-input
|
||||
v-model="formData.idCard"
|
||||
class="inquire-wd-input flex-1"
|
||||
type="text"
|
||||
placeholder="请输入准确的身份证号"
|
||||
no-border
|
||||
clearable
|
||||
@focus="handleInputClick"
|
||||
/>
|
||||
</view>
|
||||
<view class="flex items-center border-b border-gray-100 py-3">
|
||||
<text for="mobile" class="w-20 text-gray-700 font-medium">
|
||||
<text class="w-20 text-gray-700 font-medium">
|
||||
手机号
|
||||
</text>
|
||||
<input id="mobile" v-model="formData.mobile" type="tel" placeholder="请输入手机号"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick">
|
||||
<wd-input
|
||||
v-model="formData.mobile"
|
||||
class="inquire-wd-input flex-1"
|
||||
type="number"
|
||||
placeholder="请输入手机号"
|
||||
maxlength="11"
|
||||
no-border
|
||||
clearable
|
||||
@focus="handleInputClick"
|
||||
/>
|
||||
</view>
|
||||
<!-- 小微企业(companyinfo)暂不展示验证码 -->
|
||||
<view v-if="needVerificationCode" class="flex items-center border-b border-gray-100 py-3">
|
||||
<text for="verificationCode" class="w-20 text-gray-700 font-medium">
|
||||
<text class="w-20 text-gray-700 font-medium">
|
||||
验证码
|
||||
</text>
|
||||
<input id="verificationCode" ref="verificationCodeInputRef" v-model="formData.verificationCode"
|
||||
placeholder="请输入验证码" maxlength="6" class="flex-1 border-none outline-none" @click="handleInputClick">
|
||||
<wd-button class="captcha-wd-btn" size="small" type="primary" plain
|
||||
:disabled="isCountingDown || !isPhoneNumberValid" @click="sendVerificationCode">
|
||||
{{ isCountingDown ? `${countdown}s` : '获取验证码' }}
|
||||
</wd-button>
|
||||
<wd-input
|
||||
ref="verificationCodeInputRef"
|
||||
v-model="formData.verificationCode"
|
||||
class="inquire-wd-input flex-1"
|
||||
placeholder="请输入验证码"
|
||||
maxlength="6"
|
||||
no-border
|
||||
clearable
|
||||
@focus="handleInputClick"
|
||||
>
|
||||
<template #suffix>
|
||||
<wd-button
|
||||
class="captcha-wd-btn"
|
||||
size="small"
|
||||
type="primary"
|
||||
plain
|
||||
:disabled="isCountingDown || !isPhoneNumberValid"
|
||||
@click="sendVerificationCode"
|
||||
>
|
||||
{{ isCountingDown ? `${countdown}s` : '获取验证码' }}
|
||||
</wd-button>
|
||||
</template>
|
||||
</wd-input>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -481,27 +523,27 @@ watch(feature, async () => {
|
||||
</view>
|
||||
|
||||
<!-- 查询按钮 -->
|
||||
<button
|
||||
class="bg-primary mb-4 mt-10 w-full flex items-center justify-center rounded-[48px] py-4 text-lg text-white font-medium"
|
||||
@click="handleSubmit">
|
||||
<text>{{ buttonText }}</text>
|
||||
<text class="ml-4">
|
||||
¥{{ featureData.sell_price }}
|
||||
</text>
|
||||
</button>
|
||||
<wd-button class="submit-wd-btn mb-4 mt-10" block type="primary" @click="handleSubmit">
|
||||
<view class="w-full flex items-center justify-center">
|
||||
<text>{{ buttonText }}</text>
|
||||
<text class="ml-4">
|
||||
¥{{ featureData.sell_price }}
|
||||
</text>
|
||||
</view>
|
||||
</wd-button>
|
||||
<!-- <view class="text-xs text-gray-500 leading-relaxed mt-8" v-html="featureData.description">
|
||||
</view> -->
|
||||
<!-- 免责声明 -->
|
||||
<view class="mt-2 text-center text-xs text-gray-500 leading-relaxed">
|
||||
为保证用户的隐私及数据安全,查询结果生成30天后将自动删除
|
||||
为保证用户的隐私及数据安全,查询结果生成{{ queryRetentionDaysText }}后将自动删除
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 报告包含内容 -->
|
||||
<view v-if="featureData.features && featureData.features.length > 0" class="card mt-3">
|
||||
<view class="mb-3 flex items-center text-base font-semibold" style="color: var(--van-text-color);">
|
||||
<view class="mb-3 flex items-center text-base font-semibold" style="color: var(--color-text-primary);">
|
||||
<view class="mr-2 h-5 w-1 rounded-full"
|
||||
style="background: linear-gradient(to bottom, var(--van-theme-primary), var(--van-theme-primary-dark));" />
|
||||
style="background: linear-gradient(to bottom, var(--color-primary), var(--color-primary-700));" />
|
||||
报告包含内容
|
||||
</view>
|
||||
<view class="grid grid-cols-4 items-stretch gap-2">
|
||||
@@ -650,9 +692,9 @@ watch(feature, async () => {
|
||||
</view>
|
||||
<view class="mt-3 text-center">
|
||||
<view class="inline-flex items-center border rounded-full px-3 py-1.5 transition-all"
|
||||
style="background: linear-gradient(135deg, var(--van-theme-primary-light), rgba(255,255,255,0.8)); border-color: var(--van-theme-primary);">
|
||||
<view class="mr-1.5 h-1.5 w-1.5 rounded-full" style="background-color: var(--van-theme-primary);" />
|
||||
<text class="text-xs font-medium" style="color: var(--van-theme-primary);">
|
||||
style="background: linear-gradient(135deg, var(--color-primary-light), rgba(255,255,255,0.8)); border-color: var(--color-primary);">
|
||||
<view class="mr-1.5 h-1.5 w-1.5 rounded-full" style="background-color: var(--color-primary);" />
|
||||
<text class="text-xs font-medium" style="color: var(--color-primary);">
|
||||
更多信息请解锁报告
|
||||
</text>
|
||||
</view>
|
||||
@@ -661,11 +703,11 @@ watch(feature, async () => {
|
||||
|
||||
<!-- 产品详情卡片 -->
|
||||
<view class="card mt-4">
|
||||
<view class="mb-4 text-xl font-bold" style="color: var(--van-text-color);">
|
||||
<view class="mb-4 text-xl font-bold" style="color: var(--color-text-primary);">
|
||||
{{ featureData.product_name }}
|
||||
</view>
|
||||
<view class="mb-4 flex items-start justify-between">
|
||||
<view class="text-lg" style="color: var(--van-text-color-2);">
|
||||
<view class="text-lg" style="color: var(--color-text-secondary);">
|
||||
价格:
|
||||
</view>
|
||||
<view>
|
||||
@@ -676,9 +718,9 @@ watch(feature, async () => {
|
||||
</view>
|
||||
<image v-if="productMainImage" :src="productMainImage" alt="产品详情主图" class="mb-4 w-full rounded-lg" />
|
||||
|
||||
<view class="mb-4 leading-relaxed" style="color: var(--van-text-color-2);" v-html="featureData.description" />
|
||||
<view class="mb-4 leading-relaxed" style="color: var(--color-text-secondary);" v-html="featureData.description" />
|
||||
<view class="text-danger mb-2 text-xs italic">
|
||||
为保证用户的隐私以及数据安全,查询的结果生成30天之后将自动清除。
|
||||
为保证用户的隐私以及数据安全,查询的结果生成{{ queryRetentionDaysText }}之后将自动清除。
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -755,15 +797,6 @@ watch(feature, async () => {
|
||||
0 0 0 1px rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
/* 按钮悬停效果 */
|
||||
button:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* 梯形背景图片样式 */
|
||||
.trapezoid-bg-image {
|
||||
background-size: contain;
|
||||
@@ -790,6 +823,15 @@ button:active {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
:deep(.inquire-wd-input .wd-input__inner) {
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
:deep(.submit-wd-btn.wd-button) {
|
||||
border-radius: 48px;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
:deep(.captcha-wd-btn.wd-button) {
|
||||
min-width: 96px;
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
<script setup>
|
||||
// 接收 type 和 options props 以及 v-model
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'purple-pink', // 默认颜色渐变
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
required: true, // 动态传入选项
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '', // v-model 绑定的值
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
// 选中内容绑定 v-model
|
||||
const selected = ref(props.modelValue)
|
||||
|
||||
// 监听 v-model 的变化
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
selected.value = newValue
|
||||
})
|
||||
|
||||
// 根据type动态生成分割线的类名
|
||||
const lineClass = computed(() => {
|
||||
// 统一使用主题色渐变
|
||||
return 'bg-gradient-to-r from-red-600 via-red-500 to-red-700'
|
||||
})
|
||||
|
||||
// 计算滑动线的位置和宽度
|
||||
const slideLineStyle = computed(() => {
|
||||
const index = props.options.findIndex(option => option.value === selected.value)
|
||||
const buttonWidth = 100 / props.options.length
|
||||
return {
|
||||
width: `${buttonWidth}%`,
|
||||
transform: `translateX(${index * 100}%)`,
|
||||
}
|
||||
})
|
||||
|
||||
// 选择选项函数
|
||||
function selectOption(option) {
|
||||
selected.value = option.value
|
||||
// 触发 v-model 的更新
|
||||
emit('update:modelValue', option.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="relative flex">
|
||||
<view
|
||||
v-for="(option, index) in options"
|
||||
:key="index"
|
||||
class="flex-1 shrink-0 cursor-pointer py-2 text-center text-size-sm font-bold transition-transform duration-200 ease-in-out"
|
||||
:class="{ 'text-gray-900': selected === option.value, 'text-gray-500': selected !== option.value }"
|
||||
@click="selectOption(option)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</view>
|
||||
<view
|
||||
class="absolute bottom-0 h-[3px] rounded transition-all duration-300"
|
||||
:style="slideLineStyle"
|
||||
:class="lineClass"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义样式 */
|
||||
button {
|
||||
outline: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:focus {
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,42 +0,0 @@
|
||||
<script setup>
|
||||
const route = useRoute()
|
||||
|
||||
// 返回上一页逻辑
|
||||
function goBack() {
|
||||
route.goBack()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="card flex flex-col items-center justify-center text-center">
|
||||
<!-- 图片插画 -->
|
||||
<image src="/static/images/empty.svg" alt="空状态" class="h-64 w-64" />
|
||||
|
||||
<!-- 提示文字 -->
|
||||
<text class="mb-2 text-xl text-gray-700 font-semibold">
|
||||
没有查询到相关结果
|
||||
</text>
|
||||
<text class="mb-2 text-sm text-gray-500 leading-relaxed">
|
||||
订单已申请退款,预计
|
||||
<text class="font-medium" style="color: var(--van-theme-primary);">24小时内到账</text>。
|
||||
</text>
|
||||
<text class="text-xs text-gray-400">
|
||||
如果已到账,您可以忽略本提示。
|
||||
</text>
|
||||
|
||||
<!-- 返回按钮 -->
|
||||
<button
|
||||
class="mt-4 rounded-lg px-6 py-2 text-white transition duration-300 ease-in-out"
|
||||
style="background-color: var(--van-theme-primary);"
|
||||
onmouseover="this.style.backgroundColor='var(--van-theme-primary-dark)'"
|
||||
onmouseout="this.style.backgroundColor='var(--van-theme-primary)'"
|
||||
@click="goBack"
|
||||
>
|
||||
返回上一页
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 你可以添加一些额外的样式(如果需要) */
|
||||
</style>
|
||||
@@ -1,94 +0,0 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const isExpanded = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="l-remark-card my-[20px]">
|
||||
<!-- 顶部连接点 -->
|
||||
<view class="connection-line">
|
||||
<image
|
||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAACTCAYAAADmz7tVAAAAAXNSR0IArs4c6QAAB59JREFUeF7tXFtsFFUY/s7sbreFUiyFglxaEJUgVaAYNBAjl/hgYrgU44PhRd+Ud3xQE4xP8izx0RfiGwX1xcQYMApegFakSghgWygIFVqope3e5phvzo47O53ZmcM2zT6cSTZsujPnfPP9l/P/w3xHQOM42S/rFxTQhCQ6bAtdkNgIidUSaOYwAhiFwDUI9Fo2upFH30gCY9tXiam404i4J/42KPcXJLosYOucRrTmckChANgFQEo1ihCAlQASCSCVAibGMWwDpxMC3RvaxdE4c0UC6rkhX4aNzyDxlGUhycldAFETECA/to08BK7AwjudK8T3la4LBXTmhmxosHEomcTBfD4+iLDJCCyZBPJ5HJ60cGjLCjEZdG4goL5+uSQrcCSdRlcmE8WD3u/pNJDJoLtO4kDHKnHbf/U0QASTEzglLKyx7fDJHH8pXs3vPFxT2hFmtSxA2ricktjmB1UGiGaqL+CoZaErDAz9N5cH7o0DQyPAv1PARFYBmlMHzKsHli8AWhqBVNKJvMCDoGwb3VMJ7Pear+z83kH5SV0dDgaZKWEBow+B6/eAm6PAVE4xxAG8DBEwGapPAcuagbYWoHkuUAhgm+bLZnF4Y7t4z0X9PyBGU8rCKYbzNLsK4NYI0HcTyOTD79p/HcGlk0DHMmDpguDAYHrI2djmRl8J0KD8UwBr/SHNu6dpegZKPqPnxoqxzpXKlEHjS+BSZ7t4pphcASY9W+JzAEnvZDTT0D3gXBVg3PEI6nmCagk0X94SeIvJU3A5aBL4ImFhr9+RxyaBX64B2bwuJ8Hn1yWBF1YDTQ3lv9PBCzaOj0m8KS5cla12Ehch0Oqlk/b/YwgYuBvfZ6Jgc8yVC4F1y8vHdIJCYtjK41lxYUDuqG/Edw/Hy4cjKycvAflC1DR6vycTwPa1ANnyHnMbgalx7BS91+WnqRQOZD0ZmYhvjQK//gUkrfAJyWjeLjkqqU8U16+wq3j+5ieApc3lDl6XBnI5HBG9g/K0lcCWgsdP6Mw/XwVuPwiOLALhHa5cBHS2Ay3zFJM3R4Dfh4C/R51QDkyKdO4l84EXnyx37kTSqRzOiN4BeRsCi73+Q0Df9gETmVLSc++Y5zHRvboeWN0KZHJArgBkCyqDE0j/MHCuHxjn9T6qeP2cNPBKRzmgoh/dET0Dksaq815HO584H3yHqQTw+mblnE5WtpXZCIZJcyoLTOWVyX+6Gpyhed2eTYH+mQ0F9OX56V7Au1uzFHhjs0p2PPgvzUWGyBaXFILiUvHjFWU+d2nxjrg7DJCOyWiat18C2haWhnYB8TeHoRwwmVW5iz74w2WALuA1eUWT6Tg1J/lgj4o8965pMrLhZ4hmI2Nf9QB0AW/GruzUGmFPBj7eBxSkCm8e/M7amoCyRZORITJF9o6dLQcUGfY6ibEioLwC5ZgsEw4oMjHqLB3VAoq1dOgsrtUCirW40g/ilh/VAIpdfrje3xOjQGNohzp1BR/iGqdVoBFUnBL2/CDw4e6QKAsB9HUvsKHtEUpYgooq8v8ZA7atVU38tLAPAMSouz+hOhDtIp+Aotog+sH6NpUU4wByGoKQPihWG0RQlRpFZuX17dUDit0oug4e1krPBCDtVtoFFfSwoRpAVT1s8JYJ3scxEkg+t0LPZHxWNCOPY/zVEJNnroA9m1ZhH507jlOzPygUcMwSODFjD6z8wKSUMnC1Dwj7VAOwZmFYnAW3AZFP0AygsP4p7O/GZFGMGYYMQz4GTB6KcgnDkGHI20qb1b7s6Yeph4rhYRbXqDxhGDIMmfLDNIrug3OzdJilIyojGoYMQ+YJGmC6DtN1BMWBqamj8qNhyDBkug7TdZiuwxcFJjGaxGgSo0mMJjGaxBiVCQ1DhqHKDJj/Jo/yEMOQYci8SGC6jqgoMAyFMVQzbwvXzPvUNfXGeU29k19TqoWa03XUlPKlam1QiALvkbVBWuopG6B6hUeoRpHbcEig+6w6V0s9paUv61ITufs1hKk4HY2iDXSfUxrF2PoyXQXeR/vURH5AfhUnhUpUTB0vAiKbsRR4uhrFQ3vVRK52ld+nKYGp5swr4FQUu7LSWBpFHRUnda7v71L0e33IUQJT6+oqgXNKeMvzqCj26lwjVZw68nZOcvA1Z8+Oksm47QaF226U8bWd4odSZr/wNlLnqqME5qTv7lR37FcCO4CKAm5HVV4E+c3Fcs3+jMrbeXe7NgKPP1ZSmjtRRj9yWSqCoW+NPFS6fS2ttI68nSbgxiI71pWk6fwb0wABOZ8iODLIrTnuPAjQ7M+UvJ2hS+H21qeB1ia1xYELyNXdkxmyxq1cLlwPFktynBmRt3Mghu7ctJKKLm5SjLggCI7MDI8Bl26pPWW0dyTQcWq3vCUoMrWoCWhvAeY3KKbuTwKDdxU7BBr04CDaqTXk7f56myCcrX2KPxCAs8FNhUcYkWGvkxijWqQ4v0cmRp2lI86Elc6JtXToLK7VAoq1uHKSuOVHNYBilx/uJHEKNLfk0AWmvf8QJ4hTws7qDk0EFVXkz+oeVgQU1Qa5mXrWdvnihDW1D5rrrDW1U5wLqqb20vOGdc3sNujPNTWzH6MX2GzsWPkfBLU1i3+dVUIAAAAASUVORK5CYII="
|
||||
alt="左链条" class="connection-chain left" />
|
||||
<image
|
||||
src="data:image/png;base64,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"
|
||||
alt="右链条" class="connection-chain right" />
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<wd-icon name="info-o" class="tips-icon" />
|
||||
<text class="tips-title">温馨提示</text>
|
||||
</view>
|
||||
<view>
|
||||
<wd-text rows="2" :content="content" expand-text="展开" collapse-text="收起" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.l-remark-card {
|
||||
position: relative;
|
||||
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
border-radius: 0.75rem;
|
||||
background-color: #ffffff;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.tips-card {
|
||||
background: var(--van-theme-primary-light);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.tips-icon {
|
||||
color: var(--van-theme-primary);
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.tips-title {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.tips-content {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 连接链条样式 */
|
||||
.connection-line {
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60px;
|
||||
z-index: 20;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.connection-chain {
|
||||
height: 60px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.connection-chain.left {
|
||||
width: 80px;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
.connection-chain.right {
|
||||
width: 80px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,80 +0,0 @@
|
||||
<script setup>
|
||||
import { computed, onMounted } from 'vue'
|
||||
|
||||
// 接收表格数据和类型的 props
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'purple-pink', // 默认渐变颜色
|
||||
},
|
||||
})
|
||||
// 根据 type 设置不同的渐变颜色(偶数行)
|
||||
const evenClass = computed(() => {
|
||||
// 统一使用主题色浅色背景
|
||||
return 'bg-red-50/40'
|
||||
})
|
||||
|
||||
// 动态计算表头的背景颜色和文本颜色
|
||||
const headerClass = computed(() => {
|
||||
// 统一使用主题色浅色背景
|
||||
return 'bg-red-100'
|
||||
})
|
||||
// 斑马纹样式,偶数行带颜色,奇数行没有颜色,且从第二行开始
|
||||
function zebraClass(index) {
|
||||
return index % 2 === 1 ? evenClass.value : ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="l-table overflow-x-auto">
|
||||
<table
|
||||
class="min-w-full border-collapse table-auto text-center text-size-xs"
|
||||
>
|
||||
<thead :class="headerClass">
|
||||
<tr>
|
||||
<!-- 插槽渲染表头 -->
|
||||
<slot name="header" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(row, index) in props.data"
|
||||
:key="index"
|
||||
:class="zebraClass(index)"
|
||||
class="border-t"
|
||||
>
|
||||
<slot :row="row" />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 基础表格样式 */
|
||||
th {
|
||||
font-weight: bold;
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
/* 表格行样式 */
|
||||
td {
|
||||
padding: 12px;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
}
|
||||
.l-table {
|
||||
@apply rounded-xl;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -1,27 +0,0 @@
|
||||
<script setup>
|
||||
// 接收 props
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
})
|
||||
|
||||
const titleClass = computed(() => {
|
||||
// 统一使用主题色
|
||||
return 'bg-primary'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="relative">
|
||||
<!-- 标题部分 -->
|
||||
<view :class="titleClass" class="inline-block rounded-lg px-2 py-1 text-white font-bold shadow-md">
|
||||
{{ title }}
|
||||
</view>
|
||||
|
||||
<!-- 左上角修饰 -->
|
||||
<view
|
||||
class="absolute left-0 top-0 h-4 w-4 transform rounded-full bg-white shadow-md -translate-x-2 -translate-y-2"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, ref } from 'vue'
|
||||
import { computed, nextTick, onUnmounted, ref } from 'vue'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
import { useUserStore } from '@/stores/userStore'
|
||||
import { setAuthSession } from '@/utils/storage'
|
||||
@@ -103,24 +103,30 @@ async function handleLogin() {
|
||||
}
|
||||
|
||||
async function performLogin() {
|
||||
const { data, error } = await useApiFetch('/user/mobileCodeLogin')
|
||||
.post({ mobile: phoneNumber.value, code: verificationCode.value })
|
||||
.json()
|
||||
uni.showLoading({ title: '登录中...', mask: true })
|
||||
try {
|
||||
const { data, error } = await useApiFetch('/user/mobileCodeLogin', { silent: true })
|
||||
.post({ mobile: phoneNumber.value, code: verificationCode.value })
|
||||
.json()
|
||||
|
||||
if (data.value && !error.value) {
|
||||
if (data.value.code === 200) {
|
||||
setAuthSession(data.value.data)
|
||||
if (data.value && !error.value) {
|
||||
if (data.value.code === 200) {
|
||||
setAuthSession(data.value.data)
|
||||
|
||||
await userStore.fetchUserInfo()
|
||||
await userStore.fetchUserInfo()
|
||||
|
||||
showToast({ message: '登录成功' })
|
||||
closeDialog()
|
||||
emit('login-success')
|
||||
}
|
||||
else {
|
||||
showToast(data.value.msg)
|
||||
showToast({ message: '登录成功' })
|
||||
closeDialog()
|
||||
emit('login-success')
|
||||
}
|
||||
else {
|
||||
showToast(data.value.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
@@ -146,11 +152,23 @@ function toPrivacyPolicy() {
|
||||
closeDialog()
|
||||
uni.navigateTo({ url: '/pages/privacy-policy' })
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<wd-popup v-model="dialogStore.showLogin" round position="bottom" :style="{ maxHeight: '90vh' }" :z-index="2000"
|
||||
@close="closeDialog">
|
||||
<wd-popup
|
||||
v-model="dialogStore.showLogin"
|
||||
round
|
||||
position="bottom"
|
||||
:style="{ maxHeight: '90vh' }"
|
||||
:z-index="2000"
|
||||
@close="closeDialog"
|
||||
>
|
||||
<view class="login-dialog">
|
||||
<view class="title-bar">
|
||||
<view class="title-bar-text">
|
||||
@@ -174,8 +192,15 @@ function toPrivacyPolicy() {
|
||||
<text class="form-label">
|
||||
手机号
|
||||
</text>
|
||||
<wd-input v-model="phoneNumber" class="phone-wd-input" type="number" placeholder="请输入手机号" maxlength="11"
|
||||
no-border clearable />
|
||||
<wd-input
|
||||
v-model="phoneNumber"
|
||||
class="phone-wd-input"
|
||||
type="number"
|
||||
placeholder="请输入手机号"
|
||||
maxlength="11"
|
||||
no-border
|
||||
clearable
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view v-if="!isPasswordLogin" class="form-item">
|
||||
@@ -183,11 +208,23 @@ function toPrivacyPolicy() {
|
||||
验证码
|
||||
</text>
|
||||
<view class="verification-input-wrapper">
|
||||
<wd-input ref="verificationCodeInputRef" v-model="verificationCode" class="verification-wd-input"
|
||||
placeholder="请输入验证码" maxlength="6" no-border clearable>
|
||||
<wd-input
|
||||
ref="verificationCodeInputRef"
|
||||
v-model="verificationCode"
|
||||
class="verification-wd-input"
|
||||
placeholder="请输入验证码"
|
||||
maxlength="6"
|
||||
no-border
|
||||
clearable
|
||||
>
|
||||
<template #suffix>
|
||||
<wd-button size="small" type="primary" plain :disabled="isCountingDown || !isPhoneNumberValid"
|
||||
@click="sendVerificationCode">
|
||||
<wd-button
|
||||
size="small"
|
||||
type="primary"
|
||||
plain
|
||||
:disabled="isCountingDown || !isPhoneNumberValid"
|
||||
@click="sendVerificationCode"
|
||||
>
|
||||
{{ isCountingDown ? `${countdown}s` : '获取验证码' }}
|
||||
</wd-button>
|
||||
</template>
|
||||
@@ -199,8 +236,15 @@ function toPrivacyPolicy() {
|
||||
<text class="form-label">
|
||||
密码
|
||||
</text>
|
||||
<wd-input v-model="password" class="phone-wd-input" type="text" show-password placeholder="请输入密码" no-border
|
||||
clearable />
|
||||
<wd-input
|
||||
v-model="password"
|
||||
class="phone-wd-input"
|
||||
type="text"
|
||||
show-password
|
||||
placeholder="请输入密码"
|
||||
no-border
|
||||
clearable
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="flex items-center justify-end py-1">
|
||||
|
||||
@@ -15,25 +15,14 @@ const props = defineProps({
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
const { isWeChat } = useEnv()
|
||||
const isDev = import.meta.env.DEV
|
||||
|
||||
/** APP 原生端同时展示微信与支付宝;其它端沿用微信内仅微信、否则仅支付宝 */
|
||||
const isAppClient = computed(() => {
|
||||
try {
|
||||
return uni.getSystemInfoSync().uniPlatform === 'app'
|
||||
}
|
||||
catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
const appName = import.meta.env.VITE_APP_NAME || 'App'
|
||||
const appLogo = '/static/images/logo.png'
|
||||
const wechatPayIcon = '/static/images/wechatpay.svg'
|
||||
const alipayIcon = '/static/images/alipay.svg'
|
||||
|
||||
const show = defineModel()
|
||||
const selectedPaymentMethod = ref(isWeChat.value ? 'wechat' : 'alipay')
|
||||
const selectedPaymentMethod = ref('alipay')
|
||||
const paymentDisplayTime = ref('')
|
||||
|
||||
function toFiniteNumber(value) {
|
||||
@@ -61,12 +50,6 @@ const displayAmount = computed(() => {
|
||||
return payableAmount.value.toFixed(2)
|
||||
})
|
||||
|
||||
const displayDiscountAmount = computed(() => {
|
||||
if (payableAmount.value === null)
|
||||
return '--'
|
||||
return (payableAmount.value * 0.2).toFixed(2)
|
||||
})
|
||||
|
||||
/** 支付弹窗展示用:YYYY-MM-DD HH:mm:ss */
|
||||
function formatPaymentTime(d = new Date()) {
|
||||
const pad = n => String(n).padStart(2, '0')
|
||||
@@ -84,11 +67,7 @@ function showToast(options) {
|
||||
}
|
||||
|
||||
function setDefaultPaymentMethod() {
|
||||
if (isAppClient.value) {
|
||||
selectedPaymentMethod.value = 'alipay'
|
||||
return
|
||||
}
|
||||
selectedPaymentMethod.value = isWeChat.value ? 'wechat' : 'alipay'
|
||||
selectedPaymentMethod.value = 'alipay'
|
||||
}
|
||||
|
||||
onMounted(setDefaultPaymentMethod)
|
||||
@@ -101,7 +80,6 @@ watch(show, (v) => {
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
const discountPrice = ref(false)
|
||||
|
||||
/** APP 端 wxpay:服务端返回的 prepay_data 对象,供 uni.requestPayment 使用 */
|
||||
function normalizeWxAppOrderInfo(raw) {
|
||||
@@ -129,103 +107,54 @@ async function getPayment() {
|
||||
const prepayData = respData.prepay_data
|
||||
const orderNoFromResp = respData.order_no
|
||||
|
||||
if (prepayId === 'test_payment_success') {
|
||||
if (selectedPaymentMethod.value === 'alipay' || selectedPaymentMethod.value === 'wechat') {
|
||||
showToast({ message: '支付参数异常,请重试', type: 'fail' })
|
||||
return
|
||||
}
|
||||
show.value = false
|
||||
router.push({
|
||||
path: '/payment/result',
|
||||
query: { orderNo: orderNoFromResp },
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// APP 原生:支付宝 / 微信(uni.requestPayment)
|
||||
if (isAppClient.value) {
|
||||
if (selectedPaymentMethod.value === 'alipay') {
|
||||
if (!prepayId || typeof prepayId !== 'string') {
|
||||
showToast({ message: '支付宝下单参数异常', type: 'fail' })
|
||||
return
|
||||
}
|
||||
uni.requestPayment({
|
||||
provider: 'alipay',
|
||||
orderInfo: prepayId,
|
||||
success: () => {
|
||||
show.value = false
|
||||
router.push({
|
||||
path: '/payment/result',
|
||||
query: { orderNo: orderNoFromResp },
|
||||
})
|
||||
},
|
||||
fail: (e) => {
|
||||
const msg = (e && (e.errMsg || e.message)) || '支付未完成'
|
||||
showToast({ message: String(msg), type: 'fail' })
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (selectedPaymentMethod.value === 'wechat') {
|
||||
const orderInfo = normalizeWxAppOrderInfo(prepayData)
|
||||
if (!orderInfo) {
|
||||
showToast({ message: '微信支付参数异常', type: 'fail' })
|
||||
return
|
||||
}
|
||||
uni.requestPayment({
|
||||
provider: 'wxpay',
|
||||
orderInfo,
|
||||
success: () => {
|
||||
show.value = false
|
||||
router.push({
|
||||
path: '/payment/result',
|
||||
query: { orderNo: orderNoFromResp },
|
||||
})
|
||||
},
|
||||
fail: (e) => {
|
||||
const msg = (e && (e.errMsg || e.message)) || '支付未完成'
|
||||
showToast({ message: String(msg), type: 'fail' })
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedPaymentMethod.value === 'alipay') {
|
||||
if (typeof document === 'undefined') {
|
||||
showToast({ message: '当前环境不支持网页支付宝支付', type: 'fail' })
|
||||
if (!prepayId || typeof prepayId !== 'string') {
|
||||
showToast({ message: '支付宝下单参数异常', type: 'fail' })
|
||||
return
|
||||
}
|
||||
const prepayUrl = prepayId
|
||||
const paymentForm = document.createElement('form')
|
||||
paymentForm.method = 'POST'
|
||||
paymentForm.action = prepayUrl
|
||||
paymentForm.style.display = 'none'
|
||||
document.body.appendChild(paymentForm)
|
||||
paymentForm.submit()
|
||||
show.value = false
|
||||
return
|
||||
}
|
||||
|
||||
const payload = prepayData
|
||||
if (typeof WeixinJSBridge === 'undefined') {
|
||||
showToast({ message: '请在微信内打开以完成支付', type: 'fail' })
|
||||
return
|
||||
}
|
||||
WeixinJSBridge.invoke(
|
||||
'getBrandWCPayRequest',
|
||||
payload,
|
||||
(res) => {
|
||||
if (res.err_msg === 'get_brand_wcpay_request:ok') {
|
||||
uni.requestPayment({
|
||||
provider: 'alipay',
|
||||
orderInfo: prepayId,
|
||||
success: () => {
|
||||
show.value = false
|
||||
router.push({
|
||||
path: '/payment/result',
|
||||
query: { orderNo: orderNoFromResp },
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
fail: (e) => {
|
||||
const msg = (e && (e.errMsg || e.message)) || '支付未完成'
|
||||
showToast({ message: String(msg), type: 'fail' })
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (selectedPaymentMethod.value === 'wechat') {
|
||||
const orderInfo = normalizeWxAppOrderInfo(prepayData)
|
||||
if (!orderInfo) {
|
||||
showToast({ message: '微信支付参数异常', type: 'fail' })
|
||||
return
|
||||
}
|
||||
uni.requestPayment({
|
||||
provider: 'wxpay',
|
||||
orderInfo,
|
||||
success: () => {
|
||||
show.value = false
|
||||
router.push({
|
||||
path: '/payment/result',
|
||||
query: { orderNo: orderNoFromResp },
|
||||
})
|
||||
},
|
||||
fail: (e) => {
|
||||
const msg = (e && (e.errMsg || e.message)) || '支付未完成'
|
||||
showToast({ message: String(msg), type: 'fail' })
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
showToast({ message: '支付方式不支持', type: 'fail' })
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
@@ -234,14 +163,8 @@ function onCancel() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="show"
|
||||
round
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:z-index="2000"
|
||||
custom-style="max-height: 88vh;"
|
||||
>
|
||||
<wd-popup v-model="show" round position="bottom" :safe-area-inset-bottom="true" :z-index="2000"
|
||||
custom-style="max-height: 88vh;">
|
||||
<view class="payment-popup">
|
||||
<view class="payment-popup__header">
|
||||
<text class="payment-popup__title">
|
||||
@@ -280,21 +203,11 @@ function onCancel() {
|
||||
应付金额
|
||||
</view>
|
||||
<view class="payment-popup__amount-price">
|
||||
<view v-if="discountPrice" class="payment-popup__strike">
|
||||
¥ {{ displayAmount }}
|
||||
</view>
|
||||
<view>
|
||||
¥
|
||||
{{
|
||||
discountPrice
|
||||
? displayDiscountAmount
|
||||
: displayAmount
|
||||
}}
|
||||
{{ displayAmount }}
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="discountPrice" class="payment-popup__discount-tip">
|
||||
活动价:2折优惠
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="payment-popup__methods">
|
||||
@@ -302,15 +215,15 @@ function onCancel() {
|
||||
支付方式
|
||||
</text>
|
||||
<wd-radio-group v-model="selectedPaymentMethod" shape="dot" cell class="payment-radio-group">
|
||||
<wd-radio v-if="isAppClient || isWeChat" value="wechat">
|
||||
<!-- <wd-radio value="wechat">
|
||||
<view class="payment-radio-row">
|
||||
<image class="payment-radio-row__pay-icon" :src="wechatPayIcon" mode="aspectFit" />
|
||||
<text>
|
||||
微信支付
|
||||
</text>
|
||||
</view>
|
||||
</wd-radio>
|
||||
<wd-radio v-if="isAppClient || !isWeChat" value="alipay">
|
||||
</wd-radio> -->
|
||||
<wd-radio value="alipay">
|
||||
<view class="payment-radio-row">
|
||||
<image class="payment-radio-row__pay-icon" :src="alipayIcon" mode="aspectFit" />
|
||||
<text>
|
||||
@@ -318,23 +231,13 @@ function onCancel() {
|
||||
</text>
|
||||
</view>
|
||||
</wd-radio>
|
||||
<wd-radio v-if="isDev" value="test">
|
||||
<view class="payment-radio-row">
|
||||
<wd-icon size="24" name="description" color="#ff976a" class="payment-radio-row__icon" />
|
||||
<text>
|
||||
开发环境测试支付
|
||||
</text>
|
||||
</view>
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</view>
|
||||
|
||||
<view class="payment-popup__actions">
|
||||
<!-- eslint-disable-next-line unocss/order-attributify -- 组件属性 block,非 UnoCSS -->
|
||||
<wd-button block round size="large" type="primary" custom-class="payment-btn-primary" @click="getPayment">
|
||||
确认支付
|
||||
</wd-button>
|
||||
<!-- eslint-disable-next-line unocss/order-attributify -- 组件属性 block,非 UnoCSS -->
|
||||
<wd-button block round size="small" type="text" custom-class="payment-btn-cancel" @click="onCancel">
|
||||
取消
|
||||
</wd-button>
|
||||
@@ -433,19 +336,6 @@ function onCancel() {
|
||||
color: #ee0a24;
|
||||
}
|
||||
|
||||
.payment-popup__strike {
|
||||
margin-bottom: 8rpx;
|
||||
font-size: 28rpx;
|
||||
color: #969799;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.payment-popup__discount-tip {
|
||||
margin-top: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #ee0a24;
|
||||
}
|
||||
|
||||
.payment-popup__section-label {
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
@@ -62,6 +62,9 @@ const promotionRevenue = computed(() => {
|
||||
return safeTruncate(price.value - costPrice.value)
|
||||
})
|
||||
|
||||
/** APP 端 placeholder 需用原生 style,否则字号易偏小 */
|
||||
const PRICE_PLACEHOLDER_STYLE = 'color:#9ca3af;font-size:40rpx;font-weight:500;'
|
||||
|
||||
// 价格校验与修正逻辑
|
||||
function validatePrice(currentPrice) {
|
||||
if (!productConfig.value) {
|
||||
@@ -154,24 +157,20 @@ function onBlurPrice() {
|
||||
</view>
|
||||
<view class="card m-4">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="text-lg">
|
||||
<view class="text-lg font-semibold text-gray-800">
|
||||
客户查询价 (元)
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="price-input-wrap border border-orange-100 rounded-xl bg-orange-50/40 px-2">
|
||||
<wd-input
|
||||
v-model="price"
|
||||
type="number"
|
||||
label="¥"
|
||||
size="large"
|
||||
label-width="34"
|
||||
no-border
|
||||
custom-input-class="price-input-inner"
|
||||
custom-label-class="price-input-label"
|
||||
<view class="price-input-surface">
|
||||
<!-- 不用 label 而用 prefix,避免 is-cell 下金额区与 ¥ 被拉成分栏靠右 -->
|
||||
<wd-input v-model="price" custom-class="price-wd-input" type="number" size="large" no-border
|
||||
:placeholder="`${productConfig?.price_range_min || 0} - ${productConfig?.price_range_max || 0}`"
|
||||
@blur="onBlurPrice"
|
||||
/>
|
||||
:placeholder-style="PRICE_PLACEHOLDER_STYLE" custom-input-class="price-input-inner" @blur="onBlurPrice">
|
||||
<template #prefix>
|
||||
<text class="price-currency">¥</text>
|
||||
</template>
|
||||
</wd-input>
|
||||
</view>
|
||||
<view class="mt-2 flex items-center justify-between">
|
||||
<view>
|
||||
@@ -222,27 +221,64 @@ function onBlurPrice() {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.price-input-wrap {
|
||||
box-shadow: inset 0 0 0 1px rgba(251, 146, 60, 0.08);
|
||||
/* 可点击区域:白底+粗边框+阴影,APP 上边界更清晰 */
|
||||
.price-input-surface {
|
||||
margin-top: 12px;
|
||||
padding: 24rpx 20rpx;
|
||||
min-height: 120rpx;
|
||||
border: 2px solid #fb923c;
|
||||
border-radius: 20rpx;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 12px rgba(251, 146, 60, 0.12);
|
||||
}
|
||||
|
||||
.price-input-wrap :deep(.price-input-label) {
|
||||
.price-input-surface :deep(.price-wd-input.wd-input) {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.price-input-surface :deep(.wd-input__body) {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* prefix + 输入同一行,金额紧贴 ¥ 左侧起笔 */
|
||||
.price-input-surface :deep(.wd-input__value) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
min-height: 96rpx;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.price-input-surface :deep(.wd-input__prefix) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 0;
|
||||
padding-right: 6rpx;
|
||||
}
|
||||
|
||||
.price-currency {
|
||||
color: #ea580c;
|
||||
font-size: 22px;
|
||||
font-size: 64rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.price-input-wrap :deep(.price-input-inner) {
|
||||
height: 56px;
|
||||
font-size: 40px;
|
||||
/* 金额:与 ¥ 同档字号 */
|
||||
.price-input-surface :deep(.wd-input__inner),
|
||||
.price-input-surface :deep(input.wd-input__inner),
|
||||
.price-input-surface :deep(.price-input-inner) {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 88rpx;
|
||||
font-size: 64rpx !important;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
line-height: 56px;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.price-input-wrap :deep(.wd-input__placeholder) {
|
||||
font-size: 20px;
|
||||
color: #111827 !important;
|
||||
line-height: 1.15 !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -131,14 +131,14 @@ function scaleQrRectForPoster(position, posterInfo, modeKey) {
|
||||
// QR码位置配置(为每个海报单独配置)
|
||||
const qrCodePositions = ref({
|
||||
promote: [
|
||||
{ x: 405, y: 1620, size: 480 }, // tg_qrcode_1.png
|
||||
{ x: 405, y: 1620, size: 480 }, // tg_qrcode_2.jpg
|
||||
{ x: 405, y: 1620, size: 480 }, // tg_qrcode_3.jpg
|
||||
{ x: 405, y: 1620, size: 480 }, // tg_qrcode_4.jpg
|
||||
{ x: 405, y: 1620, size: 480 }, // tg_qrcode_5.jpg
|
||||
{ x: 180, y: 1440, size: 300 }, // tg_qrcode_6.jpg
|
||||
{ x: 255, y: 940, size: 250 }, // tg_qrcode_7.jpg
|
||||
{ x: 255, y: 940, size: 250 }, // tg_qrcode_8.jpg
|
||||
{ x: 405, y: 1270, size: 440 }, // tg_qrcode_1.png
|
||||
{ x: 405, y: 1270, size: 440 }, // tg_qrcode_2.jpg
|
||||
{ x: 405, y: 1270, size: 440 }, // tg_qrcode_3.jpg
|
||||
{ x: 405, y: 1270, size: 440 }, // tg_qrcode_4.jpg
|
||||
{ x: 405, y: 1270, size: 440 }, // tg_qrcode_5.jpg
|
||||
{ x: 210, y: 1660, size: 340 }, // tg_qrcode_6.jpg
|
||||
{ x: 405, y: 1270, size: 440 }, // tg_qrcode_7.jpg
|
||||
{ x: 405, y: 1270, size: 440 }, // tg_qrcode_8.jpg
|
||||
],
|
||||
// invitation模式的配置 (yq_qrcode)
|
||||
invitation: [
|
||||
@@ -784,37 +784,28 @@ export default {
|
||||
<!-- 放在弹窗外,避免 wd-popup 关闭时子树未挂载导致 APP 无法 createCanvasContext -->
|
||||
<canvas v-if="!isWebPlatform" :id="QR_GEN_CANVAS_ID" :canvas-id="QR_GEN_CANVAS_ID" class="poster-qr-gen-canvas" />
|
||||
<!-- App:仅作 renderjs 的 change 锚点;真正画布在 renderjs 里 createElement('canvas') -->
|
||||
<view
|
||||
v-if="!isWebPlatform" class="poster-renderjs-anchor" :poster-merge-req="posterMergeRequest"
|
||||
:change:poster-merge-req="posterRender.onMergeReqChange" aria-hidden="true"
|
||||
/>
|
||||
<view v-if="!isWebPlatform" class="poster-renderjs-anchor" :poster-merge-req="posterMergeRequest"
|
||||
:change:poster-merge-req="posterRender.onMergeReqChange" aria-hidden="true" />
|
||||
<wd-popup v-model="show" round position="bottom" :style="{ maxHeight: '95vh' }">
|
||||
<view class="qrcode-popup-container">
|
||||
<view class="qrcode-content">
|
||||
<swiper
|
||||
class="poster-swiper rounded-lg shadow sm:rounded-xl" indicator-color="white" circular indicator-dots
|
||||
@change="onSwipeChange"
|
||||
>
|
||||
<swiper class="poster-swiper rounded-lg shadow sm:rounded-xl" indicator-color="white" circular indicator-dots
|
||||
@change="onSwipeChange">
|
||||
<swiper-item v-for="(_, index) in posterImages" :key="index" class="poster-swiper-item">
|
||||
<view class="poster-canvas-box">
|
||||
<template v-if="isWebPlatform">
|
||||
<view class="poster-preview-box web-preview">
|
||||
<canvas
|
||||
:id="getCanvasId(index)" :ref="(el) => (posterCanvasRefs[index] = el)"
|
||||
<canvas :id="getCanvasId(index)" :ref="(el) => (posterCanvasRefs[index] = el)"
|
||||
:canvas-id="getCanvasId(index)" :width="posterCanvasSizes[index]?.width ?? 300"
|
||||
:height="posterCanvasSizes[index]?.height ?? 300"
|
||||
:style="posterCanvasContainStyle(index)"
|
||||
class="poster-canvas poster-canvas--h5-contain rounded-lg sm:rounded-xl"
|
||||
/>
|
||||
:height="posterCanvasSizes[index]?.height ?? 300" :style="posterCanvasContainStyle(index)"
|
||||
class="poster-canvas poster-canvas--h5-contain rounded-lg sm:rounded-xl" />
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<!-- App:预览与保存同源;aspectFit 在轮播高度内整图可见,等比、宽可不拉满 -->
|
||||
<view v-if="posterCompositePath[index]" class="poster-app-preview">
|
||||
<image
|
||||
:src="posterCompositePath[index]" mode="aspectFit"
|
||||
class="poster-app-composite rounded-lg sm:rounded-xl"
|
||||
/>
|
||||
<image :src="posterCompositePath[index]" mode="aspectFit"
|
||||
class="poster-app-composite rounded-lg sm:rounded-xl" />
|
||||
</view>
|
||||
<view v-else class="poster-preview-placeholder">
|
||||
<text class="poster-preview-placeholder-text">
|
||||
@@ -874,10 +865,8 @@ export default {
|
||||
</wd-popup>
|
||||
|
||||
<!-- 图片保存指引遮罩层 -->
|
||||
<ImageSaveGuide
|
||||
:show="showImageGuide" :image-url="currentImageUrl" :title="imageGuideTitle"
|
||||
@close="closeImageGuide"
|
||||
/>
|
||||
<ImageSaveGuide :show="showImageGuide" :image-url="currentImageUrl" :title="imageGuideTitle"
|
||||
@close="closeImageGuide" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -943,7 +932,7 @@ export default {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.poster-canvas-box > view {
|
||||
.poster-canvas-box>view {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
@@ -1085,13 +1074,13 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化 van-divider 在小屏幕上的间距 */
|
||||
:deep(.van-divider) {
|
||||
/* 优化 wd-divider 在小屏幕上的间距 */
|
||||
:deep(.wd-divider) {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
:deep(.van-divider) {
|
||||
:deep(.wd-divider) {
|
||||
margin: 0.75rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ const isPhoneNumberValid = computed(() => {
|
||||
})
|
||||
|
||||
const isIdCardValid = computed(() => {
|
||||
return /^(?:\d{15}|\d{17}[\dXx])$/.test(idCard.value)
|
||||
return /^(?:\d{15}|\d{17}[\dX])$/i.test(idCard.value)
|
||||
})
|
||||
|
||||
const isRealNameValid = computed(() => {
|
||||
@@ -172,22 +172,22 @@ onUnmounted(() => {
|
||||
>
|
||||
<view
|
||||
class="real-name-auth-dialog"
|
||||
style="background: linear-gradient(135deg, var(--van-theme-primary-light), rgba(255,255,255,0.9));"
|
||||
style="background: linear-gradient(135deg, var(--color-primary-light), rgba(255,255,255,0.9));"
|
||||
>
|
||||
<view class="title-bar">
|
||||
<view class="text-base font-bold sm:text-lg" style="color: var(--van-text-color);">
|
||||
<view class="text-base font-bold sm:text-lg" style="color: var(--color-text-primary);">
|
||||
实名认证
|
||||
</view>
|
||||
<wd-icon name="cross" class="close-icon" style="color: var(--van-text-color-2);" @click="closeDialog" />
|
||||
<wd-icon name="cross" class="close-icon" style="color: var(--color-text-secondary);" @click="closeDialog" />
|
||||
</view>
|
||||
<view class="dialog-content">
|
||||
<view class="dialog-inner px-4 pb-4 pt-2">
|
||||
<view
|
||||
class="auth-notice mb-4 rounded-xl p-3 sm:p-4"
|
||||
style="background-color: var(--van-theme-primary-light); border: 1px solid rgba(162, 37, 37, 0.2);"
|
||||
style="background-color: var(--color-primary-light); border: 1px solid rgba(162, 37, 37, 0.2);"
|
||||
>
|
||||
<view class="text-xs space-y-1.5 sm:text-sm sm:space-y-2" style="color: var(--van-text-color);">
|
||||
<text class="font-medium" style="color: var(--van-theme-primary);">
|
||||
<view class="text-xs space-y-1.5 sm:text-sm sm:space-y-2" style="color: var(--color-text-primary);">
|
||||
<text class="font-medium" style="color: var(--color-primary);">
|
||||
实名认证说明:
|
||||
</text>
|
||||
<text>1. 实名认证是提现的必要条件</text>
|
||||
@@ -320,7 +320,7 @@ onUnmounted(() => {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--van-border-color);
|
||||
border-bottom: 1px solid var(--color-border-primary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
orderId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
orderNo: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isExample: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const isLoading = ref(false)
|
||||
function showToast(options) {
|
||||
const message = typeof options === 'string' ? options : (options?.message || options?.title || '')
|
||||
if (!message)
|
||||
return
|
||||
uni.showToast({
|
||||
title: message,
|
||||
icon: options?.type === 'success' ? 'success' : 'none',
|
||||
})
|
||||
}
|
||||
|
||||
async function copyToClipboard(text) {
|
||||
await navigator.clipboard.writeText(text)
|
||||
showToast({
|
||||
type: 'success',
|
||||
message: '链接已复制到剪贴板',
|
||||
position: 'bottom',
|
||||
})
|
||||
}
|
||||
|
||||
async function handleShare() {
|
||||
if (isLoading.value || props.disabled)
|
||||
return
|
||||
|
||||
// 如果是示例模式,直接分享当前URL
|
||||
if (props.isExample) {
|
||||
try {
|
||||
const currentUrl = window.location.href
|
||||
await copyToClipboard(currentUrl)
|
||||
showToast({
|
||||
type: 'success',
|
||||
message: '示例链接已复制到剪贴板',
|
||||
position: 'bottom',
|
||||
})
|
||||
}
|
||||
catch {
|
||||
showToast({
|
||||
type: 'fail',
|
||||
message: '复制链接失败',
|
||||
position: 'bottom',
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 优先使用 orderId,如果没有则使用 orderNo
|
||||
const orderIdentifier = props.orderId || props.orderNo
|
||||
if (!orderIdentifier) {
|
||||
showToast({
|
||||
type: 'fail',
|
||||
message: '缺少订单标识',
|
||||
position: 'bottom',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
isLoading.value = true
|
||||
try {
|
||||
// 根据实际使用的标识构建请求参数
|
||||
const requestData = props.orderId
|
||||
? { order_id: Number.parseInt(props.orderId) }
|
||||
: { order_no: props.orderNo }
|
||||
|
||||
const { data, error } = await useApiFetch('/query/generate_share_link')
|
||||
.post(requestData)
|
||||
.json()
|
||||
|
||||
if (error.value) {
|
||||
throw new Error(error.value)
|
||||
}
|
||||
|
||||
if (data.value?.code === 200 && data.value.data?.share_link) {
|
||||
const baseUrl = window.location.origin
|
||||
const linkId = encodeURIComponent(data.value.data.share_link)
|
||||
const fullShareUrl = `${baseUrl}/report/share/${linkId}`
|
||||
|
||||
try {
|
||||
const { confirm } = await uni.showModal({
|
||||
title: '分享链接已生成',
|
||||
content: '链接将在7天后过期,是否复制到剪贴板?',
|
||||
confirmText: '复制链接',
|
||||
cancelText: '取消',
|
||||
})
|
||||
if (!confirm)
|
||||
return
|
||||
|
||||
await copyToClipboard(fullShareUrl)
|
||||
}
|
||||
catch (dialogErr) {
|
||||
console.error(dialogErr)
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error(data.value?.message || '生成分享链接失败')
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
showToast({
|
||||
type: 'fail',
|
||||
message: err.message || '生成分享链接失败',
|
||||
position: 'bottom',
|
||||
})
|
||||
}
|
||||
finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view
|
||||
class="bg-primary border-primary hover:bg-primary-600 flex cursor-pointer items-center justify-center border rounded-[40px] px-3 py-1 transition-colors duration-200"
|
||||
:class="{ 'opacity-50 cursor-not-allowed': isLoading || disabled }" @click="handleShare"
|
||||
>
|
||||
<image src="/static/images/report/fx.png" alt="分享" class="mr-1 h-4 w-4" />
|
||||
<text class="text-sm text-white font-medium">
|
||||
{{ isLoading ? "生成中..." : (isExample ? "分享示例" : "分享报告") }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 样式已通过 Tailwind CSS 类实现 */
|
||||
</style>
|
||||
@@ -1,44 +0,0 @@
|
||||
<script setup>
|
||||
// 透传所有属性和事件到 van-tabs
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<wd-tabs v-bind="$attrs" type="card" class="styled-tabs">
|
||||
<slot />
|
||||
</wd-tabs>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* van-tabs 卡片样式定制 - 仅用于此组件 */
|
||||
.styled-tabs:deep(.van-tabs__line) {
|
||||
background-color: var(--van-theme-primary) !important;
|
||||
}
|
||||
|
||||
.styled-tabs:deep(.van-tabs__nav) {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.styled-tabs:deep(.van-tabs__nav--card) {
|
||||
border: unset !important;
|
||||
}
|
||||
|
||||
.styled-tabs:deep(.van-tab--card) {
|
||||
color: #666666 !important;
|
||||
border-right: unset !important;
|
||||
background-color: #eeeeee !important;
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
|
||||
.styled-tabs:deep(.van-tab--active) {
|
||||
color: white !important;
|
||||
background-color: var(--van-theme-primary) !important;
|
||||
}
|
||||
|
||||
.styled-tabs:deep(.van-tabs__wrap) {
|
||||
background-color: #ffffff !important;
|
||||
padding: 9px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,23 +0,0 @@
|
||||
<script setup>
|
||||
// 不需要额外的 props 或逻辑,只是一个简单的样式组件
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="title-banner">
|
||||
<slot />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.title-banner {
|
||||
@apply mx-auto mt-2 w-64 py-1.5 text-center text-white font-bold text-lg relative rounded-2xl;
|
||||
background: var(--color-primary);
|
||||
border: 1px solid var(--color-primary-300);
|
||||
background-image:
|
||||
linear-gradient(45deg, transparent 25%, rgba(255, 255, 255, 0.1) 25%, rgba(255, 255, 255, 0.1) 50%, transparent 50%, transparent 75%, rgba(255, 255, 255, 0.1) 75%);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -1,187 +0,0 @@
|
||||
<script setup>
|
||||
import LTitle from './LTitle.vue'
|
||||
import ShareReportButton from './ShareReportButton.vue'
|
||||
|
||||
const props = defineProps({
|
||||
reportParams: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
reportDateTime: {
|
||||
type: [String, null],
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
reportName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isEmpty: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
isShare: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
orderId: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
orderNo: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
|
||||
// 脱敏函数
|
||||
function maskValue(type, value) {
|
||||
if (!value)
|
||||
return value
|
||||
if (type === 'name') {
|
||||
// 姓名脱敏(保留首位)
|
||||
if (value.length === 1) {
|
||||
return '*' // 只保留一个字,返回 "*"
|
||||
}
|
||||
else if (value.length === 2) {
|
||||
return `${value[0]}*` // 两个字,保留姓氏,第二个字用 "*" 替代
|
||||
}
|
||||
else {
|
||||
return (
|
||||
value[0]
|
||||
+ '*'.repeat(value.length - 2)
|
||||
+ value[value.length - 1]
|
||||
) // 两个字以上,保留第一个和最后一个字,其余的用 "*" 替代
|
||||
}
|
||||
}
|
||||
else if (type === 'id_card') {
|
||||
// 身份证号脱敏(保留前6位和最后4位)
|
||||
return value.replace(/^(.{6})\d+(.{4})$/, '$1****$2')
|
||||
}
|
||||
else if (type === 'mobile') {
|
||||
if (value.length === 11) {
|
||||
return `${value.substring(0, 3)}****${value.substring(7)}`
|
||||
}
|
||||
return value // 如果手机号不合法或长度不为 11 位,直接返回原手机号
|
||||
}
|
||||
return value
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="card" style="padding-left: 0; padding-right: 0; padding-bottom: 24px;">
|
||||
<view class="flex flex-col gap-y-2">
|
||||
<!-- 报告信息 -->
|
||||
<view class="flex items-center justify-between py-2">
|
||||
<LTitle title="报告信息" />
|
||||
<!-- 分享按钮 -->
|
||||
<ShareReportButton
|
||||
v-if="!isShare" :order-id="orderId" :order-no="orderNo" :is-example="!orderId"
|
||||
class="mr-4"
|
||||
/>
|
||||
</view>
|
||||
<view class="mx-4 my-2 flex flex-col gap-2">
|
||||
<view class="flex pb-2 pl-2">
|
||||
<text class="w-[6em] text-[#666666]">报告时间:</text>
|
||||
<text class="text-gray-600">{{
|
||||
reportDateTime
|
||||
|| "2025-01-01 12:00:00"
|
||||
}}</text>
|
||||
</view>
|
||||
<view v-if="!isEmpty" class="flex pb-2 pl-2">
|
||||
<text class="w-[6em] text-[#666666]">报告项目:</text>
|
||||
<text class="text-gray-600 font-bold">
|
||||
{{ reportName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 报告对象 -->
|
||||
<template v-if="Object.keys(reportParams).length != 0">
|
||||
<LTitle title="报告对象" />
|
||||
<view class="mx-4 my-2 flex flex-col gap-2">
|
||||
<!-- 姓名 -->
|
||||
<view v-if="reportParams?.name" class="flex pb-2 pl-2">
|
||||
<text class="w-[6em] text-[#666666]">姓名</text>
|
||||
<text class="text-gray-600">{{
|
||||
maskValue(
|
||||
"name",
|
||||
reportParams?.name,
|
||||
)
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 身份证号 -->
|
||||
<view v-if="reportParams?.id_card" class="flex pb-2 pl-2">
|
||||
<text class="w-[6em] text-[#666666]">身份证号</text>
|
||||
<text class="text-gray-600">{{
|
||||
maskValue(
|
||||
"id_card",
|
||||
reportParams?.id_card,
|
||||
)
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 手机号 -->
|
||||
<view v-if="reportParams?.mobile" class="flex pb-2 pl-2">
|
||||
<text class="w-[6em] text-[#666666]">手机号</text>
|
||||
<text class="text-gray-600">{{
|
||||
maskValue(
|
||||
"mobile",
|
||||
reportParams?.mobile,
|
||||
)
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 验证卡片 -->
|
||||
<view class="mt-4 flex flex-col gap-4">
|
||||
<!-- 身份证检查结果 -->
|
||||
<view class="flex flex-1 items-center border border-[#EEEEEE] rounded-lg bg-[#F9F9F9] px-4 py-3">
|
||||
<view class="mr-4 h-11 w-11 flex items-center justify-center">
|
||||
<image src="/static/images/report/sfz.png" alt="身份证" class="h-10 w-10 object-contain" />
|
||||
</view>
|
||||
<view class="flex-1">
|
||||
<view class="text-lg text-gray-800 font-bold">
|
||||
身份证检查结果
|
||||
</view>
|
||||
<view class="text-sm text-[#999999]">
|
||||
身份证信息核验通过
|
||||
</view>
|
||||
</view>
|
||||
<view class="ml-4 h-11 w-11 flex items-center justify-center">
|
||||
<image src="/static/images/report/zq.png" alt="资金安全" class="h-10 w-10 object-contain" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 手机号检测结果 -->
|
||||
<!-- <view class="flex items-center px-4 py-3 flex-1 border border-[#EEEEEE] rounded-lg bg-[#F9F9F9]">
|
||||
<view class="w-11 h-11 flex items-center justify-center mr-4">
|
||||
<image src="/static/images/report/sjh.png" alt="手机号" class="w-10 h-10 object-contain" />
|
||||
</view>
|
||||
<view class="flex-1">
|
||||
<view class="font-bold text-gray-800 text-lg">
|
||||
手机号检测结果
|
||||
</view>
|
||||
<view class="text-sm text-[#999999]">
|
||||
被查询人姓名与运营商提供的一致
|
||||
</view>
|
||||
<view class="text-sm text-[#999999]">
|
||||
被查询人身份证与运营商提供的一致
|
||||
</view>
|
||||
</view>
|
||||
<view class="w-11 h-11 flex items-center justify-center ml-4">
|
||||
<image src="/static/images/report/zq.png" alt="资金安全" class="w-10 h-10 object-contain" />
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 组件样式已通过 Tailwind CSS 类实现 */
|
||||
</style>
|
||||
Reference in New Issue
Block a user