vue3实现管理后台PC端【车牌号】输入框控件

2,081 阅读4分钟

在pc端后台系统有个需求是填车牌号。需求是可以不用输。点击输入框弹出个可以选的。就像交管APP哪种车牌号选中。因此基于自己的场景进行封装控件

交管效果如下

befe81724f6f116c9ab2db04e7e1742.jpg

实现效果如下

kk.gif

组件结构和功能

  1. 键盘组件

    • 包含中文和英文字符的键盘。
    • 用户可以通过点击按键输入字符,或点击删除键删除字符。
    • 切换按键可以在中文和英文键盘之间切换。
  2. 输入框组件

    • 显示当前车牌号码的每一位。
    • 用户可以点击车牌号码的任意一位来选择输入位置。

组件交互流程

  1. 用户点击输入框中的某一位车牌号码,激活该位置。
  2. 键盘组件接收输入,并更新到输入框组件中激活的位置。
  3. 用户可以切换输入类型(中文/英文),继续输入或删除字符。
  4. 用户完成输入,点击完成按钮。 image.png
输入框组件
  • 定义属性和事件

    • props 接收父组件传递的 value,表示当前输入的键值。

    • emit 用于向父组件发送更新事件。

  • 状态管理

    • plateNumber数组。定义8个''的字符串且作为渲染8个input输入框
  • 事件处理

    • handleClickItem点击输入框时emit事件activeIndex
<template>
  <ul class="input-box">
    <li
      v-for="(item, index) in plateNumber"
      :key="index"
      :class="{ active: props.activeIndex === index }"
      @click="handleClickItem(index)"
    >
      <span>{{ item }}</span>
      <span v-if="props.activeIndex === index" class="active_border"></span>
    </li>
  </ul>
</template>
<script lang="ts" setup>
import {  reactive,defineProps, defineEmits ,watch} from "vue";
// 定义 props 类型
interface Props {
  value: string;
  activeIndex: number;
}
// 定义 emit 事件类型
const emit = defineEmits<{
  (event: "update:activeIndex", value: number): void;
}>();
const props = defineProps<Props>();
let plateNumber = reactive<string[]>(Array(8).fill("")); //车牌是8位数
const handleClickItem = (index:number) => {
  //选择框的下标
  emit("update:activeIndex", index);
};

watch(() => props.value, (newValue) => {
  const currentValue = newValue.split("");
  plateNumber = currentValue.concat(Array(plateNumber.length - currentValue.length).fill(""));
});

</script>
<style lang="less" scoped>
.input-box {
    flex:1;
  display: flex;
  list-style: none; /* 去掉 ul 的默认点样式 */
  padding-left: 0;  /* 去掉 ul 的默认内边距 */
  > li {
    list-style-type: none; /* 去掉 li 的默认点样式 */
    flex: 1;
    height: 28px;
    line-height: 28px;
    border: 1px solid #ccc;
    margin: 5px;
    cursor: pointer;
    text-align: center;
  }
  >li.active{
    position: relative;
    .active_border{
      display: block;
      position: absolute;
      bottom: 0;
      left: 0;
      width:100%;
      height: 1px;
      background: red;
     
    }
  }
}
</style>

键盘组件

  • 定义属性和事件

    • props 接收父组件传递的 modelValue,表示当前输入的键值。
    • emit 用于向父组件发送更新事件。
  • 状态管理

    • activeIndex:记录当前激活的键值索引。
    • keyValue:当前输入的键值。
    • type:键盘类型(中文 cn 或英文 en)。
  • 键盘布局

    • 使用 reactive 创建中文和英文键盘布局数组。
    • 使用 computed 根据当前类型选择相应的键盘布局。
  • 切换类型

    • handleChangeType 函数切换键盘类型,并更新键盘布局。
  • 监听变化

    • 监听 modelValueactiveIndex 的变化,同步更新 keyValue 和键盘类型。
  • 事件处理

    • finish:完成输入,向父组件发送更新事件。
    • handleDel:删除当前激活索引的字符。
    • handleClickKey:输入按键字符,并更新激活索引。
<template>
    <div class="key-box">
            <div class="key-box-top">
                <key-board-item :value="keyValue" v-model:activeIndex="activeIndex"></key-board-item>
                <el-button type="primary" size="mini" @click="finish">完成</el-button>
            </div>
            <div class="key-box-container">
         <div class="key-box-row" v-for="(item, index) in list" :key="index">
            <div
              class="key-box-row-wrapper"
              v-for="(val, index) in item"
              :key="index"
              :class="{
                'key-box-row-del-wrapper': val === 'del',
                'key-box-row-type-wrapper': val === 'type'
              }"
            >
              <el-button v-if="val === 'type'" class="key-box-row-btn-key" @click="handleChangeType">
                <span v-if="type === 'cn'">中/<span class="key-box-row-smaller"></span></span>
                <span v-else><span class="key-box-row-smaller"></span>/英</span>
              </el-button>
              <el-button v-else-if="val === 'del'" class="key-box-row-btn-key" type="text" @click="handleDel" :icon="CloseBold" />
              <el-button v-else class="key-box-row-btn-key" :class="{'key-box-row-btn-empty': !val}" @click="handleClickKey(val)">
                {{ val }}
              </el-button
              >
            </div>
          </div>
      </div>
    </div>
