787 lines
23 KiB
Vue
787 lines
23 KiB
Vue
<template>
|
||
<view class="payment">
|
||
<view class="payment-content">
|
||
<!-- 缴费金额输入 -->
|
||
<view class="amount-section">
|
||
<view class="section-title">缴费金额</view>
|
||
<view class="amount-input-wrapper">
|
||
<text class="currency">¥</text>
|
||
<input
|
||
class="amount-input"
|
||
type="digit"
|
||
v-model="amount"
|
||
placeholder="请输入缴费金额"
|
||
placeholder-class="placeholder"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 缴费说明 -->
|
||
<view class="remark-section">
|
||
<view class="section-title">缴费说明(选填)</view>
|
||
<textarea
|
||
class="remark-input"
|
||
v-model="remark"
|
||
placeholder="请输入缴费说明"
|
||
placeholder-class="placeholder"
|
||
maxlength="100"
|
||
/>
|
||
<text class="remark-count">{{ remark.length }}/100</text>
|
||
</view>
|
||
|
||
<!-- 支付方式 -->
|
||
<view class="payway-section">
|
||
<view class="section-title">支付方式</view>
|
||
<view class="payway-list">
|
||
<view
|
||
class="payway-item"
|
||
v-for="item in payWayList"
|
||
:key="item.pay_way"
|
||
:class="{ active: selectedPayWay === item.pay_way }"
|
||
@click="selectPayWay(item.pay_way)"
|
||
>
|
||
<view class="payway-info">
|
||
<u-icon
|
||
:name="getPayWayIcon(item.pay_way)"
|
||
size="48"
|
||
:color="getPayWayColor(item.pay_way)"
|
||
/>
|
||
<text class="payway-name">{{ item.name }}</text>
|
||
</view>
|
||
<view class="payway-check">
|
||
<u-icon
|
||
v-if="selectedPayWay === item.pay_way"
|
||
name="checkmark-circle-fill"
|
||
size="40"
|
||
color="#3B82F6"
|
||
/>
|
||
<view v-else class="check-circle"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部提交按钮 -->
|
||
<view class="footer">
|
||
<view class="total-amount">
|
||
<text class="label">合计:</text>
|
||
<text class="currency">¥</text>
|
||
<text class="amount">{{ amount || '0.00' }}</text>
|
||
</view>
|
||
<view class="submit-btn" :class="{ disabled: !canSubmit }" @click="handleSubmit">
|
||
立即缴费
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 支付密码弹窗 -->
|
||
<u-popup v-model="showPasswordPopup" mode="center" border-radius="20">
|
||
<view class="password-popup">
|
||
<view class="popup-title">请输入支付密码</view>
|
||
<u-message-input
|
||
mode="box"
|
||
:maxlength="6"
|
||
:focus="true"
|
||
v-model="password"
|
||
@finish="onPasswordFinish"
|
||
/>
|
||
<view class="popup-cancel" @click="showPasswordPopup = false"> 取消</view>
|
||
</view>
|
||
</u-popup>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { getPayWay, getPayResult, alipayJspay, wechatJspay, bankFeeTradeQuery } from '@/api/pay'
|
||
import { useUserStore } from '@/stores/user'
|
||
import config from '@/config'
|
||
import { onLoad } from '@dcloudio/uni-app'
|
||
|
||
declare const WeixinJSBridge: any
|
||
declare const AlipayJSBridge: any
|
||
|
||
const userStore = useUserStore()
|
||
|
||
// 支付配置
|
||
const paymentConfig = config.payment
|
||
|
||
// 数据
|
||
const amount = ref('')
|
||
const remark = ref('')
|
||
const payWayList = ref<any[]>([])
|
||
const selectedPayWay = ref('')
|
||
const showPasswordPopup = ref(false)
|
||
const password = ref('')
|
||
const studentId = ref<number | null>(null)
|
||
const outTradeNo = ref('')
|
||
|
||
// 计算属性
|
||
const canSubmit = computed(() => {
|
||
const numAmount = parseFloat(amount.value)
|
||
return numAmount > 0 && selectedPayWay.value && !!studentId.value
|
||
})
|
||
|
||
// 获取支付方式 - 使用内置的开放银行支付方式
|
||
const getPayWayData = async () => {
|
||
// 直接设置开放银行支持的支付方式
|
||
payWayList.value = [
|
||
{
|
||
pay_way: 'wechat',
|
||
name: '微信支付'
|
||
},
|
||
{
|
||
pay_way: 'alipay',
|
||
name: '支付宝支付'
|
||
}
|
||
]
|
||
selectedPayWay.value = 'wechat'
|
||
}
|
||
|
||
// 选择支付方式
|
||
const selectPayWay = (payWay: string) => {
|
||
selectedPayWay.value = payWay
|
||
}
|
||
|
||
// 获取支付方式图标
|
||
const getPayWayIcon = (payWay: string) => {
|
||
const iconMap: Record<string, string> = {
|
||
wechat: 'weixin-fill',
|
||
alipay: 'zhifubao-circle-fill',
|
||
balance: 'rmb-circle-fill'
|
||
}
|
||
return iconMap[payWay] || 'rmb-circle-fill'
|
||
}
|
||
|
||
// 获取支付方式颜色
|
||
const getPayWayColor = (payWay: string) => {
|
||
const colorMap: Record<string, string> = {
|
||
wechat: '#07C160',
|
||
alipay: '#1677FF',
|
||
balance: '#FF6B00'
|
||
}
|
||
return colorMap[payWay] || '#999'
|
||
}
|
||
|
||
// 提交缴费
|
||
const handleSubmit = () => {
|
||
if (!canSubmit.value) return
|
||
|
||
const numAmount = parseFloat(amount.value)
|
||
if (numAmount <= 0) {
|
||
uni.showToast({ title: '请输入有效的缴费金额', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
// 余额支付需要输入密码
|
||
if (selectedPayWay.value === 'balance') {
|
||
showPasswordPopup.value = true
|
||
return
|
||
}
|
||
|
||
doPay()
|
||
}
|
||
|
||
// 密码输入完成
|
||
const onPasswordFinish = () => {
|
||
showPasswordPopup.value = false
|
||
doPay()
|
||
}
|
||
|
||
// 执行支付
|
||
const doPay = async () => {
|
||
try {
|
||
uni.showLoading({ title: '支付中...' })
|
||
if (!studentId.value) {
|
||
throw new Error('缺少学生ID,请从预报名详情进入缴费')
|
||
}
|
||
|
||
// 只走开放银行正式接口:由后端生成 out_trade_no(QDKXJG-YBMJF-序号)
|
||
if (selectedPayWay.value === 'wechat') {
|
||
await wechatJspayPay()
|
||
} else if (selectedPayWay.value === 'alipay') {
|
||
await alipayJspayPay()
|
||
} else {
|
||
throw new Error('暂不支持该支付方式')
|
||
}
|
||
} catch (error: any) {
|
||
uni.hideLoading()
|
||
uni.showToast({ title: error.message || '支付失败', icon: 'none' })
|
||
password.value = ''
|
||
}
|
||
}
|
||
|
||
// 微信支付
|
||
const wechatPay = (payParams: any) => {
|
||
return new Promise((resolve, reject) => {
|
||
// #ifdef MP-WEIXIN
|
||
uni.requestPayment({
|
||
provider: 'wxpay',
|
||
...payParams,
|
||
success: () => {
|
||
resolve(true)
|
||
checkPayResult()
|
||
},
|
||
fail: (err: any) => {
|
||
reject(err)
|
||
goToResult(false)
|
||
}
|
||
})
|
||
// #endif
|
||
|
||
// #ifdef H5
|
||
if (payParams.mweb_url) {
|
||
window.location.href = payParams.mweb_url
|
||
} else if (payParams.jsapi_params) {
|
||
// JSAPI支付
|
||
const {
|
||
appId,
|
||
timeStamp,
|
||
nonceStr,
|
||
package: packageStr,
|
||
signType,
|
||
paySign
|
||
} = payParams.jsapi_params
|
||
if (typeof WeixinJSBridge !== 'undefined') {
|
||
WeixinJSBridge.invoke(
|
||
'getBrandWCPayRequest',
|
||
{ appId, timeStamp, nonceStr, package: packageStr, signType, paySign },
|
||
(res: any) => {
|
||
if (res.err_msg === 'get_brand_wcpay_request:ok') {
|
||
resolve(true)
|
||
checkPayResult()
|
||
} else {
|
||
reject(res)
|
||
goToResult(false)
|
||
}
|
||
}
|
||
)
|
||
}
|
||
}
|
||
// #endif
|
||
|
||
// #ifdef APP-PLUS
|
||
uni.requestPayment({
|
||
provider: 'wxpay',
|
||
orderInfo: payParams,
|
||
success: () => {
|
||
resolve(true)
|
||
checkPayResult()
|
||
},
|
||
fail: (err: any) => {
|
||
reject(err)
|
||
goToResult(false)
|
||
}
|
||
})
|
||
// #endif
|
||
})
|
||
}
|
||
|
||
// 支付宝支付
|
||
const alipay = (payParams: any) => {
|
||
return new Promise((resolve, reject) => {
|
||
// #ifdef H5
|
||
if (payParams.form) {
|
||
const div = document.createElement('div')
|
||
div.innerHTML = payParams.form
|
||
document.body.appendChild(div)
|
||
div.querySelector('form')?.submit()
|
||
}
|
||
// #endif
|
||
|
||
// #ifdef APP-PLUS
|
||
uni.requestPayment({
|
||
provider: 'alipay',
|
||
orderInfo: payParams.order_str,
|
||
success: () => {
|
||
resolve(true)
|
||
checkPayResult()
|
||
},
|
||
fail: (err: any) => {
|
||
reject(err)
|
||
goToResult(false)
|
||
}
|
||
})
|
||
// #endif
|
||
})
|
||
}
|
||
// 开放银行支付宝服务窗支付
|
||
const alipayJspayPay = async () => {
|
||
try {
|
||
const res = await alipayJspay({
|
||
amountFen: Math.round(parseFloat(String(amount.value)) * 100),
|
||
body: remark.value || '缴费支付',
|
||
studentId: Number(studentId.value),
|
||
buyerLogonId: '',
|
||
includeRawResponse: false
|
||
})
|
||
|
||
// 检查返回结果(后端 AjaxResult.data 为驼峰字段)
|
||
if (res && res.status === '0' && res.resultCode === '0') {
|
||
outTradeNo.value = res.outTradeNo || ''
|
||
// #ifdef H5
|
||
if (res.payUrl) {
|
||
window.location.href = res.payUrl
|
||
return
|
||
}
|
||
// #endif
|
||
|
||
const payInfo = JSON.parse(res.payInfo || '{}')
|
||
|
||
if (payInfo.tradeNO) {
|
||
// #ifdef APP-PLUS
|
||
uni.requestPayment({
|
||
provider: 'alipay',
|
||
orderInfo: {
|
||
tradeNO: payInfo.tradeNO
|
||
},
|
||
success: () => {
|
||
checkPayResult()
|
||
},
|
||
fail: (err: any) => {
|
||
console.error('支付宝支付失败:', err)
|
||
goToResult(false)
|
||
}
|
||
})
|
||
// #endif
|
||
|
||
// #ifdef H5
|
||
if (typeof AlipayJSBridge !== 'undefined') {
|
||
AlipayJSBridge.call(
|
||
'tradePay',
|
||
{
|
||
tradeNO: payInfo.tradeNO
|
||
},
|
||
(result: any) => {
|
||
if (result.resultCode === '9000') {
|
||
checkPayResult()
|
||
} else {
|
||
goToResult(false)
|
||
}
|
||
}
|
||
)
|
||
}
|
||
// #endif
|
||
} else {
|
||
throw new Error('获取支付参数失败')
|
||
}
|
||
} else {
|
||
throw new Error(res.errMsg || res.message || '支付请求失败')
|
||
}
|
||
} catch (error: any) {
|
||
console.error('开放银行支付错误:', error)
|
||
uni.showToast({ title: error.message || '支付请求失败', icon: 'none' })
|
||
goToResult(false)
|
||
}
|
||
}
|
||
|
||
// 开放银行微信支付(JSPay)
|
||
const wechatJspayPay = async () => {
|
||
try {
|
||
// 走后端正式接口:openid / termId / mchId / notify 均由服务端处理
|
||
const res = await wechatJspay({
|
||
amountFen: Math.round(parseFloat(String(amount.value)) * 100),
|
||
body: remark.value || '缴费支付',
|
||
studentId: Number(studentId.value),
|
||
includeRawResponse: false
|
||
})
|
||
|
||
// 检查返回结果
|
||
if (res && res.status === '0' && res.resultCode === '0') {
|
||
outTradeNo.value = res.outTradeNo || ''
|
||
const payInfo = res.payInfo
|
||
|
||
if (payInfo) {
|
||
// #ifdef MP-WEIXIN
|
||
// 小程序支付 - 解析 XML 格式的 pay_info
|
||
const miniPayParams = parseXmlPayInfo(payInfo)
|
||
if (miniPayParams) {
|
||
;(uni as any).requestPayment({
|
||
provider: 'wxpay',
|
||
...miniPayParams,
|
||
success: () => {
|
||
checkPayResult()
|
||
},
|
||
fail: (err: any) => {
|
||
console.error('微信支付失败:', err)
|
||
goToResult(false)
|
||
}
|
||
})
|
||
} else {
|
||
throw new Error('解析支付参数失败')
|
||
}
|
||
// #endif
|
||
|
||
// #ifdef H5
|
||
// H5 公众号支付 - 解析 XML 格式的 pay_info
|
||
const h5PayParams = parseXmlPayInfo(payInfo)
|
||
if (h5PayParams && typeof WeixinJSBridge !== 'undefined') {
|
||
WeixinJSBridge.invoke('getBrandWCPayRequest', h5PayParams, (res: any) => {
|
||
if (res.err_msg === 'get_brand_wcpay_request:ok') {
|
||
checkPayResult()
|
||
} else {
|
||
goToResult(false)
|
||
}
|
||
})
|
||
} else if (h5PayParams) {
|
||
// 使用 uni.requestPayment
|
||
;(uni as any).requestPayment({
|
||
provider: 'wxpay',
|
||
...h5PayParams,
|
||
success: () => {
|
||
checkPayResult()
|
||
},
|
||
fail: (err: any) => {
|
||
console.error('微信支付失败:', err)
|
||
goToResult(false)
|
||
}
|
||
})
|
||
} else {
|
||
throw new Error('解析支付参数失败')
|
||
}
|
||
// #endif
|
||
|
||
// #ifdef APP-PLUS
|
||
// APP 环境
|
||
uni.requestPayment({
|
||
provider: 'wxpay',
|
||
orderInfo: payInfo,
|
||
success: () => {
|
||
checkPayResult()
|
||
},
|
||
fail: (err: any) => {
|
||
console.error('微信支付失败:', err)
|
||
goToResult(false)
|
||
}
|
||
})
|
||
// #endif
|
||
} else {
|
||
throw new Error('获取支付参数失败')
|
||
}
|
||
} else {
|
||
throw new Error(res.errMsg || res.message || '支付请求失败')
|
||
}
|
||
} catch (error: any) {
|
||
console.error('开放银行微信支付错误:', error)
|
||
uni.showToast({ title: error.message || '支付请求失败', icon: 'none' })
|
||
goToResult(false)
|
||
}
|
||
}
|
||
|
||
// 解析 XML 格式的支付参数
|
||
const parseXmlPayInfo = (xmlStr: string) => {
|
||
try {
|
||
// 简单的 XML 解析
|
||
const getValue = (xml: string, tag: string) => {
|
||
const regex = new RegExp(
|
||
`<${tag}><!\[CDATA\[(.*?)\]\]></${tag}>|<${tag}>(.*?)</${tag}>`,
|
||
'i'
|
||
)
|
||
const match = xml.match(regex)
|
||
return match ? match[1] || match[2] : ''
|
||
}
|
||
|
||
return {
|
||
appId: getValue(xmlStr, 'appId'),
|
||
timeStamp: getValue(xmlStr, 'timeStamp'),
|
||
nonceStr: getValue(xmlStr, 'nonceStr'),
|
||
package: getValue(xmlStr, 'package'),
|
||
signType: getValue(xmlStr, 'signType') || 'RSA',
|
||
paySign: getValue(xmlStr, 'paySign')
|
||
}
|
||
} catch (error) {
|
||
console.error('解析 XML 失败:', error)
|
||
return null
|
||
}
|
||
}
|
||
|
||
// 查询支付结果 - 使用开放银行订单查询接口
|
||
const checkPayResult = async () => {
|
||
try {
|
||
uni.showLoading({ title: '查询中...' })
|
||
if (!outTradeNo.value) {
|
||
throw new Error('缺少 outTradeNo')
|
||
}
|
||
const res = await bankFeeTradeQuery({ outTradeNo: outTradeNo.value })
|
||
|
||
uni.hideLoading()
|
||
|
||
// 检查查询结果
|
||
if (res && res.status === '0' && res.resultCode === '0') {
|
||
// trade_state: SUCCESS-支付成功, REFUND-转入退款, NOTPAY-未支付,
|
||
// CLOSED-已关闭, REVOKED-已撤销, USERPAYING-用户支付中, PAYERROR-支付失败
|
||
const tradeState = res.tradeState
|
||
|
||
if (tradeState === 'SUCCESS') {
|
||
// 支付成功
|
||
goToResult(true)
|
||
} else if (tradeState === 'NOTPAY' || tradeState === 'USERPAYING') {
|
||
// 未支付或支付中,可以轮询查询
|
||
uni.showToast({ title: '支付处理中,请稍后...', icon: 'none' })
|
||
// 3秒后再次查询
|
||
setTimeout(() => {
|
||
checkPayResult()
|
||
}, 3000)
|
||
} else {
|
||
// 其他状态视为失败
|
||
goToResult(false)
|
||
}
|
||
} else {
|
||
// 查询失败,使用备用查询方式
|
||
console.error('开放银行订单查询失败:', res)
|
||
await fallbackCheckPayResult()
|
||
}
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
console.error('查询支付结果错误:', error)
|
||
// 使用备用查询方式
|
||
await fallbackCheckPayResult()
|
||
}
|
||
}
|
||
|
||
// 备用查询方式 - 使用原系统接口
|
||
const fallbackCheckPayResult = async () => {
|
||
try {
|
||
// 兜底:仍然用开放银行查单
|
||
if (!outTradeNo.value) return goToResult(false)
|
||
const res = await bankFeeTradeQuery({ outTradeNo: outTradeNo.value })
|
||
if (res && res.status === '0' && res.resultCode === '0' && res.tradeState === 'SUCCESS')
|
||
return goToResult(true)
|
||
return goToResult(false)
|
||
} catch (error) {
|
||
goToResult(false)
|
||
}
|
||
}
|
||
|
||
// 跳转到结果页
|
||
const goToResult = (success: boolean) => {
|
||
uni.redirectTo({
|
||
url: `/pages/payment_result/payment_result?status=${success ? 'success' : 'fail'}&id=${
|
||
outTradeNo.value
|
||
}&from=bankFee`
|
||
})
|
||
}
|
||
|
||
onMounted(() => {
|
||
getPayWayData()
|
||
})
|
||
|
||
onLoad((options: any) => {
|
||
const sid = Number(options?.studentId || 0)
|
||
if (sid > 0) {
|
||
studentId.value = sid
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.payment {
|
||
min-height: 100vh;
|
||
background-color: #f5f7fa;
|
||
padding-bottom: calc(180rpx + env(safe-area-inset-bottom));
|
||
}
|
||
|
||
.payment-content {
|
||
padding: 20rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
// 金额输入区域
|
||
.amount-section {
|
||
background: #ffffff;
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
|
||
.amount-input-wrapper {
|
||
display: flex;
|
||
align-items: center;
|
||
border-bottom: 2rpx solid #e5e7eb;
|
||
padding: 20rpx 0;
|
||
|
||
.currency {
|
||
font-size: 60rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.amount-input {
|
||
flex: 1;
|
||
font-size: 60rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
height: 80rpx;
|
||
}
|
||
|
||
.placeholder {
|
||
font-size: 40rpx;
|
||
color: #999;
|
||
font-weight: normal;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 备注区域
|
||
.remark-section {
|
||
background: #ffffff;
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
position: relative;
|
||
|
||
.remark-input {
|
||
width: 100%;
|
||
height: 160rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.placeholder {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.remark-count {
|
||
position: absolute;
|
||
right: 30rpx;
|
||
bottom: 20rpx;
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
// 支付方式区域
|
||
.payway-section {
|
||
background: #ffffff;
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
|
||
.payway-list {
|
||
.payway-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 30rpx 0;
|
||
border-bottom: 2rpx solid #f3f4f6;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
&.active {
|
||
.payway-name {
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.payway-info {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.payway-name {
|
||
font-size: 30rpx;
|
||
color: #666;
|
||
margin-left: 20rpx;
|
||
}
|
||
}
|
||
|
||
.payway-check {
|
||
.check-circle {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
border: 2rpx solid #d1d5db;
|
||
border-radius: 50%;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 底部区域
|
||
.footer {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: #ffffff;
|
||
padding: 20rpx 30rpx;
|
||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
|
||
.total-amount {
|
||
display: flex;
|
||
align-items: baseline;
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.currency {
|
||
font-size: 32rpx;
|
||
color: #ff6b00;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.amount {
|
||
font-size: 48rpx;
|
||
color: #ff6b00;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
|
||
.submit-btn {
|
||
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
||
color: #ffffff;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
padding: 24rpx 60rpx;
|
||
border-radius: 40rpx;
|
||
|
||
&:active {
|
||
opacity: 0.9;
|
||
}
|
||
|
||
&.disabled {
|
||
background: #d1d5db;
|
||
color: #9ca3af;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 密码弹窗
|
||
.password-popup {
|
||
width: 600rpx;
|
||
padding: 40rpx;
|
||
background: #ffffff;
|
||
border-radius: 20rpx;
|
||
|
||
.popup-title {
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
color: #333;
|
||
text-align: center;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.popup-cancel {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
text-align: center;
|
||
margin-top: 40rpx;
|
||
padding: 20rpx;
|
||
}
|
||
}
|
||
</style>
|