v-uni-app-ui 1.0.0 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +147 -0
  2. package/components/config/css/basic.scss +19 -0
  3. package/components/config/interface/basic-type.js +16 -0
  4. package/components/config/interface/components-interface.ts +0 -0
  5. package/components/config/interface/monitor/components/input-monitor.js +0 -0
  6. package/components/config/interface/monitor/property-monitor.ts +136 -0
  7. package/components/config/interface/props/basic-props.ts +88 -0
  8. package/components/config/interface/props/components/button-props.ts +85 -0
  9. package/components/config/interface/props/components/input-props.ts +69 -0
  10. package/components/config/interface/props/props-tools.ts +64 -0
  11. package/components/config/style/basic.js +346 -0
  12. package/components/config/style/component-registry.js +142 -0
  13. package/components/config/style/components/button-style.js +160 -0
  14. package/components/config/style/components/input-style.js +98 -0
  15. package/components/config/style/components-style.js +622 -0
  16. package/components/config/style/property-mapper.js +377 -0
  17. package/components/config/style/pseudo-processor.js +213 -0
  18. package/components/config.js +123 -0
  19. package/components/icon/iconfont.css +87 -0
  20. package/components/icon/iconfont.js +1 -0
  21. package/components/icon/iconfont.json +135 -0
  22. package/components/icon/iconfont.ttf +0 -0
  23. package/components/icon/iconfont.woff +0 -0
  24. package/components/icon/iconfont.woff2 +0 -0
  25. package/components/layout/v-card/v-card.vue +108 -0
  26. package/components/layout/v-grid/v-grid.vue +162 -0
  27. package/components/layout/v-icon-grid/v-icon-grid.vue +195 -0
  28. package/components/layout/v-infinite-scroll/v-infinite-scroll.vue +172 -0
  29. package/components/layout/v-list/v-list.vue +43 -0
  30. package/components/layout/v-row/v-row.vue +142 -0
  31. package/components/layout/v-waterfall/v-waterfall.vue +79 -0
  32. package/components/model/compound/v-checkbox-group/v-checkbox-group.vue +96 -0
  33. package/components/model/compound/v-console/v-console.js +20 -0
  34. package/components/model/compound/v-console/v-console.vue +299 -0
  35. package/components/model/compound/v-date-time/v-date-time.vue +261 -0
  36. package/components/model/compound/v-dialog/v-dialog.vue +178 -0
  37. package/components/model/compound/v-drum-select-picker/v-drum-select-picker.vue +83 -0
  38. package/components/model/compound/v-form/v-form.vue +226 -0
  39. package/components/model/compound/v-form-item/v-form-item.vue +255 -0
  40. package/components/model/compound/v-image/v-image.vue +357 -0
  41. package/components/model/compound/v-input-desensitize/v-input-desensitize.vue +101 -0
  42. package/components/model/compound/v-page/v-page.vue +11 -0
  43. package/components/model/compound/v-pages/v-pages.vue +141 -0
  44. package/components/model/compound/v-picker-list/v-picker-list.vue +109 -0
  45. package/components/model/compound/v-popup/v-popup.vue +151 -0
  46. package/components/model/compound/v-radio-group/v-radio-group.vue +86 -0
  47. package/components/model/compound/v-select-picker/v-select-picker.vue +202 -0
  48. package/components/model/compound/v-series-picker-list/v-series-picker-list.vue +221 -0
  49. package/components/model/compound/v-series-select-picker/v-series-select-picker.vue +203 -0
  50. package/components/model/compound/v-switch/v-switch.vue +136 -0
  51. package/components/model/compound/v-tabs-page/v-tabs-page.vue +138 -0
  52. package/components/model/native/v-badge/v-badge.vue +143 -0
  53. package/components/model/native/v-button/v-button.vue +81 -0
  54. package/components/model/native/v-carousel/v-carousel.vue +138 -0
  55. package/components/model/native/v-checkbox/v-checkbox.vue +215 -0
  56. package/components/model/native/v-collapse/v-collapse.vue +190 -0
  57. package/components/model/native/v-header-navigation-bar/v-header-navigation-bar.vue +92 -0
  58. package/components/model/native/v-input/v-input.vue +163 -0
  59. package/components/model/native/v-input-code/v-input-code.vue +146 -0
  60. package/components/model/native/v-loading/v-loading.vue +206 -0
  61. package/components/model/native/v-menu/v-menu.vue +222 -0
  62. package/components/model/native/v-menu-slide/v-menu-slide.vue +364 -0
  63. package/components/model/native/v-min-loading/v-min-loading.vue +80 -0
  64. package/components/model/native/v-null/v-null.vue +97 -0
  65. package/components/model/native/v-overlay/v-overlay.vue +96 -0
  66. package/components/model/native/v-pull-up-refresh/v-pull-up-refresh.vue +157 -0
  67. package/components/model/native/v-radio/v-radio.vue +138 -0
  68. package/components/model/native/v-scroll-list/v-scroll-list.vue +169 -0
  69. package/components/model/native/v-steps/v-steps.vue +253 -0
  70. package/components/model/native/v-table/v-table.vue +203 -0
  71. package/components/model/native/v-tabs/v-tabs.vue +235 -0
  72. package/components/model/native/v-tag/v-tag.vue +206 -0
  73. package/components/model/native/v-text/v-text.vue +187 -0
  74. package/components/model/native/v-textarea/v-textarea.vue +178 -0
  75. package/components/model/native/v-title/v-title.vue +91 -0
  76. package/components/model/native/v-toast/info.png +0 -0
  77. package/components/model/native/v-toast/success.png +0 -0
  78. package/components/model/native/v-toast/v-toast.vue +198 -0
  79. package/components/model/native/v-toast/warn.png +0 -0
  80. package/components/model/native/v-upload-file-button/v-upload-file-button.vue +296 -0
  81. package/components/model/native/v-video/v-video.vue +175 -0
  82. package/components/model/native/v-window/v-window.vue +158 -0
  83. package/components/utils/event-modifiers.ts +139 -0
  84. package/components/utils/validator.ts +451 -0
  85. package/index.js +372 -0
  86. package/package.json +25 -93