</template>
<script lang="ts" setup>
import {  CloseBold} from '@element-plus/icons-vue'
import keyBoardItem from './keyBoardItem.vue'
import { ref,reactive, defineProps,computed , defineEmits, watch } from "vue";
import { fa } from 'element-plus/es/locale';

// 定义 props 类型
const props = defineProps({
  modelValue: String
});
//定义emit事件
const emit = defineEmits(['update:modelValue']);
//定义active点击事件
let activeIndex =  ref<number>(0);

//定义输入值的值
let keyValue = ref<string>(props.modelValue)
//定义类型
let type = ref<string>("cn");
const cn = reactive<string[][]>([
          ["京", "津", "沪", "渝", "冀", "豫", "云", "辽", "黑", "湘"],
          ["皖", "鲁", "新", "苏", "浙", "赣", "鄂", "桂", "甘", "晋"],
          ["蒙", "陕", "吉", "闽", "贵", "粤", "青", "藏", "川", "宁"],
          ["type", "琼", "使", "领", "学", "警", "", "", "del"]
        ]);
const en = reactive<string[][]>([
          ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
          ["Q", "W", "E", "R", "T", "Y", "U", "O", "P"],
          ["A", "S", "D", "F", "G", "H", "J", "K", "L"],
          ["type", "Z", "X", "C", "V", "B", "N", "M", "del"]
        ])
// 定义计算属性
const list = computed(() => type.value== "en" ? en:cn);


//切换类型
const handleChangeType = ()=>{
  type.value = type.value=="cn"?"en":"cn";
}
//wath外部的值变化时同步keyValue
watch(() => props.modelValue, (newValue:string) => {
  keyValue.value = newValue;
});
//watch监听input框的值
watch(() => activeIndex.value, (newValue:number) => {
  if(newValue==0){
    type.value = "cn";
  }else{
    type.value = "en";
  }
});


const finish = ()=>{
  //如果要加验证。可自己定义传多参数在完成前抛出
   emit('update:modelValue',keyValue.value);
}
const handleDel = ()=>{
  if (activeIndex.value === 0 && !keyValue.value) return;

  let currentValue = keyValue.value.split("");

  if (currentValue[activeIndex.value]) {
    currentValue.splice(activeIndex.value, 1);
  }

  if (activeIndex.value > 0) {
    activeIndex.value--;
  }

  keyValue.value = currentValue.join('');
}
//输入框赋值事件
const handleClickKey = (val:string)=>{
  const currentValue = keyValue.value.split("");
  currentValue[activeIndex.value] = val;
  keyValue.value = currentValue.join('');

  // 仅在 activeIndex 小于最大值时递增
  if (activeIndex.value < 7) {
    activeIndex.value++;
  }
 
}
</script>
<style scoped lang="less">
.key-box{
    width:100%;
    .key-box-top{
    display: flex;
    align-items: center;

    .key-box-top{
        border-bottom: 1px solid #ccc;
    }
    
}
.key-box-container {
    padding: 3px;
   
    padding-bottom: 22px;
  
    .key-box-row {
      display: flex;
      justify-content: center;
    }
    .key-box-row-wrapper {
      flex: 0 1 calc((100% - 6px * 10) / 10);
      padding: 3px;
      box-sizing: content-box;
      margin:1px;
        border:1px solid #999;
        display: flex;
        align-items: center;
        text-align: center;
        cursor: pointer;
      &.key-box-row-del-wrapper,
      &.key-box-row-type-wrapper {
        flex: 1;
      }
      &.key-box-row-type-wrapper {
        .key-box-row-smaller {
          color: #999;
          font-size: 12px;
        }
      }
  
      .key-box-row-btn-key {
        padding: 0;
        width: 100%;
        border-radius: 4px;
      }
      .key-box-row-btn-empty {
        background: transparent;
        border: none;
      }
      .key-box-row-delete-icon {
        width: 18px;
        vertical-align: middle;
      }
    }
  }
}

</style>

完整使用

 <el-popover placement="top" :width="370" trigger="click">
    <template #reference>
       <el-input
       readonly
      v-model="PlateNumber"
      style="width: 200px"
      placeholder="输入车牌号"
      class="input-with-select"
    >
      
      <template #append>
        <el-button :icon="Operation" />
      </template>
    </el-input>
</template>
<key-board v-model=PlateNumber  ></key-board>
</el-popover>

如果你想实时跟使用的input框绑定。手动把keyValue换成prop.modelValue