uniapp自定义封装支付密码组件(vue3)

使用uniapp自定义封装支付密码组件(vue3语法)

父组件

<paypassword v-model="passwordValue" @complete="onPasswordComplete"></paypassword>
const passwordValue = ref('')

// 密码输入完成回调
const onPasswordComplete = (password) => {
  console.log('密码输入完成:', password)
  passwordValue.value = password;
  uni.showToast({
    title: '密码输入完成',
    icon: 'success'
  })
}

子组件

<template>
    <view class="password-container">
      <!-- 第一行:6个密码输入框 -->
      <view class="password-input-container">
        <view class="password-input-row">
          <view 
            v-for="index in 6" 
            :key="index"
            class="password-input-box"
            :class="{ 'active': currentIndex === index - 1 }"
          >
            <view v-if="password[index - 1]" class="password-dot"></view>
          </view>
        </view>
      </view>
      
      <!-- 第二行:状态图标和文字 -->
      <view class="password-status">
        <uni-icons 
          :type="isCompleted ? 'checkbox-filled' : 'checkbox-filled'" 
          :color="isCompleted ? '#12a58c' : '#d4d4d4'"
          size="32rpx"
        ></uni-icons>
        <text 
          class="status-text" 
          :class="{ 'status-text-complete': isCompleted }"
        >
          {{ '6个数字' }}
        </text>
      </view>
      
      <!-- 第三模块:12宫格数字键盘 -->
      <view class="number-keyboard">
        <view class="keyboard-row" v-for="(row, rowIndex) in keyboardLayout" :key="rowIndex">
          <view 
            v-for="(key, keyIndex) in row" 
            :key="keyIndex"
            class="keyboard-key"
            @click="handleKeyClick(key)"
          >
            <text v-if="key.type === 'number'" class="key-text">{{ key.value }}</text>
            <!-- 移除按钮可以使用icon,也可以使用自定义图片我使用的是自定义图片 -->
           <!-- <uni-icons 
              v-if="key.type === 'delete'" 
              type="closeempty" 
              size="56rpx" 
              color="#171717"
            ></uni-icons> -->
            <image class="delImg" v-if="key.type === 'delete'"  src="/static/image/home/Frame.png" mode=""></image>
          </view>
        </view>
      </view>
    </view>
  </template>
  
  <script setup>
  import { ref, computed, watch, defineProps, defineEmits } from 'vue'
  
  // 定义props
  const props = defineProps({
    // 提示文字
    tipText: {
      type: String,
      default: '请输入6位数字密码'
    },
    // 初始密码值
    modelValue: {
      type: String,
      default: ''
    }
  })
  
  // 定义emits
  const emits = defineEmits(['update:modelValue', 'complete'])
  
  // 数据状态
  const password = ref([])
  const currentIndex = ref(0)
  
  // 键盘布局
  const keyboardLayout = [
    [
      { type: 'number', value: '1' },
      { type: 'number', value: '2' },
      { type: 'number', value: '3' }
    ],
    [
      { type: 'number', value: '4' },
      { type: 'number', value: '5' },
      { type: 'number', value: '6' }
    ],
    [
      { type: 'number', value: '7' },
      { type: 'number', value: '8' },
      { type: 'number', value: '9' }
    ],
    [
      { type: 'number', value: '*' },
      { type: 'number', value: '0' },
      { type: 'delete'}
    ]
  ]
  
  // 计算属性:是否输入完成
  const isCompleted = computed(() => password.value.length === 6)
  
  // 监听密码完成状态
  watch(isCompleted, (newVal) => {
    if (newVal) {
      const passwordStr = password.value.join('')
      emits('update:modelValue', passwordStr)
      emits('complete', passwordStr)
    }
  })
  
  // 监听modelValue变化
  watch(() => props.modelValue, (newVal) => {
    if (newVal && newVal.length <= 6) {
      password.value = newVal.split('')
      currentIndex.value = password.value.length
    }
  }, { immediate: true })
  
  // 处理按键点击
  const handleKeyClick = (key) => {
    if (key.type === 'number') {
      // 输入数字
      if (password.value.length < 6) {
        password.value.push(key.value)
        currentIndex.value = password.value.length
      }
    } else if (key.type === 'delete') {
      // 删除
      if (password.value.length > 0) {
        password.value.pop()
        currentIndex.value = password.value.length
      }
    }
    
    // 更新modelValue
    const passwordStr = password.value.join('')
    emits('update:modelValue', passwordStr)
  }
  
  // 重置密码
  const resetPassword = () => {
    password.value = []
    currentIndex.value = 0
  }
  
  // 暴露方法给父组件
  defineExpose({
    resetPassword
  })
  </script>
  
  <style lang="scss" scoped>
  .password-container {
    width: 100%;
    padding-top: 96rpx;
    box-sizing: border-box;
    
    .password-input-container {
      margin-bottom: 32rpx;
      .password-input-row {
        display: flex;
        justify-content: space-between;
        .password-input-box {
          width: 96rpx;
          height: 112rpx;
          border-radius: 24rpx;
          border: 2rpx solid #171717;
          display: flex;
          align-items: center;
          justify-content: center;
          
          &.active {
            border-color: #FF4001;
          }
          
          .password-dot {
            width: 20rpx;
            height: 20rpx;
            border-radius: 50%;
            background: #333;
          }
        }
      }
    }
    
    .password-status {
      display: flex;
      align-items: center;
      margin-bottom: 40rpx;
      
      .status-text {
        font-family: PingFang SC, PingFang SC;
        font-size: 24rpx;
        color: #737373;
        margin-left: 8rpx;
      }
      
      .status-text-complete {
        color: #12a58c;
      }
    }
    
    .number-keyboard {
      .keyboard-row {
        display: flex;
        justify-content: space-around;
        margin-bottom: 20rpx;
        .keyboard-key {
            width: 207rpx;
            height: 112rpx;
            background: #FAFAFA;
            border-radius: 24rpx;
            display: flex;
            align-items: center;
            justify-content: center;
          .key-text {
            font-family: PingFang SC, PingFang SC;
            font-weight: 500;
            font-size: 48rpx;
            color: #171717;
          }
          .delImg {
              width: 56rpx;
              height: 56rpx;
          }
          
          &.key-delete {
            // background: #e0e0e0;
          }
          
          &.key-empty {
            // background: transparent;
            // visibility: hidden;
          }
        }
      }
    }
  }
  </style>

效果图:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值