2025/2/8第一次更新

This commit is contained in:
爱吃咸鱼小猫咪
2025-02-08 18:50:38 +08:00
commit d7af560866
26519 changed files with 5046029 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
import { withNoopInstall } from '../../utils'
import keyboard from './src/keyboard.vue'
export const TnKeyboard = withNoopInstall(keyboard)
export default TnKeyboard
export * from './src/keyboard'
export type { TnKeyboardInstance } from './src/instance'
@@ -0,0 +1,80 @@
export const carKeyboardCnData = [
'京',
'沪',
'粤',
'津',
'冀',
'豫',
'云',
'辽',
'黑',
'湘',
'皖',
'鲁',
'苏',
'浙',
'赣',
'鄂',
'桂',
'甘',
'晋',
'陕',
'蒙',
'吉',
'闽',
'贵',
'渝',
'川',
'青',
'琼',
'宁',
'藏',
'港',
'澳',
'新',
'使',
'学',
'临',
'警',
]
export const carKeyboardEnData = [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'0',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
]
export const carKeyboardSpecialData = ['I', 'O']
@@ -0,0 +1 @@
export * from './car-keyboard-data'
@@ -0,0 +1,2 @@
export * from './use-keyboard-data'
export * from './use-keyboard'
@@ -0,0 +1,99 @@
import { computed } from 'vue'
import {
carKeyboardCnData,
carKeyboardEnData,
carKeyboardSpecialData,
} from '../../libs'
import type { Ref } from 'vue'
import type { CarKeyboardLang, KeyboardProps } from '../keyboard'
type KeyboardDataItem = {
value: string
disabled: boolean
}
type KeyboardData = Array<KeyboardDataItem>
export const useKeyboardData = (
props: KeyboardProps,
carLang: Ref<CarKeyboardLang>
) => {
// 生成普通键盘数据
const generateNormalKeyboardData = (
digit = false,
idcard = false,
random = false
): KeyboardData => {
const numbers = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
if (digit) numbers.push('.')
if (idcard) numbers.push('X')
if (random) numbers.sort(() => Math.random() - 0.5)
return numbers.map((item) => {
return {
value: item,
disabled: props.disabled,
}
})
}
const keyboardData = computed<KeyboardData>(() => {
let data: KeyboardData = []
switch (props.mode) {
case 'number':
data = generateNormalKeyboardData()
break
case 'digit':
data = generateNormalKeyboardData(true)
break
case 'idcard':
data = generateNormalKeyboardData(false, true)
break
case 'random':
data = generateNormalKeyboardData(false, false, true && props.show)
break
}
return data
})
// 车牌键盘数据
const carKeyboardData = computed<KeyboardData[]>(() => {
const data: KeyboardData[] = []
const _fillCarKeyboardData = (rawData: string[]): KeyboardData => {
return rawData.map((item) => {
return {
value: item,
disabled: carKeyboardSpecialData.includes(item) || props.disabled,
}
})
}
if (props.mode === 'car') {
if (carLang.value === 'cn') {
const fillCnData = _fillCarKeyboardData(carKeyboardCnData)
data.push(
fillCnData.slice(0, 10),
fillCnData.slice(10, 20),
fillCnData.slice(20, 30),
fillCnData.slice(30, 37)
)
} else {
const fillEnData = _fillCarKeyboardData(carKeyboardEnData)
data.push(
fillEnData.slice(0, 10),
fillEnData.slice(10, 20),
fillEnData.slice(20, 29),
fillEnData.slice(29, 36)
)
}
}
return data
})
return {
keyboardData,
carKeyboardData,
}
}
@@ -0,0 +1,153 @@
import { nextTick, ref, toRef, watch } from 'vue'
import { useLongPress } from '../../../../hooks'
import { isEmptyVariableInDefault } from '../../../../utils'
import type { SetupContext } from 'vue'
import type { CarKeyboardLang, KeyboardEmits, KeyboardProps } from '../keyboard'
export const useKeyboard = (
props: KeyboardProps,
emits: SetupContext<KeyboardEmits>['emit']
) => {
// 弹出隐藏键盘
const showKeyboard = ref<boolean>(false)
watch(
() => props.show,
(val) => {
showKeyboard.value = val
}
)
// popup弹框关闭事件
const popupCloseEvent = () => {
emits('close')
emits('update:show', false)
}
// 用户输入的值
const inputValue = ref<string>(props.modelValue || '')
watch(
() => props.modelValue,
(val) => (inputValue.value = val)
)
// 键盘点击事件
const keyboardClickEvent = (value: string, disabled: boolean) => {
if (disabled) return
if (props.vibrate) {
uni.vibrateShort()
}
let concatValue = true
if (value === 'confirm') {
popupCloseEvent()
emits('confirm')
return
} else if (value === 'cancel') {
popupCloseEvent()
return
} else {
// 处理小数的0位和小数点位置
if (props.mode === 'digit') {
// 判断当前点击的value是是否位.
if (value === '.') {
// 判断modelValue中是否已经存在.,如果存在,则直接return
if (inputValue.value.includes('.')) {
return
}
// 当小数点在第一位时,前面自动补0
if (inputValue.value === '') {
inputValue.value = '0.'
concatValue = false
}
}
// 判断当前点击的value是否为0
if (value === '0') {
// 判断第一位是否为0,如果是,直接return
if (inputValue.value === '0') {
return
}
}
// 如果第一位为0,第二位不为.,则将第一位替换为当前点击的value
if (inputValue.value === '0' && value !== '.') {
inputValue.value = value
concatValue = false
}
}
if (
props.mode === 'idcard' &&
value === 'X' &&
inputValue.value.includes('X')
) {
return
}
if (concatValue) {
inputValue.value += value
}
}
emits('update:modelValue', inputValue.value)
nextTick(() => {
emits('change', inputValue.value)
})
}
// 单独处理删除事件
const backspaceEvent = () => {
if (inputValue.value === '') {
clearBackspaceLongPressEvent()
return
}
if (props.vibrate) {
uni.vibrateShort()
}
inputValue.value = inputValue.value.slice(0, -1)
emits('backspace')
emits('update:modelValue', inputValue.value)
nextTick(() => {
emits('change', inputValue.value)
})
}
const {
handleLongPressEvent: handleBackspaceEvent,
clearLongPressTimer: clearBackspaceLongPressEvent,
} = useLongPress(backspaceEvent, toRef(props, 'longPressDelete'))
// 车牌键盘中英文切换
const carKeyboardLang = ref<CarKeyboardLang>(
isEmptyVariableInDefault(props.carLang, 'cn')
)
let updateCarLangInner = false
watch(
() => props.carLang,
(val) => {
if (updateCarLangInner) {
updateCarLangInner = false
return
}
carKeyboardLang.value = val
}
)
const carKeyboardSwitchLang = () => {
if (carKeyboardLang.value === 'cn') {
carKeyboardLang.value = 'en'
} else {
carKeyboardLang.value = 'cn'
}
updateCarLangInner = true
emits('update:carLangMode', carKeyboardLang.value)
}
return {
showKeyboard,
carKeyboardLang,
popupCloseEvent,
keyboardClickEvent,
handleBackspaceEvent,
clearBackspaceLongPressEvent,
carKeyboardSwitchLang,
}
}
@@ -0,0 +1,3 @@
import type Keyboard from './keyboard.vue'
export type TnKeyboardInstance = InstanceType<typeof Keyboard>
@@ -0,0 +1,98 @@
import { CHANGE_EVENT, UPDATE_MODEL_EVENT, ZIndex } from '../../../constants'
import { buildProps, isBoolean, isString } from '../../../utils'
import type { ExtractPropTypes } from 'vue'
const keyboardMode = ['number', 'digit', 'idcard', 'random', 'car'] as const
const carKeyboardLang = ['cn', 'en'] as const
export const keyboardProps = buildProps({
/**
* @description 键盘输入绑定的值
*/
modelValue: {
type: String,
default: '',
},
/**
* @description 是否显示键盘
*/
show: {
type: Boolean,
default: false,
},
/**
* @description 键盘类型
*/
mode: {
type: String,
values: keyboardMode,
default: 'number',
},
/**
* @description 车牌键盘中/英文模式,在 `mode` 为 `car` 时生效
*/
carLang: {
type: String,
values: carKeyboardLang,
default: 'cn',
},
/**
* @description 点击按钮是否有震动效果
*/
vibrate: {
type: Boolean,
default: true,
},
/**
* @description 是否允许长按删除删除内容
*/
longPressDelete: {
type: Boolean,
default: true,
},
/**
* @description 禁止键盘输入
*/
disabled: Boolean,
/**
* @description 点击遮罩关闭键盘
*/
overlayCloseable: {
type: Boolean,
default: true,
},
/**
* @description ZIndex
*/
zIndex: {
type: Number,
default: ZIndex.popup,
},
})
export const keyboardEmits = {
[UPDATE_MODEL_EVENT]: (value: string) => isString(value),
'update:show': (value: boolean) => isBoolean(value),
'update:carLangMode': (value: CarKeyboardLang) =>
value === 'cn' || value === 'en',
[CHANGE_EVENT]: (value: string) => isString(value),
/**
* @description 键盘关闭事件
*/
close: () => true,
/**
* @description 退格事件
*/
backspace: () => true,
/**
* @description 确认事件
*/
confirm: () => true,
}
export type KeyboardProps = ExtractPropTypes<typeof keyboardProps>
export type KeyboardEmits = typeof keyboardEmits
export type KeyboardMode = (typeof keyboardMode)[number]
export type CarKeyboardLang = (typeof carKeyboardLang)[number]
@@ -0,0 +1,162 @@
<script lang="ts" setup>
import TnPopup from '../../popup/src/popup.vue'
import TnIcon from '../../icon/src/icon.vue'
import { useNamespace } from '../../../hooks'
import { keyboardEmits, keyboardProps } from './keyboard'
import { useKeyboard, useKeyboardData } from './composables'
const props = defineProps(keyboardProps)
const emits = defineEmits(keyboardEmits)
const ns = useNamespace('keyboard')
const {
showKeyboard,
carKeyboardLang,
popupCloseEvent,
keyboardClickEvent,
handleBackspaceEvent,
clearBackspaceLongPressEvent,
carKeyboardSwitchLang,
} = useKeyboard(props, emits)
const { keyboardData, carKeyboardData } = useKeyboardData(
props,
carKeyboardLang
)
</script>
<template>
<TnPopup
v-model="showKeyboard"
open-direction="bottom"
bg-color="transparent"
radius=""
overlay
:overlay-opacity="0"
:overlay-closeable="overlayCloseable"
:safe-area-inset-bottom="false"
:z-index="zIndex"
@close="popupCloseEvent"
>
<view class="tn-u-safe-area" :class="[ns.b(), ns.e(mode)]">
<!-- 普通键盘 -->
<template v-if="mode !== 'car'">
<!-- 左边操作区域 -->
<view class="left" :class="ns.e('normal-container')">
<view
v-for="(item, index) in keyboardData"
:key="index"
class="normal-item"
:class="[
{
full: keyboardData.length === 10 && index === 9,
fill: keyboardData.length === 11 && index === 9,
disabled: item.disabled,
},
]"
:hover-class="item.disabled ? '' : 'keyboard-btn-hover'"
:hover-stay-time="150"
@tap.stop="keyboardClickEvent(item.value, item.disabled)"
>
{{ item.value }}
</view>
</view>
<!-- 右边操作区域 -->
<view class="right" :class="ns.e('normal-container')">
<view
class="normal-item delete"
hover-class="keyboard-btn-hover"
:hover-stay-time="150"
@touchstart.stop.prevent="handleBackspaceEvent"
@touchend.stop.prevent="clearBackspaceLongPressEvent"
>
<TnIcon name="backspace-fill" />
</view>
<view
class="normal-item confirm"
hover-class="keyboard-btn-hover"
:hover-stay-time="150"
@tap.stop="keyboardClickEvent('confirm', false)"
>
确认
</view>
</view>
</template>
<!-- 汽车键盘 -->
<template v-if="mode === 'car'">
<!-- 顶部操作栏 -->
<view :class="[ns.e('car-top-operation-bar')]">
<view
class="operation-btn cancel"
@tap.stop="keyboardClickEvent('cancel', false)"
>
取消
</view>
<view
class="operation-btn confirm"
@tap.stop="keyboardClickEvent('confirm', false)"
>
确认
</view>
</view>
<view :class="[ns.e('car-container')]">
<!-- 行数据 -->
<view
v-for="(item, index) in carKeyboardData"
:key="index"
class="column-data"
>
<!-- 如果是最后一行显示切换按钮 -->
<view
v-if="index === 3"
class="car-item switch-mode"
hover-class="keyboard-btn-hover"
:hover-stay-time="150"
@tap.stop="carKeyboardSwitchLang"
>
{{ carKeyboardLang === 'cn' ? 'ABC' : '返回' }}
</view>
<!-- 列数据 -->
<view
v-for="(rowData, rowIndex) in item"
:key="rowIndex"
class="car-item"
:class="[{ disabled: rowData.disabled }]"
:hover-class="rowData.disabled ? '' : 'keyboard-btn-hover'"
:hover-stay-time="150"
@tap.stop="keyboardClickEvent(rowData.value, rowData.disabled)"
>
{{ rowData.value }}
</view>
<!-- 如果是最后一行显示删除按钮 -->
<view
v-if="index === 3"
class="car-item delete"
hover-class="keyboard-btn-hover"
:hover-stay-time="150"
@touchstart.stop.prevent="handleBackspaceEvent"
@touchend.stop.prevent="clearBackspaceLongPressEvent"
>
<TnIcon name="backspace-fill" />
</view>
</view>
</view>
</template>
</view>
</TnPopup>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/keyboard.scss';
</style>
<style>
/* 按钮点击效果 */
.keyboard-btn-hover {
box-shadow: inset 0rpx 0rpx 30rpx 2rpx rgba(0, 0, 0, 0.8);
opacity: 0.8;
}
</style>