介绍
组合了键盘、弹出框组件,在弹出框中显示键盘。
代码演示
基础使用
使用 v-model:visible 控制弹出框显隐,通过 input 和 delete 事件处理输入内容。
vue
<template>
<s-list card>
<s-list-item title="当前值" :value="value || '暂无输入'" />
<s-list-item title="打开数字键盘" arrow hover @click="visible = true" />
<s-keyboard-popout
v-model:visible="visible"
title="请输入数字"
transparent
show-shadow
@input="onInput"
@delete="onDelete"
@confirm="onConfirm"
/>
</s-list>
</template>
<script setup lang="ts">
import { toast } from 'sard'
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>
<s-list card>
<s-list-item title="当前值" :value="value || '暂无输入'" />
<s-list-item title="打开数字键盘" arrow hover @click="visible = true" />
<s-keyboard-popout
v-model:visible="visible"
title="请输入数字"
transparent
show-shadow
@input="onInput"
@delete="onDelete"
@confirm="onConfirm"
/>
</s-list>
</template>
<script setup lang="js">
import { toast } from 'sard'
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(`输入完成,值:${value.value}`)
value.value = ''
}
</script>插槽
使用默认插槽可以在键盘上方添加自定义内容。
vue
<template>
<s-list card>
<s-list-item title="打开键盘" arrow hover @click="visible = true" />
<s-keyboard-popout
v-model:visible="visible"
title="安全键盘"
:show-cancel="false"
:show-confirm="false"
@input="onInput"
@delete="onDelete"
@before-enter="value = ''"
>
<div class="px-8 pb-8">
<s-password-input v-model="value" type="underlined" :length="6" custom-keyboard focused />
</div>
</s-keyboard-popout>
</s-list>
</template>
<script setup lang="ts">
import { toast } from 'sard'
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>
<s-list card>
<s-list-item title="打开键盘" arrow hover @click="visible = true" />
<s-keyboard-popout
v-model:visible="visible"
title="安全键盘"
:show-cancel="false"
:show-confirm="false"
@input="onInput"
@delete="onDelete"
@before-enter="value = ''"
>
<div class="px-8 pb-8">
<s-password-input v-model="value" type="underlined" :length="6" custom-keyboard focused />
</div>
</s-keyboard-popout>
</s-list>
</template>
<script setup lang="js">
import { toast } from 'sard'
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(`输入完成,密码:${value.value}`)
}
}
const onDelete = () => {
value.value = value.value.slice(0, -1)
}
</script>类型
可以切换不同的键盘类型,在弹出框中展示数字、小数、身份证和随机数字键盘。
vue
<template>
<s-list card>
<s-list-item title="当前类型" :value="type" />
<s-list-item title="当前值" :value="value || '暂无输入'" />
<s-list-item>
<s-segmented v-model="type" :options="types" />
</s-list-item>
<s-list-item title="打开键盘" arrow hover @click="visible = true" />
<s-keyboard-popout
v-model:visible="visible"
:title="`类型:${type}`"
:type="type"
@input="onInput"
@delete="onDelete"
>
<div class="flex items-center justify-center text-center h-10">
{{ value || '暂无输入' }}
</div>
</s-keyboard-popout>
</s-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>
<s-list card>
<s-list-item title="当前类型" :value="type" />
<s-list-item title="当前值" :value="value || '暂无输入'" />
<s-list-item>
<s-segmented v-model="type" :options="types" />
</s-list-item>
<s-list-item title="打开键盘" arrow hover @click="visible = true" />
<s-keyboard-popout
v-model:visible="visible"
:title="`类型:${type}`"
:type="type"
@input="onInput"
@delete="onDelete"
>
<div class="flex items-center justify-center text-center h-10">
{{ value || '暂无输入' }}
</div>
</s-keyboard-popout>
</s-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>
<s-list card>
<s-list-item title="当前值" :value="value || '暂无输入'" />
<s-list-item title="打开车牌号键盘" arrow hover @click="visible = true" />
<s-keyboard-popout
v-model:visible="visible"
v-model:mode="mode"
title="请输入车牌号"
type="plate"
:disabled-key="disabledKey"
@input="onInput"
@delete="onDelete"
>
<div class="input-wrapper">
<template v-for="(_, index) in maxLength" :key="index">
<div :class="getItemClass(index)">
{{ value[index] || '' }}
</div>
<div v-if="index === 1" class="dot"></div>
</template>
</div>
</s-keyboard-popout>
</s-list>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { plateEnglishLetterKeys, plateSuffixKeys, type KeyboardPlateMode } from 'sard'
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: 5px;
padding: 16px;
}
.input-item {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 30px;
height: 40px;
font-size: 16px;
border-radius: 4px;
border: 1px solid var(--s-border-color);
&.active {
border-color: var(--s-color-primary);
background: rgba(var(--s-color-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: 4px;
height: 4px;
background: currentColor;
border-radius: 50%;
}
@keyframes blink {
0%,
50%,
100% {
opacity: 1;
}
25%,
75% {
opacity: 0;
}
}
</style>vue
<template>
<s-list card>
<s-list-item title="当前值" :value="value || '暂无输入'" />
<s-list-item title="打开车牌号键盘" arrow hover @click="visible = true" />
<s-keyboard-popout
v-model:visible="visible"
v-model:mode="mode"
title="请输入车牌号"
type="plate"
:disabled-key="disabledKey"
@input="onInput"
@delete="onDelete"
>
<div class="input-wrapper">
<template v-for="(_, index) in maxLength" :key="index">
<div :class="getItemClass(index)">
{{ value[index] || '' }}
</div>
<div v-if="index === 1" class="dot"></div>
</template>
</div>
</s-keyboard-popout>
</s-list>
</template>
<script setup lang="js">
import { computed, ref, watch } from 'vue'
import { plateEnglishLetterKeys, plateSuffixKeys } from 'sard'
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: 5px;
padding: 16px;
}
.input-item {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 30px;
height: 40px;
font-size: 16px;
border-radius: 4px;
border: 1px solid var(--s-border-color);
&.active {
border-color: var(--s-color-primary);
background: rgba(var(--s-color-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: 4px;
height: 4px;
background: currentColor;
border-radius: 50%;
}
@keyframes blink {
0%,
50%,
100% {
opacity: 1;
}
25%,
75% {
opacity: 0;
}
}
</style>API
KeyboardPopoutProps
继承 KeyboardProps 。
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| visible | 是否显示弹出框 | boolean | - |
| title | 弹出框标题 | string | - |
| transparent | 遮罩是否透明 | boolean | false |
| show-cancel | 是否显示取消按钮 | boolean | true |
| show-confirm | 是否显示确定按钮 | boolean | true |
| show-shadow | 是否显示阴影,在无遮罩时将弹窗与页面分隔 | boolean | false |
KeyboardPopoutSlots
| 插槽 | 描述 | 属性 |
|---|---|---|
| default | 自定义内容 | - |
KeyboardPopoutEmits
继承 MotionEmits。
| 事件 | 描述 | 类型 |
|---|---|---|
| update:visible | 弹出框显隐时触发 | (visible: boolean) => void |
| close | 点击关闭按钮或遮罩时触发 | () => void |
| cancel | 点击取消按钮时触发 | () => void |
| confirm | 点击确定按钮时触发 | () => void |
| input | 可输入按键点击时触发 | (key: string) => void |
| delete | 点击删除按钮时触发 | () => void |
| toggle | 切换车牌号中英文时触发 | (mode: 'chinese' | 'english') => void |
| update:mode | 切换车牌号中英文时触发 | (mode: 'chinese' | 'english') => void |
KeyboardPopoutExpose
| 属性 | 描述 | 类型 |
|---|---|---|
| shuffle | 重新打乱随机数字键盘 | () => void |
| toggle | 切换车牌号中英文键盘 | (mode?: 'chinese' | 'english') => void |