835 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			835 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Vue
		
	
	
	
| <!--
 | ||
|  * @Descripttion: 自助购票页面
 | ||
|  * @version: 1.0.0
 | ||
|  * @Author: Assistant
 | ||
|  * @Date: 2025-01-22
 | ||
| -->
 | ||
| <template>
 | ||
|   <view class="buy-ticket-container">
 | ||
|     <!-- 内容区域 -->
 | ||
|     <view class="content-area">
 | ||
|       <!-- 提示信息 -->
 | ||
|       <view class="tip-banner">
 | ||
|         <view class="tip-icon">
 | ||
|           <u-icon name="info-circle" size="16" color="#f56c6c"></u-icon>
 | ||
|         </view>
 | ||
|         <text class="tip-text">自助购票请填写以下信息:</text>
 | ||
|       </view>
 | ||
| 
 | ||
|       <!-- 购票人信息列表 -->
 | ||
|       <view class="buyer-list">
 | ||
|         <view
 | ||
|           class="buyer-item"
 | ||
|           v-for="(buyer, index) in buyerList"
 | ||
|           :key="index"
 | ||
|         >
 | ||
|           <view class="buyer-header">
 | ||
|             <text class="buyer-title">购票人信息{{ index + 1 }}</text>
 | ||
|             <view
 | ||
|               class="delete-btn"
 | ||
|               v-if="buyerList.length > 1"
 | ||
|               @click="removeBuyer(index)"
 | ||
|             >
 | ||
|               <u-icon name="trash" size="16" color="#999"></u-icon>
 | ||
|             </view>
 | ||
|           </view>
 | ||
| 
 | ||
|           <view class="form-section">
 | ||
|             <!-- 姓名 -->
 | ||
|             <view class="form-item">
 | ||
|               <view class="label required">
 | ||
|                 <text>姓名</text>
 | ||
|               </view>
 | ||
|               <view class="input-wrapper">
 | ||
|                 <input
 | ||
|                   class="form-input"
 | ||
|                   v-model="buyer.name"
 | ||
|                   placeholder="请输入"
 | ||
|                   maxlength="20"
 | ||
|                 />
 | ||
|               </view>
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- 联系方式 -->
 | ||
|             <view class="form-item">
 | ||
|               <view class="label required">
 | ||
|                 <text>联系方式</text>
 | ||
|               </view>
 | ||
|               <view class="input-wrapper">
 | ||
|                 <input
 | ||
|                   class="form-input"
 | ||
|                   v-model="buyer.phone"
 | ||
|                   placeholder="请输入"
 | ||
|                   type="number"
 | ||
|                   maxlength="11"
 | ||
|                 />
 | ||
|               </view>
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- 证件号码 -->
 | ||
|             <view class="form-item">
 | ||
|               <view class="label required">
 | ||
|                 <text>证件号码</text>
 | ||
|               </view>
 | ||
|               <view class="input-wrapper">
 | ||
|                 <input
 | ||
|                   class="form-input"
 | ||
|                   v-model="buyer.idCard"
 | ||
|                   placeholder="请输入"
 | ||
|                   maxlength="18"
 | ||
|                 />
 | ||
|               </view>
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- 性别 -->
 | ||
|             <view class="form-item">
 | ||
|               <view class="label required">
 | ||
|                 <text>性别</text>
 | ||
|               </view>
 | ||
|               <view class="input-wrapper">
 | ||
|                 <picker
 | ||
|                   :value="buyer.sexIndex"
 | ||
|                   :range="sexOptions"
 | ||
|                   @change="onSexChange($event, index)"
 | ||
|                 >
 | ||
|                   <view class="picker-input">
 | ||
|                     <text
 | ||
|                       class="picker-text"
 | ||
|                       :class="{ placeholder: buyer.sexIndex === -1 }"
 | ||
|                     >
 | ||
|                       {{
 | ||
|                         buyer.sexIndex !== -1
 | ||
|                           ? sexOptions[buyer.sexIndex]
 | ||
|                           : '请选择'
 | ||
|                       }}
 | ||
|                     </text>
 | ||
|                     <u-icon name="arrow-down" size="12" color="#999"></u-icon>
 | ||
|                   </view>
 | ||
