You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
5.6 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<wd-popup
v-model="show"
position="bottom"
:z-index="zIndex"
:safe-area-inset-bottom="safeAreaInsetBottom"
:modal-style="modal ? '' : 'opacity: 0;'"
:modal="hideOnClickOutside"
:lockScroll="lockScroll"
@click-modal="handleClose"
>
<view :class="`wd-number-keyboard ${customClass}`" :style="customStyle">
<view class="wd-number-keyboard__header" v-if="showTitle">
<text class="wd-number-keyboard__title">{{ title }}</text>
<view class="wd-number-keyboard__close" hover-class="wd-number-keyboard__close--hover" v-if="showClose" @click="handleClose">
<text>{{ closeText }}</text>
</view>
</view>
<view class="wd-number-keyboard__body">
<view class="wd-number-keyboard__keys">
<wd-key v-for="key in keys" :key="key.text" :text="key.text" :type="key.type" :wider="key.wider" @press="handlePress"></wd-key>
</view>
<view class="wd-number-keyboard__sidebar" v-if="mode === 'custom'">
<wd-key v-if="showDeleteKey" large :text="deleteText" type="delete" @press="handlePress"></wd-key>
<wd-key large :text="closeText" type="close" :loading="closeButtonLoading" @press="handlePress"></wd-key>
</view>
</view>
</view>
</wd-popup>
</template>
<script lang="ts">
export default {
name: 'wd-number-keyboard',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue'
import WdKey from './key/index.vue'
type KeyboardMode = 'default' | 'custom'
type KeyType = '' | 'delete' | 'extra' | 'close'
interface Key {
text?: number | string // key文本
type?: KeyType // key的类型
wider?: boolean // 是否占2个key的宽度
}
interface Props {
// 是否可见
visible?: boolean
// 绑定的值
modelValue?: string
// 标题
title?: string
// 键盘模式
mode?: KeyboardMode
// 层级
zIndex?: number
// 最大长度
maxlength?: number
// 是否显示删除键
showDeleteKey?: boolean
// 是否随机键盘按键顺序
randomKeyOrder?: boolean
// 确认按钮文本
closeText?: string
// 删除按钮文本
deleteText?: string
// 关闭按钮是否显示加载状态
closeButtonLoading?: boolean
// 是否显示蒙层
modal?: boolean
// 是否在点击外部时收起键盘
hideOnClickOutside?: boolean
// 是否锁定滚动
lockScroll?: boolean
// 是否在底部安全区域内
safeAreaInsetBottom?: boolean
// 额外按键
extraKey?: string | string[]
// 自定义样式
customStyle?: string
// 自定义类名
customClass?: string
}
const props = withDefaults(defineProps<Props>(), {
visible: false,
modelValue: '',
mode: 'default',
zIndex: 100,
maxlength: Infinity,
showDeleteKey: true,
randomKeyOrder: false,
closeButtonLoading: false,
modal: false,
hideOnClickOutside: true,
lockScroll: true,
safeAreaInsetBottom: true,
extraKey: '',
customClass: '',
customStyle: ''
})
const show = ref(props.visible)
watch(
() => props.visible,
(newValue) => {
show.value = newValue
}
)
const keys = computed(() => (props.mode === 'custom' ? genCustomKeys() : genDefaultKeys()))
const showClose = computed(() => {
return props.closeText && props.mode === 'default'
})
const showTitle = computed(() => {
return props.title || showClose.value
})
const emit = defineEmits(['update:visible', 'input', 'close', 'delete', 'update:modelValue'])
/**
* 随机打乱数组的顺序
* @param arr 要打乱顺序的数组
* @returns 打乱顺序后的数组
*/
function shuffleArray<T>(arr: T[]): T[] {
const newArr = [...arr]
for (let i = newArr.length - 1; i > 0; i--) {
// 生成一个随机索引 j范围是 [0, i]
const j = Math.floor(Math.random() * (i + 1))
// 交换索引 i 和 j 处的元素
;[newArr[i], newArr[j]] = [newArr[j], newArr[i]]
}
return newArr
}
function genBasicKeys(): Key[] {
const keys = Array.from({ length: 9 }, (_, i) => ({ text: i + 1 }))
// 如果需要随机顺序,则调用 shuffleArray 方法打乱数组顺序
return props.randomKeyOrder ? shuffleArray(keys) : keys
}
function genDefaultKeys(): Key[] {
return [
...genBasicKeys(),
{ text: props.extraKey as string, type: 'extra' },
{ text: 0 },
{
// 根据条件是否显示删除键的文本和类型
text: props.showDeleteKey ? props.deleteText : '',
type: props.showDeleteKey ? 'delete' : ''
}
]
}
function genCustomKeys(): Key[] {
const keys = genBasicKeys()
const extraKeys = Array.isArray(props.extraKey) ? props.extraKey : [props.extraKey]
if (extraKeys.length === 1) {
// 如果只有一个额外按键则添加一个宽度较大的数字0和一个额外按键
keys.push({ text: 0, wider: true }, { text: extraKeys[0], type: 'extra' })
} else if (extraKeys.length === 2) {
// 如果有两个额外按键则添加两个额外按键和一个数字0
keys.push({ text: extraKeys[0], type: 'extra' }, { text: 0 }, { text: extraKeys[1], type: 'extra' })
}
return keys
}
const handleClose = () => {
emit('close')
emit('update:visible', false)
}
const handlePress = (text: string, type: KeyType) => {
if (text === '' && type === 'extra') {
return handleClose()
}
const value = props.modelValue
if (type === 'delete') {
emit('delete')
emit('update:modelValue', value.slice(0, value.length - 1))
} else if (type === 'close') {
handleClose()
} else if (value.length < +props.maxlength) {
emit('input', text)
emit('update:modelValue', value + text)
}
}
</script>
<style lang="scss">
@import './index.scss';
</style>