Files
2026-09-24 16:25:22 +08:00

406 lines
10 KiB
Plaintext

<script lang="ts" setup>
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue"
import { getDefaultColor } from "../../core/util/xCoreColorUtil.uts"
import { xConfig, xProvitae } from "../../config/xConfig.uts"
import { checkIsCssUnit, getUnit } from "../../core/util/xCoreUtil.uts"
/**
* @name 密码键盘 xKeyboard
* @description 密码键盘,如果你只是要单纯的数字键盘见x-keyboard-number
* @page /pages/index/keyboard
* @category 表单组件
* @constant 平台兼容
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
| --- | --- | --- | --- | --- | --- | --- | --- |
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
*/
defineOptions({name:"xKeyboard"})
const i18n = xConfig.i18n
defineSlots<{
default(props:{show:boolean}): any
}>()
const emits = defineEmits([
/**
* 值变化时触发
* @param {string} value - 当前值
*/
'change',
/**
* 变量控制打开状态
* 等同v-model:model-show
*/
'update:modelShow',
/**
* 确认时触发
* @param {string} value - 当前值
*/
'confirm',
/**
* 关闭取消时触发
* @param {string} value - 当前值
*/
'cancel',
'update:modelValue'
])
type xKeyboardPropsType = {
/**
* 当前输入的值
*/
modelValue: string,
/**
* 最大长度
*/
maxLen: number,
/**
* 当前打开的状态。
* 等同v-model:model-show
*/
modelShow: boolean,
/**
* 顶部标题,默认:安全键盘请放心输入
*/
title: string,
/**
* 主按钮色,空值取全局主题
*/
color: string,
/**
* 按钮背景
*/
btnColor: string,
/**
* 键盘背景
*/
bgColor: string,
/**
* 文字颜色
*/
fontColor: string,
/**
* 点击确认是否保持键盘不收起
*/
hold: boolean
}
const props = withDefaults(defineProps<xKeyboardPropsType>(), {
modelValue: "",
maxLen: 9,
modelShow: false,
title: "",
color: "",
btnColor: 'white',
bgColor: 'info',
fontColor: '#3b3b3b',
hold: false
})
// 响应式数据
const show = ref(false)
const nowValue = ref("")
const model = ref<'number' | 'abc'>('abc')
const abcList = ref([
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'],
['shift', 'z', 'x', 'c', 'v', 'b', 'n', 'm', 'del'],
['123', '空格', '确认'],
])
const numList = ref([
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
['#', '/', ':', ';', '(', ')', '^', '*', '+'],
['-', '=', '\\', '|', '~', '$', '&', '.', ',', 'del'],
['abc', '%', '?', '!', '{', '}', '确认'],
])
const isShift = ref(false)
const tid = ref(0)
// 计算属性
const _hold = computed((): boolean => props.hold)
const _color = computed((): string => {
if (props.color == '') return getDefaultColor(xConfig.color)
return getDefaultColor(props.color)
})
const _btnColor = computed((): string => {
if (xConfig.dark == 'dark') return xConfig.inputDarkColor
return getDefaultColor(props.btnColor)
})
const _btnBorderColor = computed((): string => {
if (xConfig.dark == 'dark') return xConfig.borderDarkColor
return "#f5f5f5"
})
const _bgColor = computed((): string => {
return getDefaultColor(props.bgColor)
})
const _fontColor = computed((): string => {
if (xConfig.dark == 'dark') return "#ffffff"
return getDefaultColor(props.fontColor)
})
const _title = computed((): string => {
if(props.title=='') return i18n.t('tmui4x.keyboard.placeholder')
return props.title
})
// 方法
function getFontSize(k: string): string {
return checkIsCssUnit(k, xConfig.unit)
}
function openShow(): void {
show.value = true;
/**
* 变量控制打开状态
* 等同v-model:model-show
*/
emits('update:modelShow', true)
}
function onCancel(): void {
/**
* 关闭取消时触发
*/
emits('cancel', nowValue.value);
}
function onClose(): void {
emits('update:modelShow', false)
}
function ok(): void {
/**
* 点击确认时触发
*/
emits('confirm', nowValue.value);
if(!_hold.value){
show.value = false;
emits('update:modelShow', false)
}
}
function del(): void {
if (nowValue.value.split('').length == 0) return;
let stp = nowValue.value.split('');
stp = stp.slice(0, stp.length - 1)
nowValue.value = stp.join("")
/**
* 等同v-model
*/
emits('update:modelValue', nowValue.value);
/**
* 值变化时触发
* @paramt {string} value
*/
emits('change', nowValue.value);
}
function itemClick(value: string): void {
let value_convaer = value
if (value == 'abc') {
model.value = 'abc'
return;
} else if (value == '123') {
model.value = 'number'
return;
} else if (value == 'shift') {
isShift.value = !isShift.value
return;
} else if (value == 'del') {
del()
return;
} else if (value == '确认') {
ok()
return;
} else if (value == '空格') {
value_convaer = " "
}
if (isShift.value) {
value_convaer = value_convaer.toLocaleUpperCase()
}
let isMaxvalu = nowValue.value.split('').length >= props.maxLen;
if (isMaxvalu) {
uni.showToast({ title: '最多输入' + props.maxLen.toString() + '个字符', icon: 'error' })
return;
}
nowValue.value = nowValue.value + value_convaer;
/**
* 等同v-model
*/
emits('update:modelValue', nowValue.value);
/**
* 值变化时触发
* @paramt {string} value
*/
emits('change', nowValue.value);
}
function getFontColor(value: string): string {
if (value == "确认") return 'white'
if (value == 'del') return _color.value
if (value == 'shift' && isShift.value) return _color.value
return _fontColor.value
}
// 监听器
watch((): string => props.modelValue, (newvalue: string) => {
if (newvalue == nowValue.value) return;
nowValue.value = newvalue
})
watch((): boolean => props.modelShow, (newValue: boolean) => {
if (newValue == show.value) return;
show.value = newValue
})
// 生命周期
onMounted(() => {
nowValue.value = props.modelValue
if (props.modelShow) {
tid.value = setTimeout(function () {
show.value = props.modelShow
}, 200);
}
})
onBeforeUnmount(() => {
clearTimeout(tid.value)
})
</script>
<template>
<view @click="openShow">
<!--
@slot 插槽,默认触发打开选择器。你的默认布局可以放置在这里。
@prop {boolean} show - 控制打开关闭状态
-->
<slot :show="show"></slot>
</view>
<x-drawer @close="onClose" :widthCoverCenter="true" :disabled-scroll="true" :bgColor="_bgColor" size="auto" overflayBgColor="rgba(0,0,0,0)" :title="title"
@cancel="onCancel" v-model:show="show" :show-close="true">
<template v-slot:title>
<view style="height: 44px;display: flex;justify-content: center;align-items: center;flex-direction: row;">
<text :style="{fontSize:nowValue.split('').length>0?getFontSize('16'):getFontSize('12'),color:_fontColor}">{{nowValue.split('').length>0?nowValue:_title}}</text>
</view>
</template>
<template v-slot:default>
<view v-if="model=='abc'" class="xKeyboardNumber">
<view class="xKeyboardLeft">
<view v-for="(item,index) in abcList" :key="index" class="xKeyboardLeftLine">
<view @click="itemClick(item2)"
v-for="(item2,index2) in item"
:key="index2"
class="xKeyboardItem" :hover-start-time="20" :hover-stay-time="250"
hover-class="xKeyboardHover"
:style="{
backgroundColor:item2=='确认'?_color:_btnColor,
flex:item2=='空格'?'2':'1',
border:`1px solid ${_btnBorderColor}`
}">
<text v-if="item2!='shift'&&item2!=='del'" :style="{color:getFontColor(item2)}"
class="xKeyboardText">
{{(item2=='确认'?i18n.t('tmui4x.keyboard.confirm'):'')}}
{{(item2!='确认'&&item2!='空格'?(isShift?item2.toLocaleUpperCase():item2):'')}}
{{(item2=='空格'?i18n.t('tmui4x.keyboard.space'):'')}}
</text>
<x-icon v-if="item2=='del'" :color="_color" name="delete-back-2-line"
font-size="19"></x-icon>
<x-icon v-if="item2=='shift'" :color="isShift?_color:_fontColor" name="upload-fill"
font-size="19" ></x-icon>
</view>
</view>
</view>
</view>
<view v-if="model=='number'" class="xKeyboardNumber">
<view class="xKeyboardLeft">
<view v-for="(item,index) in numList" :key="index" class="xKeyboardLeftLine">
<view @click="itemClick(item2)" v-for="(item2,index2) in item" :key="index2"
class="xKeyboardItem" :hover-start-time="20" :hover-stay-time="250"
hover-class="xKeyboardHover" :style="{
backgroundColor:item2=='确认'?_color:_btnColor,
flex:item2=='确认'||item2=='abc'?'2':'1',
border:`1px solid ${_btnBorderColor}`
}">
<text v-if="item2!='shift'&&item2!=='del'" :style="{color:getFontColor(item2)}"
class="xKeyboardText">
<!-- #ifdef WEB -->
{{(item2=='\\'?'\\\\':'')}}
{{(item2=='确认'?i18n.t('tmui4x.keyboard.confirm'):'')}}
{{(item2!='确认'&&item2!='\\'?item2:'')}}
<!-- #endif -->
<!-- #ifdef APP || MP-WEIXIN -->
{{(item2=='确认'?i18n.t('tmui4x.keyboard.confirm'):item2)}}
<!-- #endif -->
</text>
<x-icon v-if="item2=='del'" :color="_color" name="delete-back-2-line"
font-size="19"></x-icon>
<x-icon v-if="item2=='shift'" :color="isShift?_color:_fontColor" name="upload-fill"
font-size="19" ></x-icon>
</view>
</view>
</view>
</view>
<view style="height:12px"></view>
</template>
</x-drawer>
</template>
<style scoped>
.xKeyboardText {
font-weight: bold;
font-size: 16px;
}
.xKeyboardHover {
opacity: 0.6;
}
.xKeyboardLeftLine {
display: flex;
flex-direction: row;
}
.xKeyboardNumber {
display: flex;
flex-direction: row;
}
.xKeyboardLeft {
flex: 1;
}
.xKeyboardItem {
height: 50px;
/* background-color: white; */
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-right: 5px;
margin-bottom: 5px;
border-radius: 6px;
}
.xKeyboardItemNoright {
margin-right: 0px;
}
.xKeyboardRight {
width: 70px;
display: flex;
flex-direction: column;
}
</style>