|                 </picker>
 | ||
|               </view>
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- 服装尺寸 -->
 | ||
|             <view class="form-item">
 | ||
|               <view class="label required">
 | ||
|                 <text>服装尺寸</text>
 | ||
|               </view>
 | ||
|               <view class="input-wrapper">
 | ||
|                 <input
 | ||
|                   class="form-input"
 | ||
|                   v-model="buyer.clothSize"
 | ||
|                   placeholder="请输入"
 | ||
|                   maxlength="10"
 | ||
|                 />
 | ||
|               </view>
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- 同住人 -->
 | ||
|             <view class="form-item">
 | ||
|               <view class="label">
 | ||
|                 <text>同住人</text>
 | ||
|               </view>
 | ||
|               <view class="input-wrapper">
 | ||
|                 <input
 | ||
|                   class="form-input"
 | ||
|                   v-model="buyer.cohabitant"
 | ||
|                   placeholder="请输入"
 | ||
|                   maxlength="20"
 | ||
|                 />
 | ||
|               </view>
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- 紧急联系方式 -->
 | ||
|             <view class="form-item">
 | ||
|               <view class="label required">
 | ||
|                 <text>紧急联系方式</text>
 | ||
|               </view>
 | ||
|               <view class="input-wrapper">
 | ||
|                 <input
 | ||
|                   class="form-input"
 | ||
|                   v-model="buyer.emergencyPhone"
 | ||
|                   placeholder="请输入"
 | ||
|                   type="number"
 | ||
|                   maxlength="11"
 | ||
|                 />
 | ||
|               </view>
 | ||
|             </view>
 | ||
|           </view>
 | ||
|         </view>
 | ||
|       </view>
 | ||
| 
 | ||
|       <!-- 添加购票人按钮 -->
 | ||
|       <view class="add-buyer-btn" @click="addBuyer">
 | ||
|         <u-icon name="plus" size="16" color="#666"></u-icon>
 | ||
|         <text class="add-text">继续添加购票人</text>
 | ||
|       </view>
 | ||
| 
 | ||
|       <!-- 提交按钮 -->
 | ||
|       <view class="submit-wrapper">
 | ||
|         <button
 | ||
|           class="submit-btn"
 | ||
|           :class="{ disabled: submitting }"
 | ||
|           :disabled="submitting"
 | ||
|           @click="handleSubmit"
 | ||
|         >
 | ||
|           {{ submitting ? '提交中...' : '提交' }}
 | ||
|         </button>
 | ||
|       </view>
 | ||
|     </view>
 | ||
| 
 | ||
|     <!-- 支付密码弹窗 -->
 | ||
|     <u-popup
 | ||
|       :show="showPayPwdModal"
 | ||
|       mode="center"
 | ||
|       border-radius="16"
 | ||
|       width="320px"
 | ||
|       height="auto"
 | ||
|     >
 | ||
|       <view class="pay-pwd-modal">
 | ||
|         <view class="modal-header">
 | ||
|           <view class="header-icon">
 | ||
|             <u-icon name="lock" size="24" color="#005bac"></u-icon>
 | ||
|           </view>
 | ||
|           <text class="modal-title">输入支付密码</text>
 | ||
|           <text class="modal-subtitle">请输入您的支付密码以完成购票</text>
 | ||
|         </view>
 | ||
|         <view class="modal-body">
 | ||
|           <view class="pwd-input-wrapper">
 | ||
|             <input
 | ||
|               class="pwd-input"
 | ||
|               v-model="payPassword"
 | ||
|               placeholder="请输入支付密码"
 | ||
|               type="password"
 | ||
|               @input="onPayPwdInput"
 | ||
|               @focus="onPwdFocus"
 | ||
|               @blur="onPwdBlur"
 | ||
|             />
 | ||
|             <view
 | ||
|               class="input-border"
 | ||
|               :class="{ focused: pwdInputFocused }"
 | ||
|             ></view>
 | ||
|           </view>
 | ||
|         </view>
 | ||
|         <view class="modal-footer">
 | ||
|           <view class="btn-group">
 | ||
|             <button class="modal-btn cancel-btn" @click="closePayPwdModal">
 | ||
|               取消
 | ||
|             </button>
 | ||
