| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- <template>
- <FlexCol>
- <view :style="{
- ...themeStyles.view.value,
- ...props.innerStyle,
- }">
- <NumberInputBox
- v-for="i of numberCount"
- :key="i"
- :index="i"
- :value="modelValue.charAt(i - 1)"
- :active="(isFocus && (i === 1)) || Boolean(modelValue.charAt(i - 1) || Boolean(modelValue.charAt(i - 2)))"
- :autoSize="autoSize"
- :isPassword="isPassword"
- :disableKeyPad="disableKeyPad"
- :showCursur="showCursur"
- :gutter="gutter"
- :boxStyle="boxStyle"
- :textStyle="textStyle"
- :borderWidth="borderWidth"
- :borderType="borderType"
- :borderColor="borderColor"
- :backgroundColor="backgroundColor"
- :activeBorderColor="activeBorderColor"
- :activeBackgroundColor="activeBackgroundColor"
- @click="onBoxClicked(i - 1)"
- />
- </view>
- <input
- v-if="useSystemInput"
- :style="themeStyles.invisibleInput.value"
- ref="inputRef"
- :value="textShadow"
- :focus="isFocus"
- :password="isPassword"
- type="number"
- @input="onInputChangeText(($event as any).detail.value)"
- @blur="onBlurInput"
- />
- <text v-if="info" :style="themeStyles.info.value">{{info}}</text>
- <text v-if="errorMessage" :style="themeStyles.errorMessage.value">{{errorMessage}}</text>
- <NumberKeyBoard
- v-model:show="showInput"
- @input="onInput"
- @delete="onDelete"
- />
- </FlexCol>
- </template>
- <script setup lang="ts">
- import { onMounted, ref, watch } from 'vue';
- import NumberKeyBoard from '../keyboard/NumberKeyBoard.vue';
- import NumberInputBox from './NumberInputBox.vue';
- import { propGetThemeVar, useTheme, type TextStyle, type ViewStyle } from '../theme/ThemeDefine';
- import { DynamicColor, DynamicSize } from '../theme/ThemeTools';
- import FlexCol from '../layout/FlexCol.vue';
- /**
- * * underline 下划线
- * * box 外边框样式
- * * flat 仅有背景颜色样式
- * * activeBorderBox 外边框样式(激活状态)和背景颜色样式
- */
- export type NumberInputBorderType = 'underline'|'box'|'flat'|'activeBorderBox';
- export interface NumberInputProps {
- /**
- * 数值
- */
- modelValue: string;
- /**
- * 底部错误提示文案,为空时不展示
- */
- errorMessage?: string;
- /**
- * 数字位数
- * @default 6
- */
- numberCount?: number;
- /**
- * 是否是密码
- * @default false
- */
- isPassword?: boolean;
- /**
- * 是否在组件初始化时激活键盘
- * @default false
- */
- startFocus?: boolean;
- /**
- * 使用系统输入键盘,否则使用 NumberKeyBoard 作为输入键盘,默认是
- * @default true
- */
- useSystemInput?: boolean;
- /**
- * 输入框下方文字提示
- */
- info?: string;
- /**
- * 输入框格子之间的间距
- * @default 0
- */
- gutter?: number;
- /**
- * 是否自动调整宽度
- * @default false
- */
- autoSize?: boolean;
- /**
- * 格子的边框
- * @default 'activeBorderBox'
- */
- borderType?: NumberInputBorderType;
- /**
- * 格子的边框颜色
- * @default 'border.default'
- */
- borderColor?: string;
- /**
- * 格子的背景颜色
- * @default 'background.input'
- */
- backgroundColor?: string;
- /**
- * 格子的边框宽度
- * @default 1.5
- */
- borderWidth?: number;
- /**
- * 已输入格子的边框颜色
- * @default 'primary'
- */
- activeBorderColor?: string;
- /**
- * 已输入格子的背景颜色
- * @default 'background.primary'
- */
- activeBackgroundColor?: string;
- /**
- * 格子样式
- */
- boxStyle?: ViewStyle;
- /**
- * 是否禁用点击打开键盘
- * @default false
- */
- disableKeyPad?: boolean;
- /**
- * 是显示输入闪烁光标
- * @default true
- */
- showCursur?: boolean;
- /**
- * 是否在输入完成后自动收起键盘
- * @default true
- */
- finishHideKeyPad?: boolean;
- /**
- * 文字样式
- */
- textStyle?: TextStyle;
- /**
- * 外层样式
- */
- innerStyle?: ViewStyle;
- }
- const emit = defineEmits([
- /**
- * 文字更改回调
- */
- 'update:modelValue',
- /**
- * 当输入完成(全部位数都已经输入)时返回事件
- */
- 'enterFinish',
- ]);
- const props = withDefaults(defineProps<NumberInputProps>(), {
- useSystemInput: true,
- numberCount: 6,
- isPassword: false,
- autoSize: false,
- disableKeyPad: false,
- startFocus: false,
- finishHideKeyPad: true,
- showCursur: true,
- borderType: () => propGetThemeVar('NumberInputBorderType', 'activeBorderBox'),
- borderWidth: () => propGetThemeVar('NumberInputBorderWidth', 4),
- borderColor: () => propGetThemeVar('NumberInputBorderColor', 'border.default'),
- backgroundColor: () => propGetThemeVar('NumberInputBackgroundColor', 'background.input'),
- gutter: () => propGetThemeVar('NumberInputGutter', 4),
- activeBorderColor: () => propGetThemeVar('NumberInputActiveBorderColor', 'primary'),
- activeBackgroundColor: () => propGetThemeVar('NumberInputActiveBackgroundColor', 'background.primary'),
- });
- const themeContext = useTheme();
- const themeStyles = themeContext.useThemeStyles({
- view: {
- display: 'flex',
- position: 'relative',
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'center',
- },
- info: {
- marginTop: DynamicSize('NumberInputInfoMarginTop', 10),
- textAlign: 'center',
- color: DynamicColor('NumberInputInfoColor', 'text.second'),
- },
- errorMessage: {
- marginTop: DynamicSize('NumberInputErrorMessageMarginTop', 10),
- textAlign: 'center',
- color: DynamicColor('NumberInputErrorMessageColor', 'danger'),
- },
- invisibleInput: {
- opacity: 0,
- position: 'absolute',
- height: 1,
- left: -300,
- },
- });
- let lastClickBoxRet = -1;
- const textShadow = ref(props.modelValue);
- const inputRef = ref();
- const showInput = ref(props.startFocus);
- const isFocus = ref(props.startFocus);
- function focusInput() {
- if (props.useSystemInput) {
- if (typeof inputRef.value?.focus === 'function')
- inputRef.value.focus();
- } else
- showInput.value = true;
- isFocus.value = true;
- }
- function blurInput() {
- if (props.useSystemInput) {
- if (typeof inputRef.value?.blur === 'function')
- inputRef.value.blur();
- } else
- showInput.value = false;
- isFocus.value = false;
- }
- function onBlurInput() {
- isFocus.value = false;
- }
- function onInputChangeText(text: string) {
- //点击了前面的方格输入,需要清除方格后面的文字
- if (lastClickBoxRet >= 0 && text.length > lastClickBoxRet) {
- text = (text.substring(0, lastClickBoxRet) + text.charAt(text.length - 1));
- lastClickBoxRet = -1;
- }
- if (text.length > props.numberCount)
- return;
- textShadow.value = text;
- if (text.length === props.numberCount) {
- emit('enterFinish', text);
- if (props.finishHideKeyPad)
- blurInput();
- }
- }
- function onBoxClicked(i: number) {
- lastClickBoxRet = i;
- focusInput();
- }
- function onInput(str: string) {
- onInputChangeText(textShadow.value + str);
- }
- function onDelete() {
- onInputChangeText(textShadow.value.substring(0, textShadow.value.length - 1));
- }
- watch(textShadow, (val) => {
- emit('update:modelValue', val);
- });
- onMounted(() => {
- if (props.startFocus)
- focusInput();
- });
- </script>
|