版本发布
This commit is contained in:
@@ -0,0 +1,407 @@
|
||||
<template>
|
||||
<cl-popup
|
||||
:title="title"
|
||||
:swipe-close-threshold="100"
|
||||
:pt="{
|
||||
inner: {
|
||||
className: parseClass([
|
||||
[isDark, '!bg-surface-700', '!bg-surface-100'],
|
||||
pt.popup?.className
|
||||
])
|
||||
},
|
||||
mask: {
|
||||
className: '!bg-transparent'
|
||||
}
|
||||
}"
|
||||
v-model="visible"
|
||||
>
|
||||
<view class="cl-keyboard-number" :class="[pt.className]">
|
||||
<slot name="value" :value="value">
|
||||
<view
|
||||
v-if="showValue"
|
||||
class="cl-keyboard-number__value"
|
||||
:class="[pt.value?.className]"
|
||||
>
|
||||
<text
|
||||
class="cl-keyboard-number__value-text dark:!text-white"
|
||||
v-if="value != ''"
|
||||
>{{ value }}</text
|
||||
>
|
||||
<text class="cl-keyboard-number__value-placeholder" v-else>{{
|
||||
placeholder
|
||||
}}</text>
|
||||
</view>
|
||||
</slot>
|
||||
|
||||
<view class="cl-keyboard-number__list">
|
||||
<cl-row :gutter="10">
|
||||
<cl-col :span="6" v-for="item in list" :key="item">
|
||||
<view
|
||||
class="cl-keyboard-number__item"
|
||||
:class="[
|
||||
`is-keycode-${item}`,
|
||||
{
|
||||
'is-dark': isDark,
|
||||
'is-empty': item == ''
|
||||
},
|
||||
pt.item?.className
|
||||
]"
|
||||
hover-class="opacity-50"
|
||||
:hover-stay-time="50"
|
||||
@touchstart.stop="onCommand(item)"
|
||||
>
|
||||
<slot name="item" :item="item">
|
||||
<cl-icon
|
||||
v-if="item == 'delete'"
|
||||
name="delete-back-2-line"
|
||||
:size="36"
|
||||
></cl-icon>
|
||||
|
||||
<view
|
||||
v-else-if="item == 'confirm'"
|
||||
class="cl-keyboard-number__item-confirm"
|
||||
>
|
||||
<text class="cl-keyboard-number__item-text">{{
|
||||
confirmText
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
<text
|
||||
v-else
|
||||
class="cl-keyboard-number__item-text dark:!text-white"
|
||||
>{{ item }}</text
|
||||
>
|
||||
</slot>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</view>
|
||||
</view>
|
||||
</cl-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUi } from "../../hooks";
|
||||
import type { PassThroughProps } from "../../types";
|
||||
import type { ClPopupProps } from "../cl-popup/props";
|
||||
import { ref, computed, watch, type PropType } from "vue";
|
||||
import { $t, t } from "@/locale";
|
||||
import { isDark, parseClass, parsePt } from "@/cool";
|
||||
import { vibrate } from "@/uni_modules/cool-vibrate";
|
||||
|
||||
defineOptions({
|
||||
name: "cl-keyboard-number"
|
||||
});
|
||||
|
||||
defineSlots<{
|
||||
value(props: { value: string }): any;
|
||||
item(props: { item: string }): any;
|
||||
}>();
|
||||
|
||||
const props = defineProps({
|
||||
// 透传样式配置
|
||||
pt: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
// v-model绑定的值
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
// 键盘类型,支持number、digit、idcard
|
||||
type: {
|
||||
type: String as PropType<"number" | "digit" | "idcard">,
|
||||
default: "digit"
|
||||
},
|
||||
// 弹窗标题
|
||||
title: {
|
||||
type: String,
|
||||
default: t("数字键盘")
|
||||
},
|
||||
// 输入框占位符
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: t("安全键盘,请放心输入")
|
||||
},
|
||||
// 最大输入长度
|
||||
maxlength: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
// 确认按钮文本
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: t("确定")
|
||||
},
|
||||
// 是否显示输入值
|
||||
showValue: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否输入即绑定
|
||||
inputImmediate: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
// 定义事件发射器,支持v-model和change事件
|
||||
const emit = defineEmits(["update:modelValue", "change"]);
|
||||
|
||||
// 样式穿透类型
|
||||
type PassThrough = {
|
||||
className?: string;
|
||||
item?: PassThroughProps;
|
||||
value?: PassThroughProps;
|
||||
popup?: ClPopupProps;
|
||||
};
|
||||
|
||||
// 样式穿透计算
|
||||
const pt = computed(() => parsePt<PassThrough>(props.pt));
|
||||
|
||||
// 获取UI相关的工具方法
|
||||
const ui = useUi();
|
||||
|
||||
// 控制弹窗显示/隐藏
|
||||
const visible = ref(false);
|
||||
|
||||
// 输入框当前值,双向绑定
|
||||
const value = ref(props.modelValue);
|
||||
|
||||
// 最大输入长度
|
||||
const maxlength = computed(() => {
|
||||
if (props.type == "idcard") {
|
||||
return 18;
|
||||
}
|
||||
|
||||
return props.maxlength;
|
||||
});
|
||||
|
||||
// 数字键盘的按键列表,包含数字、删除、00和小数点
|
||||
const list = computed(() => {
|
||||
const arr = [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"delete",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"",
|
||||
"00",
|
||||
"0",
|
||||
"",
|
||||
"confirm"
|
||||
];
|
||||
|
||||
// 数字键盘显示为小数点 "."
|
||||
if (props.type == "digit") {
|
||||
arr[14] = ".";
|
||||
}
|
||||
|
||||
// 身份证键盘显示为 "X"
|
||||
if (props.type == "idcard") {
|
||||
arr[14] = "X";
|
||||
}
|
||||
|
||||
return arr;
|
||||
});
|
||||
|
||||
// 打开键盘弹窗
|
||||
function open() {
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
// 关闭键盘弹窗
|
||||
function close() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
// 处理键盘按键点击事件
|
||||
function onCommand(key: string) {
|
||||
// 震动
|
||||
vibrate(1);
|
||||
|
||||
// 确认按钮逻辑
|
||||
if (key == "confirm") {
|
||||
if (value.value == "") {
|
||||
ui.showToast({
|
||||
message: t("请输入内容")
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果最后一位是小数点,去掉
|
||||
if (value.value.endsWith(".")) {
|
||||
value.value = value.value.slice(0, -1);
|
||||
}
|
||||
|
||||
// 身份证号码正则校验(支持15位和18位,18位末尾可为X/x)
|
||||
if (props.type == "idcard") {
|
||||
if (
|
||||
!/^(^[1-9]\d{5}(18|19|20)?\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}(\d|X|x)?$)$/.test(
|
||||
value.value
|
||||
)
|
||||
) {
|
||||
ui.showToast({
|
||||
message: t("身份证号码格式不正确")
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 触发v-model和change事件
|
||||
emit("update:modelValue", value.value);
|
||||
emit("change", value.value);
|
||||
|
||||
// 关闭弹窗
|
||||
close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 删除键,去掉最后一位
|
||||
if (key == "delete") {
|
||||
value.value = value.value.slice(0, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 超过最大输入长度,提示并返回
|
||||
if (value.value.length >= maxlength.value) {
|
||||
ui.showToast({
|
||||
message: $t("最多输入{maxlength}位", {
|
||||
maxlength: maxlength.value
|
||||
})
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理小数点输入,已存在则不再添加
|
||||
if (key == ".") {
|
||||
if (value.value.includes(".")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.value == "") {
|
||||
value.value = "0.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理00键,首位不能输入00,只能输入0
|
||||
if (key == "00") {
|
||||
if (value.value.length + 2 > maxlength.value) {
|
||||
value.value += "0";
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.value == "") {
|
||||
value.value = "0";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (key == "00" || key == "0") {
|
||||
if (value.value == "" || value.value == "0") {
|
||||
value.value = "0";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 其他按键直接拼接到value
|
||||
value.value += key;
|
||||
}
|
||||
|
||||
watch(value, (val: string) => {
|
||||
// 如果输入即绑定,则立即更新绑定值
|
||||
if (props.inputImmediate) {
|
||||
emit("update:modelValue", val);
|
||||
emit("change", val);
|
||||
}
|
||||
});
|
||||
|
||||
// 监听外部v-model的变化,保持内部value同步
|
||||
watch(
|
||||
computed(() => props.modelValue),
|
||||
(val: string) => {
|
||||
value.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
close
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cl-keyboard-number {
|
||||
padding: 0 20rpx 20rpx 20rpx;
|
||||
|
||||
&__value {
|
||||
@apply flex flex-row items-center justify-center;
|
||||
height: 60rpx;
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
&-text {
|
||||
@apply text-2xl text-surface-700;
|
||||
}
|
||||
|
||||
&-placeholder {
|
||||
@apply text-md text-surface-400;
|
||||
}
|
||||
}
|
||||
|
||||
&__list {
|
||||
@apply relative;
|
||||
}
|
||||
|
||||
&__item {
|
||||
@apply flex items-center justify-center rounded-xl bg-white;
|
||||
height: 100rpx;
|
||||
margin-top: 10rpx;
|
||||
overflow: visible;
|
||||
|
||||
&.is-dark {
|
||||
@apply bg-surface-800;
|
||||
}
|
||||
|
||||
&-text {
|
||||
@apply text-lg;
|
||||
}
|
||||
|
||||
&.is-keycode-delete {
|
||||
@apply bg-surface-200;
|
||||
|
||||
&.is-dark {
|
||||
@apply bg-surface-800;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-keycode-confirm {
|
||||
@apply bg-transparent relative;
|
||||
|
||||
.cl-keyboard-number__item-text {
|
||||
@apply text-white;
|
||||
}
|
||||
}
|
||||
|
||||
&-confirm {
|
||||
@apply flex flex-col items-center justify-center;
|
||||
@apply bg-primary-500 absolute w-full rounded-xl;
|
||||
height: 320rpx;
|
||||
top: -220rpx;
|
||||
}
|
||||
|
||||
&.is-empty {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user