介绍
组合了键盘、弹出框组件,在弹出框中显示键盘。
引入
js
import KeyboardPopout from 'sard-uniapp/components/keyboard-popout/keyboard-popout.vue'代码演示
基础使用
使用 v-model:visible 控制弹出框显隐,通过 input 和 delete 事件处理输入内容。
vue
<template>
<sar-list card>
<sar-list-item title="当前值" :value="value || '暂无输入'" />
<sar-list-item title="打开数字键盘" arrow hover @click="visible = true" />
<sar-keyboard-popout
v-model:visible="visible"
title="请输入数字"
transparent
@input="onInput"
@delete="onDelete"
@confirm="onConfirm"
/>
</sar-list>
</template>
<script setup lang="ts">
import { toast } from 'sard-uniapp'
import { ref } from 'vue'
const visible = ref(false)
const value = ref('')
const onInput = (key: string) => {
value.value += key
}
const onDelete = () => {
value.value = value.value.slice(0, -1)
}
const onConfirm = () => {
toast(`输入完成,值:${value.value}`)
value.value = ''
}
</script>vue
<template>
<sar-list card>
<sar-list-item title="当前值" :value="value || '暂无输入'" />
<sar-list-item title="打开数字键盘" arrow hover @click="visible = true" />
<sar-keyboard-popout
v-model:visible="visible"
title="请输入数字"
transparent
@input="onInput"
@delete="onDelete"
@confirm="onConfirm"
/>
</sar-list>
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
import { ref } from "vue";
const visible = ref(false);
const value = ref("");
const onInput = (key) => {
value.value += key;
};
const onDelete = () => {
value.value = value.value.slice(0, -1);
};
const onConfirm = () => {
toast(`\u8F93\u5165\u5B8C\u6210\uFF0C\u503C\uFF1A${value.value}`);
value.value = "";
};
</script>插槽
使用默认插槽可以在键盘上方添加自定义内容。
vue
<template>
<sar-list card>
<sar-list-item title="打开键盘" arrow hover @click="visible = true" />
<sar-keyboard-popout
v-model:visible="visible"
title="安全键盘"
:show-cancel="false"
:show-confirm="false"
@input="onInput"
@delete="onDelete"
@before-enter="value = ''"
>
<view class="px-32 pb-32">
<sar-password-input
v-model="value"
type="underline"
:length="6"
custom-keyboard
focused
/>
</view>
</sar-keyboard-popout>
</sar-list>
</template>
<script setup lang="ts">
import { toast } from 'sard-uniapp'
import { ref } from 'vue'
const visible = ref(false)
const value = ref('')
const onInput = (key: string) => {
value.value = (value.value + key).slice(0, 6)
if (value.value.length === 6) {
visible.value = false
toast(`输入完成,密码:${value.value}`)
}
}
const onDelete = () => {
value.value = value.value.slice(0, -1)
}
</script>vue
<template>
<sar-list card>
<sar-list-item title="打开键盘" arrow hover @click="visible = true" />
<sar-keyboard-popout
v-model:visible="visible"
title="安全键盘"
:show-cancel="false"
:show-confirm="false"
@input="onInput"
@delete="onDelete"
@before-enter="value = ''"
>
<view class="px-32 pb-32">
<sar-password-input
v-model="value"
type="underline"
:length="6"
custom-keyboard
focused
/>
</view>
</sar-keyboard-popout>
</sar-list>
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
import { ref } from "vue";
const visible = ref(false);
const value = ref("");
const onInput = (key) => {
value.value = (value.value + key).slice(0, 6);
if (value.value.length === 6) {
visible.value = false;
toast(`\u8F93\u5165\u5B8C\u6210\uFF0C\u5BC6\u7801\uFF1A${value.value}`);
}
};
const onDelete = () => {
value.value = value.value.slice(0, -1);
};
</script>类型
可以切换不同的键盘类型,在弹出框中展示数字、小数、身份证和随机数字键盘。
vue
<template>
<sar-list card>
<sar-list-item title="当前类型" :value="type" />
<sar-list-item title="当前值" :value="value || '暂无输入'" />
<sar-list-item>
<sar-segmented v-model="type" :options="types" />
</sar-list-item>
<sar-list-item title="打开键盘" arrow hover @click="visible = true" />
<sar-keyboard-popout
v-model:visible="visible"
:title="`类型:${type}`"
:type="type"
@input="onInput"
@delete="onDelete"
>
<view class="flex items-center justify-center text-center h-80">
{{ value || '暂无输入' }}
</view>
</sar-keyboard-popout>
</sar-list>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const types = ['number', 'digit', 'idcard', 'random']
const type = ref<'number' | 'digit' | 'idcard' | 'random'>('number')
const visible = ref(false)
const value = ref('')
const onInput = (key: string) => {
value.value += key
}
const onDelete = () => {
value.value = value.value.slice(0, -1)
}
</script>vue
<template>
<sar-list card>
<sar-list-item title="当前类型" :value="type" />
<sar-list-item title="当前值" :value="value || '暂无输入'" />
<sar-list-item>
<sar-segmented v-model="type" :options="types" />
</sar-list-item>
<sar-list-item title="打开键盘" arrow hover @click="visible = true" />
<sar-keyboard-popout
v-model:visible="visible"
:title="`类型:${type}`"
:type="type"
@input="onInput"
@delete="onDelete"
>
<view class="flex items-center justify-center text-center h-80">
{{ value || '暂无输入' }}
</view>
</sar-keyboard-popout>
</sar-list>
</template>
<script setup lang="js">
import { ref } from "vue";
const types = ["number", "digit", "idcard", "random"];
const type = ref("number");
const visible = ref(false);
const value = ref("");
const onInput = (key) => {
value.value += key;
};
const onDelete = () => {
value.value = value.value.slice(0, -1);
};
</script>车牌号键盘
下面演示如何使用 mode 和 disabled-key 属性引导和规范化车牌号输入。
vue
<template>
<sar-list card>
<sar-list-item title="当前值" :value="value || '暂无输入'" />
<sar-list-item title="打开车牌号键盘" arrow hover @click="visible = true" />
<sar-keyboard-popout
v-model:visible="visible"
v-model:mode="mode"
title="请输入车牌号"
type="plate"
:disabled-key="disabledKey"
@input="onInput"
@delete="onDelete"
>
<view class="input-wrapper">
<template v-for="(_, index) in maxLength" :key="index">
<view :class="getItemClass(index)">
{{ value[index] || '' }}
</view>
<view v-if="index === 1" class="dot"></view>
</template>
</view>
</sar-keyboard-popout>
</sar-list>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import {
plateEnglishLetterKeys,
plateSuffixKeys,
type KeyboardPlateMode,
} from 'sard-uniapp'
const visible = ref(false)
const mode = ref<KeyboardPlateMode>()
const value = ref('')
const disabledKey = (key: string) => {
const val = value.value
if (val.length === maxLength.value) {
return true
}
switch (val.length) {
case 0:
return key === 'toggle' || plateSuffixKeys.includes(key)
case 1:
return !plateEnglishLetterKeys.includes(key)
case 2:
case 3:
case 4:
case 5:
return key === 'toggle' || plateSuffixKeys.includes(key)
case 6:
return mode.value === 'chinese' && !plateSuffixKeys.includes(key)
case 7:
return key === 'toggle'
}
return false
}
const getItemClass = (index: number) => {
const classes = ['input-item']
if (
value.value.length === index ||
(index === maxLength.value - 1 && value.value.length === maxLength.value)
) {
classes.push('active')
}
if (index === 7) {
classes.push('optional')
}
if (!value.value[index]) {
classes.push('empty')
}
return classes.join(' ')
}
watch(value, (val) => {
if (val.length === 0) {
mode.value = 'chinese'
} else {
mode.value = 'english'
}
})
const maxLength = computed(() => {
return value.value.length === 7 && plateSuffixKeys.includes(value.value[6])
? 7
: 8
})
const onInput = (key: string) => {
value.value += key
}
const onDelete = () => {
value.value = value.value.slice(0, -1)
}
</script>
<style lang="scss" scoped>
.input-wrapper {
display: flex;
justify-content: center;
align-items: center;
gap: 10rpx;
padding: 32rpx;
}
.input-item {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 60rpx;
height: 80rpx;
font-size: 32rpx;
border-radius: 8rpx;
border: 1px solid var(--sar-border-color);
&.active {
border-color: var(--sar-primary);
background: rgba(var(--sar-primary-rgb), 0.08);
&.empty {
&::after {
content: '';
position: absolute;
width: 1px;
height: 40%;
background: currentColor;
animation: blink 2s infinite;
}
}
}
&.optional {
border-style: dashed;
}
}
.dot {
width: 8rpx;
height: 8rpx;
background: currentColor;
border-radius: 50%;
}
@keyframes blink {
0%,
50%,
100% {
opacity: 1;
}
25%,
75% {
opacity: 0;
}
}
</style>vue
<template>
<sar-list card>
<sar-list-item title="当前值" :value="value || '暂无输入'" />
<sar-list-item title="打开车牌号键盘" arrow hover @click="visible = true" />
<sar-keyboard-popout
v-model:visible="visible"
v-model:mode="mode"
title="请输入车牌号"
type="plate"
:disabled-key="disabledKey"
@input="onInput"
@delete="onDelete"
>
<view class="input-wrapper">
<template v-for="(_, index) in maxLength" :key="index">
<view :class="getItemClass(index)">
{{ value[index] || '' }}
</view>
<view v-if="index === 1" class="dot"></view>
</template>
</view>
</sar-keyboard-popout>
</sar-list>
</template>
<script setup lang="js">
import { computed, ref, watch } from "vue";
import {
plateEnglishLetterKeys,
plateSuffixKeys
} from "sard-uniapp";
const visible = ref(false);
const mode = ref();
const value = ref("");
const disabledKey = (key) => {
const val = value.value;
if (val.length === maxLength.value) {
return true;
}
switch (val.length) {
case 0:
return key === "toggle" || plateSuffixKeys.includes(key);
case 1:
return !plateEnglishLetterKeys.includes(key);
case 2:
case 3:
case 4:
case 5:
return key === "toggle" || plateSuffixKeys.includes(key);
case 6:
return mode.value === "chinese" && !plateSuffixKeys.includes(key);
case 7:
return key === "toggle";
}
return false;
};
const getItemClass = (index) => {
const classes = ["input-item"];
if (value.value.length === index || index === maxLength.value - 1 && value.value.length === maxLength.value) {
classes.push("active");
}
if (index === 7) {
classes.push("optional");
}
if (!value.value[index]) {
classes.push("empty");
}
return classes.join(" ");
};
watch(value, (val) => {
if (val.length === 0) {
mode.value = "chinese";
} else {
mode.value = "english";
}
});
const maxLength = computed(() => {
return value.value.length === 7 && plateSuffixKeys.includes(value.value[6]) ? 7 : 8;
});
const onInput = (key) => {
value.value += key;
};
const onDelete = () => {
value.value = value.value.slice(0, -1);
};
</script>
<style lang="scss" scoped>
.input-wrapper {
display: flex;
justify-content: center;
align-items: center;
gap: 10rpx;
padding: 32rpx;
}
.input-item {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 60rpx;
height: 80rpx;
font-size: 32rpx;
border-radius: 8rpx;
border: 1px solid var(--sar-border-color);
&.active {
border-color: var(--sar-primary);
background: rgba(var(--sar-primary-rgb), 0.08);
&.empty {
&::after {
content: '';
position: absolute;
width: 1px;
height: 40%;
background: currentColor;
animation: blink 2s infinite;
}
}
}
&.optional {
border-style: dashed;
}
}
.dot {
width: 8rpx;
height: 8rpx;
background: currentColor;
border-radius: 50%;
}
@keyframes blink {
0%,
50%,
100% {
opacity: 1;
}
25%,
75% {
opacity: 0;
}
}
</style>API
KeyboardPopoutProps
继承 KeyboardProps 并有以下额外属性:
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| visible (v-model) | 是否显示弹出框 | boolean | - |
| title | 弹出框标题 | string | - |
| popout-class | 弹出框根元素类名 | string | - |
| popout-style | 弹出框根元素样式 | StyleValue | - |
| transparent | 遮罩是否透明 | boolean | false |
| show-cancel | 是否显示取消按钮 | boolean | true |
| show-confirm | 是否显示确定按钮 | boolean | true |
KeyboardPopoutSlots
| 插槽 | 描述 | 属性 |
|---|---|---|
| default | 自定义内容 | - |
KeyboardPopoutEmits
| 事件 | 描述 | 类型 |
|---|---|---|
| update:visible | 弹出框显隐时触发 | (visible: boolean) => void |
| cancel | 点击取消按钮时触发 | () => void |
| confirm | 点击确定按钮时触发 | () => void |
| input | 可输入按键点击时触发 | (key: string) => void |
| delete | 点击删除按钮时触发 | () => void |
| toggle | 切换车牌号中英文时触发 | (mode: 'chinese' | 'english') => void |
| update:mode | 切换车牌号中英文时触发 | (mode: 'chinese' | 'english') => void |
| visible-hook | 入场/退场动画状态改变时触发 | (name: TransitionHookName) => void |
| before-enter | 入场动画开始前触发 | () => void |
| enter | 入场动画开始时触发 | () => void |
| after-enter | 入场动画结束时触发 | () => void |
| enter-cancelled | 入场动画取消时触发 | () => void |
| before-leave | 退场动画开始前触发 | () => void |
| leave | 退场动画开始时触发 | () => void |
| after-leave | 退场动画结束时触发 | () => void |
| leave-cancelled | 退场动画取消时触发 | () => void |
KeyboardPopoutExpose
| 属性 | 描述 | 类型 |
|---|---|---|
| shuffle | 重新打乱随机数字键盘 | () => void |
| toggle | 切换车牌号中英文键盘 | (mode?: 'chinese' | 'english') => void |