1214 lines
29 KiB
Vue
1214 lines
29 KiB
Vue
<template>
|
||
<view class="realname-page">
|
||
<!-- 页面加载状态 -->
|
||
<Loading type="page" :loading="pageLoading" />
|
||
|
||
<!-- 顶部背景图 -->
|
||
<view class="top-bg">
|
||
<image src="/static/title_bg.png" mode="aspectFill" class="bg-img" />
|
||
</view>
|
||
|
||
<!-- 自定义导航栏 -->
|
||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||
<view class="navbar-content">
|
||
<view class="navbar-back" @click="handleBack">
|
||
<text class="back-icon">‹</text>
|
||
</view>
|
||
<text class="navbar-title">实名认证</text>
|
||
<view class="navbar-placeholder"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 已认证状态 (Requirements 12.4) -->
|
||
<view v-if="isVerified" class="verified-section" :style="{ paddingTop: (statusBarHeight + 44) + 'px' }">
|
||
<view class="verified-icon">✓</view>
|
||
<view class="verified-title">实名认证已完成</view>
|
||
<view class="verified-info">
|
||
<view class="info-item">
|
||
<text class="label">姓名:</text>
|
||
<text class="value">{{ maskedName }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">身份证号:</text>
|
||
<text class="value">{{ maskedIdNumber }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="verified-badge">
|
||
<text class="badge-icon">🛡️</text>
|
||
<text class="badge-text">已实名认证</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 未认证状态 - 分步骤显示 -->
|
||
<view v-else class="unverified-section">
|
||
<!-- 固定的步骤指示器 -->
|
||
<view class="step-header-fixed" :style="{ top: (statusBarHeight + 44) + 'px' }">
|
||
<view class="step-indicator">
|
||
<view class="step-item" :class="{ active: currentStep === 1, completed: currentStep > 1 }">
|
||
<view class="step-num">{{ currentStep > 1 ? '✓' : '1' }}</view>
|
||
<text class="step-text">支付费用</text>
|
||
</view>
|
||
<view class="step-line" :class="{ completed: currentStep > 1 }"></view>
|
||
<view class="step-item" :class="{ active: currentStep === 2, completed: currentStep > 2 }">
|
||
<view class="step-num">{{ currentStep > 2 ? '✓' : '2' }}</view>
|
||
<text class="step-text">上传证件</text>
|
||
</view>
|
||
<view class="step-line" :class="{ completed: currentStep > 2 }"></view>
|
||
<view class="step-item" :class="{ active: currentStep === 3 }">
|
||
<view class="step-num">3</view>
|
||
<text class="step-text">认证完成</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 可滚动内容区域 -->
|
||
<scroll-view
|
||
class="content-scroll"
|
||
scroll-y
|
||
:style="{
|
||
top: (statusBarHeight + 44 + 100) + 'px',
|
||
height: 'calc(100vh - ' + (statusBarHeight + 44 + 100 + 120) + 'px)'
|
||
}"
|
||
>
|
||
<!-- 步骤1: 支付页面 (Requirements 12.1) -->
|
||
<view v-if="currentStep === 1" class="step-payment">
|
||
<view class="payment-content">
|
||
<view class="payment-icon">🛡️</view>
|
||
<view class="payment-title">实名认证</view>
|
||
<view class="payment-desc">完成实名认证,提升信任度,获得更多关注</view>
|
||
|
||
<view class="payment-benefits">
|
||
<view class="benefit-item">
|
||
<text class="benefit-icon">✓</text>
|
||
<text class="benefit-text">展示"已实名"徽章</text>
|
||
</view>
|
||
<view class="benefit-item">
|
||
<text class="benefit-icon">✓</text>
|
||
<text class="benefit-text">提升资料可信度</text>
|
||
</view>
|
||
<view class="benefit-item">
|
||
<text class="benefit-icon">✓</text>
|
||
<text class="benefit-text">获得更多用户关注</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="payment-price">
|
||
<text class="price-label">认证费用</text>
|
||
<view class="price-value">
|
||
<text class="symbol">¥</text>
|
||
<text class="amount">{{ verificationFee }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 步骤2: 身份证上传页面 (Requirements 12.2, 12.3) -->
|
||
<view v-if="currentStep === 2" class="step-upload">
|
||
<view class="upload-content">
|
||
<view class="upload-title">请上传身份证照片</view>
|
||
<view class="upload-desc">请确保照片清晰、完整,信息可辨认</view>
|
||
|
||
<!-- 身份证正面 -->
|
||
<view class="upload-card">
|
||
<view class="card-label">身份证人像面</view>
|
||
<view
|
||
class="card-upload"
|
||
:class="{ 'has-image': idCardFront }"
|
||
@click="chooseIdCardFront"
|
||
>
|
||
<image
|
||
v-if="idCardFront"
|
||
:src="idCardFront"
|
||
mode="aspectFill"
|
||
class="card-image"
|
||
/>
|
||
<view v-else class="card-placeholder">
|
||
<text class="placeholder-icon">📷</text>
|
||
<text class="placeholder-text">点击上传</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 身份证反面 -->
|
||
<view class="upload-card">
|
||
<view class="card-label">身份证国徽面</view>
|
||
<view
|
||
class="card-upload"
|
||
:class="{ 'has-image': idCardBack }"
|
||
@click="chooseIdCardBack"
|
||
>
|
||
<image
|
||
v-if="idCardBack"
|
||
:src="idCardBack"
|
||
mode="aspectFill"
|
||
class="card-image"
|
||
/>
|
||
<view v-else class="card-placeholder">
|
||
<text class="placeholder-icon">📷</text>
|
||
<text class="placeholder-text">点击上传</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="upload-tips">
|
||
<view class="tips-title">温馨提示:</view>
|
||
<view class="tips-item">• 请上传本人有效身份证照片</view>
|
||
<view class="tips-item">• 照片需清晰可辨,无遮挡</view>
|
||
<view class="tips-item">• 您的信息将被严格保密</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 步骤3: 认证结果页面 (Requirements 12.4) -->
|
||
<view v-if="currentStep === 3" class="step-result">
|
||
<view class="result-content">
|
||
<view class="result-icon success">✓</view>
|
||
<view class="result-title">实名认证成功</view>
|
||
<view class="result-desc">您的身份信息已通过验证</view>
|
||
|
||
<view class="result-info">
|
||
<view class="info-item">
|
||
<text class="label">姓名:</text>
|
||
<text class="value">{{ maskedName }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">身份证号:</text>
|
||
<text class="value">{{ maskedIdNumber }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 底部操作按钮 -->
|
||
<view class="bottom-action">
|
||
<button
|
||
v-if="currentStep === 1"
|
||
class="btn-pay"
|
||
:disabled="paying"
|
||
@click="handlePayment"
|
||
>
|
||
<text v-if="paying">支付中...</text>
|
||
<text v-else>立即支付 ¥{{ verificationFee }}</text>
|
||
</button>
|
||
|
||
<button
|
||
v-if="currentStep === 2"
|
||
class="btn-submit"
|
||
:disabled="!canSubmit || submitting"
|
||
@click="handleSubmitVerification"
|
||
>
|
||
<text v-if="submitting">提交中...</text>
|
||
<text v-else>提交认证</text>
|
||
</button>
|
||
|
||
<button
|
||
v-if="currentStep === 3"
|
||
class="btn-done"
|
||
@click="handleDone"
|
||
>
|
||
完成
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { useUserStore } from '@/store/user.js'
|
||
import { createOrder } from '@/api/order.js'
|
||
import { maskName, maskIdNumber } from '@/utils/format.js'
|
||
import { get } from '@/api/request.js'
|
||
import { getToken } from '@/utils/storage.js'
|
||
import Loading from '@/components/Loading/index.vue'
|
||
import config from '@/config/index.js'
|
||
|
||
// 从统一配置获取 API 基础地址
|
||
const BASE_URL = config.API_BASE_URL
|
||
|
||
export default {
|
||
name: 'RealnamePage',
|
||
components: {
|
||
Loading
|
||
},
|
||
setup() {
|
||
const userStore = useUserStore()
|
||
|
||
// 状态栏高度
|
||
const statusBarHeight = ref(20)
|
||
|
||
// 页面状态
|
||
const pageLoading = ref(true)
|
||
const paying = ref(false)
|
||
const submitting = ref(false)
|
||
const currentStep = ref(1) // 1: 支付, 2: 上传, 3: 结果
|
||
|
||
// 认证费用 (Requirements 12.1)
|
||
const verificationFee = ref(88)
|
||
|
||
// 身份证照片
|
||
const idCardFront = ref('')
|
||
const idCardBack = ref('')
|
||
|
||
// 认证结果
|
||
const verificationResult = ref(null)
|
||
|
||
// 获取系统信息
|
||
const getSystemInfo = () => {
|
||
uni.getSystemInfo({
|
||
success: (res) => {
|
||
statusBarHeight.value = res.statusBarHeight || 20
|
||
}
|
||
})
|
||
}
|
||
|
||
// 返回上一页
|
||
const handleBack = () => {
|
||
uni.navigateBack()
|
||
}
|
||
|
||
// 计算属性
|
||
const isVerified = computed(() => userStore.isRealName)
|
||
|
||
const canSubmit = computed(() => {
|
||
return idCardFront.value && idCardBack.value
|
||
})
|
||
|
||
const maskedName = computed(() => {
|
||
if (verificationResult.value?.name) {
|
||
return maskName(verificationResult.value.name)
|
||
}
|
||
return '***'
|
||
})
|
||
|
||
const maskedIdNumber = computed(() => {
|
||
if (verificationResult.value?.idNumber) {
|
||
return maskIdNumber(verificationResult.value.idNumber)
|
||
}
|
||
return '***************'
|
||
})
|
||
|
||
/**
|
||
* 获取实名认证状态
|
||
*/
|
||
const getRealNameStatus = async () => {
|
||
try {
|
||
const res = await get('/realname/status')
|
||
if (res && res.success && res.data) {
|
||
if (res.data.isVerified) {
|
||
userStore.setRealNameStatus(true)
|
||
verificationResult.value = {
|
||
name: res.data.name,
|
||
idNumber: res.data.idNumber
|
||
}
|
||
} else if (res.data.isPaid) {
|
||
// 已支付但未完成上传
|
||
currentStep.value = 2
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取实名状态失败:', error)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 初始化页面
|
||
*/
|
||
const initPage = async () => {
|
||
pageLoading.value = true
|
||
try {
|
||
getSystemInfo()
|
||
userStore.restoreFromStorage()
|
||
await getRealNameStatus()
|
||
} catch (error) {
|
||
console.error('初始化页面失败:', error)
|
||
} finally {
|
||
pageLoading.value = false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理支付 (Requirements 12.1, 12.2)
|
||
*/
|
||
const handlePayment = async () => {
|
||
if (paying.value) return
|
||
|
||
paying.value = true
|
||
|
||
try {
|
||
// 创建实名认证订单
|
||
const orderRes = await createOrder({
|
||
orderType: 2 // 实名认证订单
|
||
})
|
||
|
||
if (!orderRes || !orderRes.success) {
|
||
uni.showToast({
|
||
title: orderRes?.message || '创建订单失败',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
const paymentParams = orderRes.data
|
||
|
||
// 调用微信支付
|
||
await requestPayment(paymentParams)
|
||
|
||
// 支付成功,进入上传步骤 (Requirements 12.2)
|
||
currentStep.value = 2
|
||
|
||
uni.showToast({
|
||
title: '支付成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
} catch (error) {
|
||
console.error('支付失败:', error)
|
||
|
||
if (error.errMsg && error.errMsg.includes('cancel')) {
|
||
uni.showToast({ title: '支付已取消', icon: 'none' })
|
||
} else {
|
||
uni.showToast({
|
||
title: error.message || '支付失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} finally {
|
||
paying.value = false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 调用微信支付
|
||
*/
|
||
const requestPayment = (params) => {
|
||
return new Promise((resolve, reject) => {
|
||
uni.requestPayment({
|
||
provider: 'wxpay',
|
||
timeStamp: params.timeStamp,
|
||
nonceStr: params.nonceStr,
|
||
package: params.package,
|
||
signType: params.signType || 'MD5',
|
||
paySign: params.paySign,
|
||
success: resolve,
|
||
fail: reject
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 选择身份证正面照片
|
||
*/
|
||
const chooseIdCardFront = () => {
|
||
uni.chooseImage({
|
||
count: 1,
|
||
sizeType: ['compressed'],
|
||
sourceType: ['album', 'camera'],
|
||
success: (res) => {
|
||
idCardFront.value = res.tempFilePaths[0]
|
||
}
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 选择身份证反面照片
|
||
*/
|
||
const chooseIdCardBack = () => {
|
||
uni.chooseImage({
|
||
count: 1,
|
||
sizeType: ['compressed'],
|
||
sourceType: ['album', 'camera'],
|
||
success: (res) => {
|
||
idCardBack.value = res.tempFilePaths[0]
|
||
}
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 提交实名认证 (Requirements 12.3)
|
||
*/
|
||
const handleSubmitVerification = async () => {
|
||
if (!canSubmit.value || submitting.value) return
|
||
|
||
submitting.value = true
|
||
|
||
try {
|
||
// 上传身份证照片并提交认证
|
||
const result = await uploadIdCards()
|
||
|
||
if (result && result.success) {
|
||
// 认证成功,更新状态
|
||
userStore.setRealNameStatus(true)
|
||
verificationResult.value = {
|
||
name: result.data.name,
|
||
idNumber: result.data.idNumber
|
||
}
|
||
|
||
// 进入结果页面 (Requirements 12.4)
|
||
currentStep.value = 3
|
||
|
||
uni.showToast({
|
||
title: '认证成功',
|
||
icon: 'success'
|
||
})
|
||
} else {
|
||
uni.showToast({
|
||
title: result?.message || '认证失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.error('提交认证失败:', error)
|
||
uni.showToast({
|
||
title: error.message || '认证失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
submitting.value = false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 上传身份证照片
|
||
*/
|
||
const uploadIdCards = () => {
|
||
return new Promise((resolve, reject) => {
|
||
const token = getToken()
|
||
|
||
// 先上传正面
|
||
uni.uploadFile({
|
||
url: `${BASE_URL}/realname/verify`,
|
||
filePath: idCardFront.value,
|
||
name: 'frontImage',
|
||
formData: {
|
||
backImagePath: idCardBack.value
|
||
},
|
||
header: {
|
||
'Authorization': `Bearer ${token}`
|
||
},
|
||
success: async (uploadRes) => {
|
||
if (uploadRes.statusCode === 200) {
|
||
try {
|
||
const data = JSON.parse(uploadRes.data)
|
||
|
||
// 如果需要单独上传反面,再上传
|
||
if (data.needBackImage) {
|
||
const backResult = await uploadBackImage(token)
|
||
resolve(backResult)
|
||
} else {
|
||
resolve(data)
|
||
}
|
||
} catch (e) {
|
||
reject(new Error('解析响应失败'))
|
||
}
|
||
} else if (uploadRes.statusCode === 401) {
|
||
reject(new Error('未授权,请重新登录'))
|
||
} else {
|
||
try {
|
||
const errorData = JSON.parse(uploadRes.data)
|
||
reject(new Error(errorData.message || '认证失败'))
|
||
} catch (e) {
|
||
reject(new Error('认证失败'))
|
||
}
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
reject(new Error('网络连接失败'))
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 上传身份证反面
|
||
*/
|
||
const uploadBackImage = (token) => {
|
||
return new Promise((resolve, reject) => {
|
||
uni.uploadFile({
|
||
url: `${BASE_URL}/realname/verify/back`,
|
||
filePath: idCardBack.value,
|
||
name: 'backImage',
|
||
header: {
|
||
'Authorization': `Bearer ${token}`
|
||
},
|
||
success: (uploadRes) => {
|
||
if (uploadRes.statusCode === 200) {
|
||
try {
|
||
const data = JSON.parse(uploadRes.data)
|
||
resolve(data)
|
||
} catch (e) {
|
||
reject(new Error('解析响应失败'))
|
||
}
|
||
} else {
|
||
reject(new Error('上传失败'))
|
||
}
|
||
},
|
||
fail: () => {
|
||
reject(new Error('网络连接失败'))
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 完成认证,返回上一页
|
||
*/
|
||
const handleDone = () => {
|
||
uni.navigateBack()
|
||
}
|
||
|
||
onMounted(() => {
|
||
initPage()
|
||
})
|
||
|
||
return {
|
||
statusBarHeight,
|
||
pageLoading,
|
||
paying,
|
||
submitting,
|
||
currentStep,
|
||
verificationFee,
|
||
idCardFront,
|
||
idCardBack,
|
||
isVerified,
|
||
canSubmit,
|
||
maskedName,
|
||
maskedIdNumber,
|
||
handleBack,
|
||
handlePayment,
|
||
chooseIdCardFront,
|
||
chooseIdCardBack,
|
||
handleSubmitVerification,
|
||
handleDone
|
||
}
|
||
},
|
||
onShow() {
|
||
const userStore = useUserStore()
|
||
userStore.restoreFromStorage()
|
||
}
|
||
}
|
||
</script>
|
||
|
||
|
||
<style lang="scss" scoped>
|
||
.realname-page {
|
||
height: 100vh;
|
||
background-color: #f8f8f8;
|
||
}
|
||
|
||
// 顶部背景图
|
||
.top-bg {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 400rpx;
|
||
z-index: 0;
|
||
|
||
.bg-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
// 自定义导航栏
|
||
.custom-navbar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 200;
|
||
|
||
.navbar-content {
|
||
position: relative;
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 24rpx;
|
||
|
||
.navbar-back {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.back-icon {
|
||
font-size: 56rpx;
|
||
color: #333;
|
||
font-weight: 300;
|
||
}
|
||
}
|
||
|
||
.navbar-title {
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.navbar-placeholder {
|
||
width: 80rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 已认证状态
|
||
.verified-section {
|
||
padding: 60rpx 30rpx;
|
||
padding-top: 60rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.verified-icon {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 60rpx;
|
||
color: #fff;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.verified-title {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.verified-info {
|
||
width: 100%;
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 30rpx;
|
||
|
||
.info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 20rpx 0;
|
||
border-bottom: 1rpx solid #f5f5f5;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
width: 160rpx;
|
||
}
|
||
|
||
.value {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
|
||
.verified-badge {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 16rpx 32rpx;
|
||
background: linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%);
|
||
border-radius: 40rpx;
|
||
|
||
.badge-icon {
|
||
font-size: 32rpx;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
.badge-text {
|
||
font-size: 26rpx;
|
||
color: #1890ff;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 未认证状态
|
||
.unverified-section {
|
||
height: 100vh;
|
||
position: relative;
|
||
z-index: 1;
|
||
overflow: hidden;
|
||
}
|
||
|
||
// 可滚动内容区域
|
||
.content-scroll {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
background: transparent;
|
||
}
|
||
|
||
// 步骤内容区域
|
||
.step-payment,
|
||
.step-upload,
|
||
.step-result {
|
||
padding: 0;
|
||
}
|
||
|
||
// 固定的步骤指示器
|
||
.step-header-fixed {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 99;
|
||
padding: 20rpx 30rpx;
|
||
background: transparent;
|
||
|
||
.step-indicator {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #fff;
|
||
border-radius: 20rpx;
|
||
padding: 24rpx 20rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||
|
||
.step-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.step-num {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
border-radius: 50%;
|
||
background: #e0e0e0;
|
||
color: #999;
|
||
font-size: 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.step-text {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
}
|
||
|
||
&.active {
|
||
.step-num {
|
||
background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
|
||
color: #fff;
|
||
}
|
||
|
||
.step-text {
|
||
color: #ff6b6b;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
&.completed {
|
||
.step-num {
|
||
background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
|
||
color: #fff;
|
||
}
|
||
|
||
.step-text {
|
||
color: #52c41a;
|
||
}
|
||
}
|
||
}
|
||
|
||
.step-line {
|
||
width: 80rpx;
|
||
height: 4rpx;
|
||
background: #e0e0e0;
|
||
margin: 0 16rpx;
|
||
margin-bottom: 30rpx;
|
||
|
||
&.completed {
|
||
background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 步骤指示器(保留旧样式兼容)
|
||
.step-header {
|
||
padding: 30rpx;
|
||
padding-top: 20rpx;
|
||
|
||
.step-indicator {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #fff;
|
||
border-radius: 20rpx;
|
||
padding: 24rpx 20rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||
|
||
.step-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.step-num {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
border-radius: 50%;
|
||
background: #e0e0e0;
|
||
color: #999;
|
||
font-size: 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.step-text {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
}
|
||
|
||
&.active {
|
||
.step-num {
|
||
background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
|
||
color: #fff;
|
||
}
|
||
|
||
.step-text {
|
||
color: #52c41a;
|
||
}
|
||
}
|
||
}
|
||
|
||
.step-line {
|
||
width: 80rpx;
|
||
height: 4rpx;
|
||
background: #e0e0e0;
|
||
margin: 0 16rpx;
|
||
margin-bottom: 30rpx;
|
||
|
||
&.completed {
|
||
background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 步骤1: 支付页面
|
||
.step-payment {
|
||
.payment-content {
|
||
padding: 40rpx 30rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.payment-icon {
|
||
font-size: 100rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.payment-title {
|
||
font-size: 40rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.payment-desc {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.payment-benefits {
|
||
width: 100%;
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 40rpx;
|
||
|
||
.benefit-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 16rpx 0;
|
||
|
||
.benefit-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
background: #52c41a;
|
||
border-radius: 50%;
|
||
color: #fff;
|
||
font-size: 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.benefit-text {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
}
|
||
}
|
||
|
||
.payment-price {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.price-label {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.price-value {
|
||
display: flex;
|
||
align-items: baseline;
|
||
|
||
.symbol {
|
||
font-size: 32rpx;
|
||
color: #ff6b6b;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.amount {
|
||
font-size: 72rpx;
|
||
color: #ff6b6b;
|
||
font-weight: 700;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 步骤2: 上传页面
|
||
.step-upload {
|
||
.upload-content {
|
||
padding: 30rpx;
|
||
|
||
.upload-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
text-align: center;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.upload-desc {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
text-align: center;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.upload-card {
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 24rpx;
|
||
|
||
.card-label {
|
||
font-size: 28rpx;
|
||
font-weight: 500;
|
||
color: #333;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.card-upload {
|
||
width: 100%;
|
||
height: 320rpx;
|
||
background: #f8f8f8;
|
||
border: 2rpx dashed #ddd;
|
||
border-radius: 12rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: hidden;
|
||
|
||
&.has-image {
|
||
border-style: solid;
|
||
border-color: #52c41a;
|
||
}
|
||
|
||
.card-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.card-placeholder {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.placeholder-icon {
|
||
font-size: 60rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.placeholder-text {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.upload-tips {
|
||
background: #fffbe6;
|
||
border-radius: 12rpx;
|
||
padding: 24rpx;
|
||
margin-top: 20rpx;
|
||
|
||
.tips-title {
|
||
font-size: 26rpx;
|
||
font-weight: 500;
|
||
color: #d48806;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.tips-item {
|
||
font-size: 24rpx;
|
||
color: #d48806;
|
||
line-height: 1.8;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 步骤3: 结果页面
|
||
.step-result {
|
||
.result-content {
|
||
padding: 60rpx 30rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.result-icon {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 60rpx;
|
||
color: #fff;
|
||
margin-bottom: 30rpx;
|
||
|
||
&.success {
|
||
background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
|
||
}
|
||
}
|
||
|
||
.result-title {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.result-desc {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.result-info {
|
||
width: 100%;
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 30rpx;
|
||
|
||
.info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 20rpx 0;
|
||
border-bottom: 1rpx solid #f5f5f5;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
width: 160rpx;
|
||
}
|
||
|
||
.value {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 底部操作按钮
|
||
.bottom-action {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: #fff;
|
||
padding: 20rpx 30rpx;
|
||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
|
||
.btn-pay,
|
||
.btn-submit,
|
||
.btn-done {
|
||
width: 100%;
|
||
height: 96rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 48rpx;
|
||
border: none;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
|
||
text {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
&[disabled] {
|
||
opacity: 0.6;
|
||
}
|
||
}
|
||
|
||
.btn-pay {
|
||
background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
|
||
|
||
text {
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
.btn-submit {
|
||
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
|
||
|
||
text {
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
.btn-done {
|
||
background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
|
||
|
||
text {
|
||
color: #fff;
|
||
}
|
||
}
|
||
}
|
||
</style>
|