|             <button
 | ||
|               class="modal-btn confirm-btn"
 | ||
|               :class="{ disabled: !isPayPwdValid }"
 | ||
|               :disabled="!isPayPwdValid"
 | ||
|               @click="confirmPayPwd"
 | ||
|             >
 | ||
|               确认支付
 | ||
|             </button>
 | ||
|           </view>
 | ||
|         </view>
 | ||
|       </view>
 | ||
|     </u-popup>
 | ||
|   </view>
 | ||
| </template>
 | ||
| 
 | ||
| <script>
 | ||
| import { buyTicket } from '@/config/ticket.js'
 | ||
| 
 | ||
| export default {
 | ||
|   data() {
 | ||
|     return {
 | ||
|       activityId: '',
 | ||
|       activityName: '',
 | ||
|       price: '',
 | ||
|       buyerList: [
 | ||
|         {
 | ||
|           name: '',
 | ||
|           phone: '',
 | ||
|           idCard: '',
 | ||
|           sexIndex: 0,
 | ||
|           sex: 1,
 | ||
|           clothSize: '',
 | ||
|           cohabitant: '',
 | ||
|           emergencyPhone: '',
 | ||
|         },
 | ||
|       ],
 | ||
|       sexOptions: ['男', '女'],
 | ||
|       submitting: false,
 | ||
|       showPayPwdModal: false,
 | ||
|       payPassword: '',
 | ||
|       pwdInputFocused: false,
 | ||
|     }
 | ||
|   },
 | ||
|   computed: {
 | ||
|     // 支付密码是否有效(非空且为纯数字)
 | ||
|     isPayPwdValid() {
 | ||
|       return this.payPassword.length > 0 && /^\d+$/.test(this.payPassword)
 | ||
|     },
 | ||
|   },
 | ||
|   onLoad(options) {
 | ||
|     if (options.activityId) {
 | ||
|       this.activityId = options.activityId
 | ||
|     }
 | ||
|     if (options.activityName) {
 | ||
|       this.activityName = decodeURIComponent(options.activityName)
 | ||
|     }
 | ||
|     if (options.price) {
 | ||
|       this.price = options.price
 | ||
|     }
 | ||
|   },
 | ||
|   methods: {
 | ||
|     // 返回上一页
 | ||
|     goBack() {
 | ||
|       uni.navigateBack()
 | ||
|     },
 | ||
| 
 | ||
|     // 性别选择变化
 | ||
|     onSexChange(e, index) {
 | ||
|       const sexIndex = e.detail.value
 | ||
|       this.buyerList[index].sexIndex = sexIndex
 | ||
|       this.buyerList[index].sex = sexIndex == 0 ? 1 : 2 // 1-男,2-女
 | ||
|     },
 | ||
| 
 | ||
|     // 添加购票人
 | ||
|     addBuyer() {
 | ||
|       this.buyerList.push({
 | ||
|         name: '',
 | ||
|         phone: '',
 | ||
|         idCard: '',
 | ||
|         sexIndex: 0,
 | ||
|         sex: 1,
 | ||
|         clothSize: '',
 | ||
|         cohabitant: '',
 | ||
|         emergencyPhone: '',
 | ||
|       })
 | ||
|     },
 | ||
| 
 | ||
|     // 删除购票人
 | ||
|     removeBuyer(index) {
 | ||
|       uni.showModal({
 | ||
|         title: '提示',
 | ||
|         content: '确定删除该购票人信息吗?',
 | ||
|         success: res => {
 | ||
|           if (res.confirm) {
 | ||
|             this.buyerList.splice(index, 1)
 | ||
|           }
 | ||
|         },
 | ||
|       })
 | ||
|     },
 | ||
| 
 | ||
|     // 验证表单
 | ||
|     validateForm() {
 | ||
|       for (let i = 0; i < this.buyerList.length; i++) {
 | ||
|         const buyer = this.buyerList[i]
 | ||
| 
 | ||
|         if (!buyer.name.trim()) {
 | ||
|           uni.$u.toast(`购票人${i + 1}的姓名不能为空`)
 | ||
|           return false
 | ||
|         }
 | ||
| 
 | ||
|         if (!buyer.phone.trim()) {
 | ||
|           uni.$u.toast(`购票人${i + 1}的联系方式不能为空`)
 | ||
|           return false
 | ||
|         }
 | ||
| 
 | ||
|         if (!buyer.idCard.trim()) {
 | ||
|           uni.$u.toast(`购票人${i + 1}的证件号码不能为空`)
 | ||
|           return false
 | ||
|         }
 | ||
| 
 | ||
|         if (buyer.sexIndex === -1 || (!buyer.sex && buyer.sex !== 0)) {
 | ||
|           uni.$u.toast(`请选择购票人${i + 1}的性别`)
 | ||
|           return false
 | ||
|         }
 | ||
| 
 | ||
|         if (!buyer.clothSize.trim()) {
 | ||
|           uni.$u.toast(`购票人${i + 1}的服装尺寸不能为空`)
 | ||
|           return false
 | ||
|         }
 | ||
| 
 | ||
|         if (!buyer.emergencyPhone.trim()) {
 | ||
|           uni.$u.toast(`购票人${i + 1}的紧急联系方式不能为空`)
 | ||
|           return false
 | ||
|         }
 | ||
| 
 | ||
|         if (buyer.emergencyPhone === buyer.phone) {
 | ||
|           uni.$u.toast(`购票人${i + 1}的紧急联系方式不能与联系方式相同`)
 | ||
|           return false
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       return true
 | ||
|     },
 | ||
| 
 | ||
|     // 处理提交按钮点击
 | ||
|     handleSubmit() {
 | ||
|       if (!this.validateForm()) {
 | ||
|         return
 | ||
|       }
 | ||
|       console.log('支付弹窗?')
 | ||
|       // 显示支付密码弹窗
 | ||
|       this.showPayPwdModal = true
 | ||
|       this.payPassword = ''
 | ||
|     },
 | ||
| 
 | ||
|     // 关闭支付密码弹窗
 | ||
|     closePayPwdModal() {
 | ||
|       this.showPayPwdModal = false
 | ||
|       this.payPassword = ''
 | ||
|     },
 | ||
| 
 | ||
|     // 支付密码输入处理
 | ||
|     onPayPwdInput(e) {
 | ||
|       // 只允许输入数字
 | ||
|       this.payPassword = e.detail.value.replace(/[^\d]/g, '')
 | ||
|     },
 | ||
| 
 | ||
|     // 密码输入框获得焦点
 | ||
|     onPwdFocus() {
 | ||
|       this.pwdInputFocused = true
 | ||
|     },
 | ||
| 
 | ||
|     // 密码输入框失去焦点
 | ||
|     onPwdBlur() {
 | ||
|       this.pwdInputFocused = false
 | ||
|     },
 | ||
| 
 | ||
|     // 确认支付密码
 | ||
|     confirmPayPwd() {
 | ||
|       if (!this.isPayPwdValid) {
 | ||
|         uni.$u.toast('请输入正确的支付密码')
 | ||
|         return
 | ||
|       }
 | ||
| 
 | ||
|       this.showPayPwdModal = false
 | ||
|       this.submitOrder()
 | ||
|     },
 | ||
| 
 | ||
|     // 提交订单
 | ||
|     async submitOrder() {
 | ||
|       if (!this.validateForm()) {
 | ||
|         return
 | ||
|       }
 | ||
| 
 | ||
|       this.submitting = true
 | ||
| 
 | ||
|       try {
 | ||
|         // 构造提交数据
 | ||
|         const orderData = {
 | ||
|           payPwd: this.payPassword,
 | ||
|           pkTicket: this.activityId,
 | ||
|           ticketParamList: this.buyerList.map(buyer => ({
 | ||
|             buyName: buyer.name,
 | ||
|             phone: buyer.phone,
 | ||
|             idCard: buyer.idCard,
 | ||
|             sex: buyer.sex,
 | ||
|             clothSize: buyer.clothSize,
 | ||
|             cohabitant: buyer.cohabitant || '',
 | ||
|             emergencyPhone: buyer.emergencyPhone,
 | ||
|           })),
 | ||
|         }
 | ||
| 
 | ||
|         const res = await buyTicket(orderData)
 | ||
| 
 | ||
|         if (res.code === 200) {
 | ||
|           uni.showToast({
 | ||
|             title: '购票成功',
 | ||
|             icon: 'success',
 | ||
|             duration: 2000,
 | ||
|           })
 | ||
| 
 | ||
|           setTimeout(() => {
 | ||
|             // 返回门票列表页面,并切换到我的门票tab
 | ||
|             uni.navigateBack({
 | ||
|               delta: 1,
 | ||
|             })
 | ||
|           }, 2000)
 | ||
|         } else {
 | ||
|           uni.$u.toast(res.msg || '购票失败,请重试')
 | ||
|         }
 | ||
|       } catch (error) {
 | ||
|         console.error('购票失败:', error)
 | ||
|         uni.$u.toast('购票失败,请重试')
 | ||
|       } finally {
 | ||
|         this.submitting = false
 | ||
|       }
 | ||
|     },
 | ||
|   },
 | ||
| }
 | ||
| </script>
 | ||
| 
 | ||
| <style lang="scss" scoped>
 | ||
| .buy-ticket-container {
 | ||
|   min-height: 100vh;
 | ||
|   background: #f8f8f8;
 | ||
| }
 | ||
| 
 | ||
| .custom-navbar {
 | ||
|   position: fixed;
 | ||
|   top: 0;
 | ||
|   left: 0;
 | ||
|   right: 0;
 | ||
|   z-index: 999;
 | ||
|   background: #fff;
 | ||
|   padding-top: var(--status-bar-height);
 | ||
|   border-bottom: 1px solid #eee;
 | ||
| 
 | ||
|   .navbar-content {
 | ||
|     display: flex;
 | ||
|     align-items: center;
 | ||
|     height: 44px;
 | ||
|     padding: 0 16px;
 | ||
| 
 | ||
|     .back-btn {
 | ||
|       width: 40px;
 | ||
|       height: 40px;
 | ||
|       display: flex;
 | ||
|       align-items: center;
 | ||
|       justify-content: center;
 | ||
|     }
 | ||
| 
 | ||
|     .navbar-title {
 | ||
|       flex: 1;
 | ||
|       text-align: center;
 | ||
|       font-size: 18px;
 | ||
|       font-weight: 600;
 | ||
|       color: #333;
 | ||
|     }
 | ||
| 
 | ||
|     .placeholder {
 | ||
|       width: 40px;
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| .content-area {
 | ||
|   margin-top: 10rpx;
 | ||
|   padding: 24rpx;
 | ||
|   padding-bottom: 200rpx;
 | ||
| }
 | ||
| 
 | ||
| .tip-banner {
 | ||
|   background: #fff5f5;
 | ||
|   border: 1px solid #ffebee;
 | ||
|   border-radius: 8px;
 | ||
|   padding: 12px 16px;
 | ||
|   margin-bottom: 16px;
 | ||
|   display: flex;
 | ||
|   align-items: center;
 | ||
| 
 | ||
|   .tip-icon {
 | ||
|     margin-right: 8px;
 | ||
|   }
 | ||
| 
 | ||
|   .tip-text {
 | ||
|     font-size: 14px;
 | ||
|     color: #f56c6c;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| .buyer-list {
 | ||
|   .buyer-item {
 | ||
|     background: #fff;
 | ||
|     border-radius: 12px;
 | ||
|     margin-bottom: 16px;
 | ||
|     overflow: hidden;
 | ||
|     box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
 | ||
| 
 | ||
|     .buyer-header {
 | ||
|       display: flex;
 | ||
|       align-items: center;
 | ||
|       justify-content: space-between;
 | ||
|       padding: 16px;
 | ||
|       border-bottom: 1px solid #f5f5f5;
 | ||
| 
 | ||
|       .buyer-title {
 | ||
|         font-size: 16px;
 | ||
|         font-weight: 600;
 | ||
|         color: #333;
 | ||
|       }
 | ||
| 
 | ||
|       .delete-btn {
 | ||
|         width: 32px;
 | ||
|         height: 32px;
 | ||
|         display: flex;
 | ||
|         align-items: center;
 | ||
|         justify-content: center;
 | ||
|         background: #f8f8f8;
 | ||
|         border-radius: 16px;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     .form-section {
 | ||
|       padding: 16px;
 | ||
| 
 | ||
|       .form-item {
 | ||
|         display: flex;
 | ||
|         align-items: center;
 | ||
|         margin-bottom: 20px;
 | ||
| 
 | ||
|         &:last-child {
 | ||
|           margin-bottom: 0;
 | ||
|         }
 | ||
| 
 | ||
|         .label {
 | ||
|           width: 100px;
 | ||
|           font-size: 14px;
 | ||
|           color: #333;
 | ||
|           position: relative;
 | ||
| 
 | ||
|           &.required::before {
 | ||
|             content: '*';
 | ||
|             color: #f56c6c;
 | ||
|             position: absolute;
 | ||
|             left: -10px;
 | ||
|           }
 | ||
|         }
 | ||
| 
 | ||
|         .input-wrapper {
 | ||
|           flex: 1;
 | ||
| 
 | ||
|           .form-input {
 | ||
|             width: 100%;
 | ||
|             height: 44px;
 | ||
|             background: #f8f8f8;
 | ||
|             border-radius: 8px;
 | ||
|             border: none;
 | ||
|             padding: 0 16px;
 | ||
|             font-size: 14px;
 | ||
|             color: #333;
 | ||
|             box-sizing: border-box;
 | ||
|             &::placeholder {
 | ||
|               color: #c0c0c0;
 | ||
|             }
 | ||
|           }
 | ||
| 
 | ||
|           .picker-input {
 | ||
|             display: flex;
 | ||
|             align-items: center;
 | ||
|             justify-content: space-between;
 | ||
|             width: 100%;
 | ||
|             height: 44px;
 | ||
|             background: #f8f8f8;
 | ||
|             box-sizing: border-box;
 | ||
|             border-radius: 8px;
 | ||
|             padding: 0 16px;
 | ||
| 
 | ||
|             .picker-text {
 | ||
|               font-size: 14px;
 | ||
|               color: #333;
 | ||
| 
 | ||
|               &.placeholder {
 | ||
|                 color: #c0c0c0;
 | ||
|               }
 | ||
|             }
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| .add-buyer-btn {
 | ||
|   display: flex;
 | ||
|   align-items: center;
 | ||
|   justify-content: center;
 | ||
|   background: #fff;
 | ||
|   border-radius: 12px;
 | ||
|   padding: 16px;
 | ||
|   margin-bottom: 24px;
 | ||
|   border: 2px dashed #e0e0e0;
 | ||
| 
 | ||
|   .add-text {
 | ||
|     font-size: 14px;
 | ||
|     color: #666;
 | ||
|     margin-left: 8px;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| .submit-wrapper {
 | ||
|   position: fixed;
 | ||
|   bottom: 0;
 | ||
|   left: 0;
 | ||
|   right: 0;
 | ||
|   background: #fff;
 | ||
|   padding: 16px;
 | ||
|   border-top: 1px solid #eee;
 | ||
| 
 | ||
|   .submit-btn {
 | ||
|     width: 100%;
 | ||
|     height: 96r px;
 | ||
|     background: #005bac;
 | ||
|     color: #fff;
 | ||
|     border: none;
 | ||
|     border-radius: 48rpx;
 | ||
|     font-size: 32rpx;
 | ||
|     font-weight: 600;
 | ||
| 
 | ||
|     &.disabled {
 | ||
|       background: #ccc;
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| /* 支付密码弹窗样式 */
 | ||
| .pay-pwd-modal {
 | ||
|   background: #fff;
 | ||
|   border-radius: 16rpx;
 | ||
|   overflow: hidden;
 | ||
|   box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
 | ||
| 
 | ||
|   .modal-header {
 | ||
|     padding: 40rpx 40rpx 20rpx;
 | ||
|     text-align: center;
 | ||
|     background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
 | ||
| 
 | ||
|     .header-icon {
 | ||
|       width: 48px;
 | ||
|       height: 48px;
 | ||
|       background: rgba(0, 91, 172, 0.1);
 | ||
|       border-radius: 24px;
 | ||
|       display: flex;
 | ||
|       align-items: center;
 | ||
|       justify-content: center;
 | ||
|       margin: 0 auto 16px;
 | ||
|     }
 | ||
| 
 | ||
|     .modal-title {
 | ||
|       font-size: 18px;
 | ||
|       font-weight: 600;
 | ||
|       color: #333;
 | ||
|       display: block;
 | ||
|       margin-bottom: 8px;
 | ||
|     }
 | ||
| 
 | ||
|     .modal-subtitle {
 | ||
|       font-size: 14px;
 | ||
|       color: #666;
 | ||
|       display: block;
 | ||
|       line-height: 1.4;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   .modal-body {
 | ||
|     padding: 24rpx;
 | ||
| 
 | ||
|     .pwd-input-wrapper {
 | ||
|       position: relative;
 | ||
|       margin-bottom: 16rpx;
 | ||
| 
 | ||
|       .pwd-input {
 | ||
|         width: 100%;
 | ||
|         height: 80rpx;
 | ||
|         background: #f8f9fa;
 | ||
|         border-radius: 24rpx;
 | ||
|         border: none;
 | ||
|         padding: 0 20rpx;
 | ||
|         font-size: 24rpx;
 | ||
|         color: #333;
 | ||
|         text-align: center;
 | ||
|         letter-spacing: 4rpx;
 | ||
|         box-sizing: border-box;
 | ||
|         transition: all 0.3s ease;
 | ||
| 
 | ||
|         &::placeholder {
 | ||
|           color: #c0c0c0;
 | ||
|           letter-spacing: normal;
 | ||
|         }
 | ||
| 
 | ||
|         &:focus {
 | ||
|           background: #fff;
 | ||
|           outline: none;
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       .input-border {
 | ||
|         position: absolute;
 | ||
|         top: 0;
 | ||
|         left: 0;
 | ||
|         right: 0;
 | ||
|         bottom: 0;
 | ||
|         border: 2px solid transparent;
 | ||
|         border-radius: 12px;
 | ||
|         pointer-events: none;
 | ||
|         transition: all 0.3s ease;
 | ||
| 
 | ||
|         &.focused {
 | ||
|           border-color: #005bac;
 | ||
|           box-shadow: 0 0 0 4px rgba(0, 91, 172, 0.1);
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     .pwd-strength {
 | ||
|       .strength-dots {
 | ||
|         display: flex;
 | ||
|         justify-content: center;
 | ||
|         gap: 8px;
 | ||
| 
 | ||
|         .dot {
 | ||
|           width: 8px;
 | ||
|           height: 8px;
 | ||
|           border-radius: 4px;
 | ||
|           background: #e9ecef;
 | ||
|           transition: all 0.3s ease;
 | ||
| 
 | ||
|           &.active {
 | ||
|             background: #005bac;
 | ||
|             transform: scale(1.2);
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   .modal-footer {
 | ||
|     padding: 0 24px 24px;
 | ||
| 
 | ||
|     .btn-group {
 | ||
|       display: flex;
 | ||
|       gap: 12px;
 | ||
| 
 | ||
|       .modal-btn {
 | ||
|         flex: 1;
 | ||
|         height: 48rpx;
 | ||
|         border: none;
 | ||
|         border-radius: 24rpx;
 | ||
|         font-size: 24rpx;
 | ||
|         font-weight: 500;
 | ||
|         transition: all 0.3s ease;
 | ||
|         display: flex;
 | ||
|         align-items: center;
 | ||
|         justify-content: center;
 | ||
| 
 | ||
|         &.cancel-btn {
 | ||
|           background: #f8f9fa;
 | ||
|           color: #666;
 | ||
| 
 | ||
|           &:active {
 | ||
|             background: #e9ecef;
 | ||
|           }
 | ||
|         }
 | ||
| 
 | ||
|         &.confirm-btn {
 | ||
|           background: #005bac;
 | ||
|           color: #fff;
 | ||
|           font-weight: 600;
 | ||
| 
 | ||
|           &:active {
 | ||
|             background: #004494;
 | ||
|           }
 | ||
| 
 | ||
|           &.disabled {
 | ||
|             background: #e9ecef;
 | ||
|             color: #adb5bd;
 | ||
|             cursor: not-allowed;
 | ||
| 
 | ||
|             &:active {
 | ||
|               background: #e9ecef;
 | ||
|             }
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| </style>
 |