@@ -0,0 +1,190 @@
1
+ <template>
2
+ <view class="v-collapse">
3
+ <view v-for="(item, index) in items" :key="index" :class="['collapse-item', { expanded: item.expanded }]">
4
+ <view class="collapse-header" @click="toggleItem(index)">
5
+ <view class="header-title">{{ item.title }}</view>
6
+ <view v-if="!item.isRightIcon" class="header-icon">
7
+ <view class="arrow" :class="{ rotated: item.expanded }"></view>
8
+ </view>
9
+ </view>
10
+ <view class="collapse-content">
11
+ <view class="content-inner">
12
+ <slot name="content" :item="item">
13
+ {{ item.content }}
14
+ </slot>
15
+ </view>
16
+ </view>
17
+ </view>
18
+ </view>
19
+ </template>
20
+
21
+ <script lang="ts" setup>
22
+ import { ref, watch,inject } from 'vue';
23
+
24
+ interface CollapseItem {
25
+ title: string;
26
+ content: string;
27
+ expanded: boolean;
28
+ name?: string;
29
+ isRightIcon?: boolean;
30
+ }
31
+
32
+ const props = defineProps({
33
+ items: {
34
+ type: Array as () => CollapseItem[],
35
+ required: true,
36
+ default: () => []
37
+ },
38
+ accordion: {
39
+ type: Boolean,
40
+ default: true
41
+ },
42
+ activeIndex: {
43
+ type: Number,
44
+ default: 0
45
+ }
46
+ });
47
+
48
+ const emit = defineEmits(['update:activeIndex', 'change']);
49
+
50
+ const config = inject<any>('config');
51
+ const items = ref<CollapseItem[]>([...props.items]);
52
+
53
+ const initExpandedState = () => {
54
+ items.value.forEach((item, index) => {
55
+ item.expanded = index === props.activeIndex;
56
+ });
57
+ };
58
+
59
+ watch(
60
+ () => props.activeIndex,
61
+ (newIndex) => {
62
+ if (props.accordion) {
63
+ items.value.forEach((item, index) => {
64
+ item.expanded = index === newIndex;
65
+ });
66
+ }
67
+ }
68
+ );
69
+
70
+ initExpandedState();
71
+
72
+ const toggleItem = (index: number) => {
73
+ const item = items.value[index];
74
+
75
+ if (props.accordion) {
76
+ items.value.forEach((otherItem, otherIndex) => {
77
+ otherItem.expanded = otherIndex === index ? !item.expanded : false;
78
+ });
79
+ } else {
80
+ item.expanded = !item.expanded;
81
+ }
82
+
83
+ const activeIndex = items.value.findIndex((item) => item.expanded);
84
+ emit('update:activeIndex', activeIndex);
85
+ emit('change', activeIndex);
86
+ };
87
+ </script>
88
+
89
+ <style lang="scss" scoped>
90
+ .v-collapse {
91
+ width: 100%;
92
+ overflow: hidden;
93
+ box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
94
+
95
+ .collapse-item {
96
+ margin-bottom: 12rpx;
97
+ overflow: hidden;
98
+ background: transparent;
99
+ transition: all 0.5s ease;
100
+ border-top: 1rpx solid v-bind("config.border.color");
101
+ border-bottom: 1rpx solid v-bind("config.border.color");
102
+
103
+ &:last-child {
104
+ margin-bottom: 0;
105
+ }
106
+
107
+ .collapse-header {
108
+ display: flex;
109
+ justify-content: space-between;
110
+ align-items: center;
111
+ padding: 18rpx 24rpx;
112
+ font-weight: 600;
113
+ font-size: 24rpx;
114
+ cursor: pointer;
115
+ transition: all 0.3s ease;
116
+ }
117
+
118
+ .header-icon {
119
+ width: 24rpx;
120
+ height: 24rpx;
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+
125
+ .arrow {
126
+ width: 15rpx;
127
+ height: 15rpx;
128
+ border-right: 4rpx solid v-bind("config.border.color");
129
+ border-bottom: 4rpx solid v-bind("config.border.color");
130
+ transform: rotate(45deg);
131
+ transition: transform 0.5s ease;
132
+ }
133
+
134
+ .rotated {
135
+ transform: rotate(225deg);
136
+ }
137
+ }
138
+
139
+ .collapse-content {
140
+ max-height: 0;
141
+ overflow: hidden;
142
+ transition: max-height 0.5s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.5s;
143
+ box-shadow: 0 4rpx 6rpx rgba(0, 0, 0, 0.05);
144
+ }
145
+
146
+ .content-inner {
147
+ padding: 24rpx;
148
+ line-height: 1.7;
149
+ transform: translateY(10rpx);
150
+ opacity: 0;
151
+ transition: transform 0.3s ease, opacity 0.3s ease;
152
+ }
153
+
154
+ &.expanded .collapse-content {
155
+ max-height: 1000rpx;
156
+ }
157
+
158
+ &.expanded .content-inner {
159
+ transform: translateY(0);
160
+ opacity: 1;
161
+ transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1);
162
+ }
163
+
164
+ // 响应式设计
165
+ @media (max-width: 768px) {
166
+ .collapse-header {
167
+ padding: 16rpx 20rpx;
168
+ font-size: v-bind("config.fontSize.mediumTitle");
169
+ }
170
+
171
+ .content-inner {
172
+ padding: 20rpx;
173
+ font-size: v-bind("config.fontSize.largeText");
174
+ }
175
+ }
176
+
177
+ @media (max-width: 480px) {
178
+ .collapse-header {
179
+ padding: 14rpx 16rpx;
180
+ font-size: v-bind("config.fontSize.smallTitle");
181
+ }
182
+
183
+ .content-inner {
184
+ padding: 16rpx;
185
+ font-size: v-bind("config.fontSize.mediumText");
186
+ }
187
+ }
188
+ }
189
+ }
190
+ </style>
@@ -0,0 +1,92 @@
1
+ <template>
2
+ <view class="v-header-navigation-bar" :style="{ backgroundColor: backgroundColor }">
3
+ <view class="v-header-navigation-bar-left" @click="handleBack">
4
+ <slot name="left">
5
+ <view class="v-header-navigation-bar-back-icon" v-if="showBack"></view>
6
+ </slot>
7
+ </view>
8
+ <view class="v-header-navigation-bar-title">
9
+ <slot>
10
+ <text>{{ title }}</text>
11
+ </slot>
12
+ </view>
13
+ <view class="v-header-navigation-bar-right">
14
+ <slot name="right"></slot>
15
+ </view>
16
+ </view>
17
+ </template>
18
+
19
+ <script lang="ts" setup>
20
+ import { ref,inject } from 'vue';
21
+
22
+ /**
23
+ * v-header-navigation-bar 导航栏
24
+ * title 标题
25
+ * backgroundColor 背景颜色
26
+ * showBack 是否需要返回按钮
27
+ */
28
+ const props = defineProps({
29
+ title: {
30
+ type: String,
31
+ default: ''
32
+ },
33
+ backgroundColor: {
34
+ type: String,
35
+ default: '#ffffff'
36
+ },
37
+ showBack: {
38
+ type: Boolean,
39
+ default: true
40
+ }
41
+ });
42
+
43
+ const emits = defineEmits(['back']);
44
+
45
+ const config = inject<any>('config');
46
+ const handleBack = () => {
47
+ if (props.showBack) {
48
+ emits('back');
49
+ }
50
+ };
51
+ </script>
52
+
53
+ <style lang="scss" scoped>
54
+ .v-header-navigation-bar {
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: space-between;
58
+ padding: 10rpx 16rpx;
59
+ box-sizing: border-box;
60
+ height: 5vh;
61
+ border-bottom: 1px solid v-bind("config.border.color");
62
+ position: relative;
63
+ z-index: 1000;
64
+ }
65
+ .v-header-navigation-bar-left {
66
+ margin-left: 2%;
67
+ }
68
+ .v-header-navigation-bar-left,
69
+ .v-header-navigation-bar-right {
70
+ display: flex;
71
+ align-items: center;
72
+ }
73
+
74
+ .v-header-navigation-bar-back-icon {
75
+ width: 24rpx;
76
+ height: 24rpx;
77
+ border: 4rpx solid v-bind("config.fontColor.mianTitle");
78
+ border-top: none;
79
+ border-right: none;
80
+ transform: rotate(45deg);
81
+ margin-right: 10px;
82
+ cursor: pointer;
83
+ }
84
+
85
+ .v-header-navigation-bar-title {
86
+ flex: 1;
87
+ text-align: center;
88
+ font-size: v-bind("config.fontSize.mediumTitle");
89
+ font-weight: bold;
90
+ color: v-bind("config.fontColor.mianTitle");
91
+ }
92
+ </style>
@@ -0,0 +1,163 @@
1
+ <template>
2
+ <view :id="props.id" :class="computedClasses" :style="computedStyle">
3
+ <view :style="leftSlotStyle" class="left-slot">
4
+ <slot name="left">
5
+ <view class="left-icon" v-if="props.isIcon" :style="iconStyle">
6
+ <view class="iconfont icon-url"></view>
7
+ <view class="icon-fill" :style="iconFillStyle"></view>
8
+ </view>
9
+ </slot>
10
+ </view>
11
+ <view :style="inputStyle" class="center-input">
12
+ <input
13
+ v-model="inputValue"
14
+ :type="computedInputType"
15
+ :style="inputFontStyle"
16
+ :placeholder="currentPlaceholder"
17
+ :disabled="props.disabled"
18
+ :placeholder-style="toCSS(inputPlaceholderStyle)"
19
+ :cursor="props.cursor"
20
+ :selection-start="selectionStart"
21
+ :selection-end="selectionEnd"
22
+ :maxlength="props.maxlength"
23
+ :random-number="props.randomNumber"
24
+ :cursor-color="props.cursorColor"
25
+ :focus="props.focus"
26
+ :auto-blur="props.autoBlur"
27
+ :password="props.type === 'password'"
28
+ :cursor-spacing="props.cursorSpacing"
29
+ @focus="focus(true)"
30
+ @blur="focus(false)"
31
+ />
32
+ </view>
33
+ <view :style="rightSlotStyle" class="right-slot">
34
+ <slot name="right"></slot>
35
+ </view>
36
+ </view>
37
+ </template>
38
+
39
+ <script setup lang="ts">
40
+ import { ref, onUnmounted, computed } from 'vue';
41
+ import inputProps from '@/components/config/interface/props/components/input-props';
42
+ import { useComponentStyle } from '@/components/config/style/components-style';
43
+ import { usePropertyMonitor } from '@/components/config/interface/monitor/property-monitor';
44
+
45
+ const props = defineProps(inputProps);
46
+ const emit = defineEmits<{
47
+ input: [value: string];
48
+ blur: [event: FocusEvent];
49
+ focus: [event: FocusEvent];
50
+ 'update:value': [value: any];
51
+ 'update:placeholderConfig': [value: any];
52
+ 'update:disabled': [value: any];
53
+ }>();
54
+
55
+ const { style: computedStyle, classes: computedClasses, sonStyle, toCSS, focus } = useComponentStyle('input', props);
56
+ const getSonStyle = (element: string): Record<string, string | number> => {
57
+ return sonStyle.value[element as keyof typeof sonStyle.value] || {};
58
+ };
59
+ const inputPlaceholderStyle = computed(() => {
60
+ return Object.assign(getSonStyle('placeholder'), props.placeholderStyle);
61
+ });
62
+ const inputStyle = computed(() => getSonStyle('input'));
63
+ const inputFontStyle = computed(() => {
64
+ const { width, height, margin, padding, ...fontStyles } = inputStyle.value;
65
+ return fontStyles;
66
+ });
67
+ const leftSlotStyle = computed(() => getSonStyle('leftSlot'));
68
+ const rightSlotStyle = computed(() => getSonStyle('rightSlot'));
69
+ const iconStyle = computed(() => getSonStyle('icon'));
70
+ const iconFillStyle = computed(() => getSonStyle('iconFill'));
71
+ const computedInputType = computed(() => {
72
+ const typeMap: Record<string, string> = {
73
+ text: 'text',
74
+ id: 'idcard',
75
+ password: 'text',
76
+ 'safe-password': 'safe-password',
77
+ number: 'number',
78
+ digit: 'digit',
79
+ numeric: 'numeric',
80
+ decimal: 'decimal',
81
+ email: 'email',
82
+ phone: 'tel',
83
+ url: 'url',
84
+ textarea: 'text', // uni-app 的 input 不支持 textarea,需用单独的 textarea 组件,此处降级为 text
85
+ name: 'nickname',
86
+ search: 'search',
87
+ none: 'none'
88
+ };
89
+
90
+ return typeMap[props.type] || 'text';
91
+ });
92
+ const currentPlaceholderIndex = ref(0);
93
+ const currentPlaceholder = computed(() => {
94
+ const placeholder = props.placeholder;
95
+ if (!placeholder || placeholder.length === 0) return '';
96
+ const index = currentPlaceholderIndex.value % placeholder.length;
97
+ return placeholder[index];
98
+ });
99
+
100
+ // 定时器管理
101
+ let placeholderTimer: ReturnType<typeof setInterval> | null = null;
102
+
103
+ const managePlaceholderRotation = () => {
104
+ // 清理现有定时器
105
+ if (placeholderTimer) {
106
+ clearInterval(placeholderTimer);
107
+ placeholderTimer = null;
108
+ }
109
+
110
+ // 启动条件:未被禁用 且 配置有效
111
+ if (!props.disabled && props.placeholder && props.placeholder.length > 1 && props.placeholderTimeNumber > 0) {
112
+ placeholderTimer = setInterval(() => {
113
+ const placeholderArray = Array.isArray(props.placeholder) ? props.placeholder : [props.placeholder!];
114
+ currentPlaceholderIndex.value = (currentPlaceholderIndex.value + 1) % placeholderArray.length;
115
+ }, props.placeholderTimeNumber);
116
+ }
117
+ };
118
+
119
+ const inputValue = computed({
120
+ get: () => props.value,
121
+ set: (newValue) => {
122
+ emit('update:value', newValue);
123
+ }
124
+ });
125
+
126
+ usePropertyMonitor({ source: inputValue, propertyName: 'value' }, emit);
127
+ usePropertyMonitor(
128
+ {
129
+ source: () => props.disabled,
130
+ propertyName: 'disabled',
131
+ // 状态变化时重新管理定时器
132
+ onChange: managePlaceholderRotation
133
+ },
134
+ emit
135
+ );
136
+ usePropertyMonitor(
137
+ {
138
+ source: () => ({
139
+ placeholder: props.placeholder,
140
+ time: props.placeholderTimeNumber
141
+ }),
142
+ propertyName: 'placeholderConfig',
143
+ onChange: managePlaceholderRotation,
144
+ immediate: true
145
+ },
146
+ emit
147
+ );
148
+
149
+ onUnmounted(() => {
150
+ if (placeholderTimer) {
151
+ clearInterval(placeholderTimer);
152
+ placeholderTimer = null;
153
+ }
154
+ });
155
+ </script>
156
+
157
+ <style lang="scss" scoped>
158
+ uni-input {
159
+ height: 100%;
160
+ min-height: auto;
161
+ line-height: normal;
162
+ }
163
+ </style>
@@ -0,0 +1,146 @@
1
+ <template>
2
+ <div class="v-input-code">
3
+ <div class="input-box" :style="{ width: `${inputWidth * length + (length - 1) * 20}px`, height: `${inputHeight}px` }">
4
+ <input
5
+ v-for="(item, index) in length"
6
+ :key="index"
7
+ type="text"
8
+ :class="['input-item', { 'input-focus': focusIndex === index }]"
9
+ :maxlength="1"
10
+ :placeholder="placeholder"
11
+ @input="onInput(index, $event)"
12
+ @focus="onFocus(index)"
13
+ @blur="onBlur(index)"
14
+ @keydown="onkeydown(index, $event)"
15
+ :focus="inputFocus[index]"
16
+ :blur="inputBlur[index]"
17
+ />
18
+ </div>
19
+ </div>
20
+ </template>
21
+
22
+ <script lang="ts" setup>
23
+ import { ref, watch, onMounted, inject } from 'vue';
24
+
25
+ const props = defineProps({
26
+ value: {
27
+ type: String,
28
+ default: ''
29
+ },
30
+ length: {
31
+ type: Number,
32
+ default: 4
33
+ },
34
+ inputWidth: {
35
+ type: Number,
36
+ default: 30
37
+ },
38
+ inputHeight: {
39
+ type: Number,
40
+ default: 50
41
+ },
42
+ focus: {
43
+ type: Boolean,
44
+ default: false
45
+ },
46
+ placeholder: {
47
+ type: String,
48
+ default: ''
49
+ }
50
+ });
51
+
52
+ const emit = defineEmits(['update:value', 'complete']);
53
+
54
+ const config = inject<any>('config');
55
+ const focusIndex = ref<number | null>(null);
56
+ const inputFocus = ref([]);
57
+ const inputBlur = ref([]);
58
+
59
+ onMounted(() => {
60
+ inputFocus.value.length = props.length;
61
+ inputBlur.value.length = props.length;
62
+ });
63
+
64
+ const onInput = (index: number, event: any) => {
65
+ const value = event.detail.value;
66
+ if (value) {
67
+ if (index < props.length - 1) {
68
+ console.log(index + 1);
69
+ inputFocus.value[index + 1] = true;
70
+ }
71
+ updateModelValue(value, index);
72
+ } else {
73
+ updateModelValue('', index);
74
+ }
75
+ };
76
+ const onkeydown = (index: number, event: any) => {
77
+ if (event.code === 'Backspace') {
78
+ inputFocus.value[index] = false;
79
+ // inputFocus.value[index - 2] = false;
80
+ // inputFocus.value[index - 2] = true;
81
+ }
82
+ };
83
+
84
+ const onFocus = (index: number) => {
85
+ focusIndex.value = index;
86
+ };
87
+
88
+ const onBlur = (index: number) => {
89
+ if (focusIndex.value === index) {
90
+ focusIndex.value = null;
91
+ }
92
+ };
93
+
94
+ const updateModelValue = (value: string, index: number) => {
95
+ const newValue = props.value.split('');
96
+ newValue[index] = value;
97
+ const joinedValue = newValue.join('');
98
+ emit('update:value', joinedValue);
99
+ if (joinedValue.length === props.length) {
100
+ emit('complete');
101
+ }
102
+ };
103
+
104
+ watch(
105
+ () => props.focus,
106
+ (newValue) => {
107
+ if (newValue) {
108
+ inputFocus.value[0].focus();
109
+ }
110
+ }
111
+ );
112
+ </script>
113
+
114
+ <style lang="scss" scoped>
115
+ .v-input-code {
116
+ display: flex;
117
+ justify-content: center;
118
+ align-items: center;
119
+ }
120
+
121
+ .input-box {
122
+ display: flex;
123
+ justify-content: center;
124
+ align-items: center;
125
+ }
126
+
127
+ .input-item {
128
+ width: v-bind("inputWidth + 'rpx'");
129
+ height: v-bind("inputHeight + 'rpx'");
130
+ margin: 0 15rpx;
131
+ padding: 0 15rpx;
132
+ text-align: center;
133
+ font-size: 16px;
134
+ border-bottom: 4rpx solid v-bind("config.border.color");
135
+ outline: none;
136
+ transition: all 0.3s ease;
137
+ }
138
+
139
+ .input-item:focus {
140
+ border-bottom: 4rpx solid v-bind("config.border.default");
141
+ }
142
+
143
+ .input-focus {
144
+ border-bottom: 4rpx solid v-bind("config.border.default");
145
+ }
146
+ </style>