v-uni-app-ui 1.0.0 → 1.0.2

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 (61) hide show
  1. package/components/config.js +123 -0
  2. package/components/layout/v-card/v-card.vue +108 -0
  3. package/components/layout/v-grid/v-grid.vue +162 -0
  4. package/components/layout/v-icon-grid/v-icon-grid.vue +195 -0
  5. package/components/layout/v-infinite-scroll/v-infinite-scroll.vue +172 -0
  6. package/components/layout/v-list/v-list.vue +43 -0
  7. package/components/layout/v-row/v-row.vue +142 -0
  8. package/components/layout/v-waterfall/v-waterfall.vue +79 -0
  9. package/components/model/compound/v-checkbox-group/v-checkbox-group.vue +96 -0
  10. package/components/model/compound/v-console/v-console.js +20 -0
  11. package/components/model/compound/v-console/v-console.vue +299 -0
  12. package/components/model/compound/v-date-time/v-date-time.vue +261 -0
  13. package/components/model/compound/v-dialog/v-dialog.vue +178 -0
  14. package/components/model/compound/v-drum-select-picker/v-drum-select-picker.vue +83 -0
  15. package/components/model/compound/v-form/v-form.vue +226 -0
  16. package/components/model/compound/v-form-item/v-form-item.vue +255 -0
  17. package/components/model/compound/v-image/v-image.vue +357 -0
  18. package/components/model/compound/v-input-desensitize/v-input-desensitize.vue +101 -0
  19. package/components/model/compound/v-page/v-page.vue +11 -0
  20. package/components/model/compound/v-pages/v-pages.vue +141 -0
  21. package/components/model/compound/v-picker-list/v-picker-list.vue +109 -0
  22. package/components/model/compound/v-popup/v-popup.vue +151 -0
  23. package/components/model/compound/v-radio-group/v-radio-group.vue +86 -0
  24. package/components/model/compound/v-select-picker/v-select-picker.vue +202 -0
  25. package/components/model/compound/v-series-picker-list/v-series-picker-list.vue +221 -0
  26. package/components/model/compound/v-series-select-picker/v-series-select-picker.vue +203 -0
  27. package/components/model/compound/v-switch/v-switch.vue +136 -0
  28. package/components/model/compound/v-tabs-page/v-tabs-page.vue +138 -0
  29. package/components/model/native/v-badge/v-badge.vue +143 -0
  30. package/components/model/native/v-button/v-button.vue +273 -0
  31. package/components/model/native/v-carousel/v-carousel.vue +138 -0
  32. package/components/model/native/v-checkbox/v-checkbox.vue +215 -0
  33. package/components/model/native/v-collapse/v-collapse.vue +190 -0
  34. package/components/model/native/v-header-navigation-bar/v-header-navigation-bar.vue +92 -0
  35. package/components/model/native/v-input/v-input.vue +352 -0
  36. package/components/model/native/v-input-code/v-input-code.vue +146 -0
  37. package/components/model/native/v-loading/v-loading.vue +206 -0
  38. package/components/model/native/v-menu/v-menu.vue +222 -0
  39. package/components/model/native/v-menu-slide/v-menu-slide.vue +364 -0
  40. package/components/model/native/v-min-loading/v-min-loading.vue +80 -0
  41. package/components/model/native/v-null/v-null.vue +97 -0
  42. package/components/model/native/v-overlay/v-overlay.vue +96 -0
  43. package/components/model/native/v-pull-up-refresh/v-pull-up-refresh.vue +157 -0
  44. package/components/model/native/v-radio/v-radio.vue +138 -0
  45. package/components/model/native/v-scroll-list/v-scroll-list.vue +169 -0
  46. package/components/model/native/v-steps/v-steps.vue +253 -0
  47. package/components/model/native/v-table/v-table.vue +203 -0
  48. package/components/model/native/v-tabs/v-tabs.vue +235 -0
  49. package/components/model/native/v-tag/v-tag.vue +206 -0
  50. package/components/model/native/v-text/v-text.vue +187 -0
  51. package/components/model/native/v-text-button/v-text-button.vue +139 -0
  52. package/components/model/native/v-textarea/v-textarea.vue +178 -0
  53. package/components/model/native/v-title/v-title.vue +91 -0
  54. package/components/model/native/v-toast/info.png +0 -0
  55. package/components/model/native/v-toast/success.png +0 -0
  56. package/components/model/native/v-toast/v-toast.vue +198 -0
  57. package/components/model/native/v-toast/warn.png +0 -0
  58. package/components/model/native/v-upload-file-button/v-upload-file-button.vue +296 -0
  59. package/components/model/native/v-video/v-video.vue +175 -0
  60. package/components/model/native/v-window/v-window.vue +158 -0
  61. package/package.json +18 -94
@@ -0,0 +1,143 @@
1
+ <template>
2
+ <view :class="['v-badge', { 'v-badge--dot': dot }, `v-badge--size-${size}`, `v-badge--position-${position}`]" :style="badgeStyle">
3
+ <slot></slot>
4
+ <view v-if="!dot && (isShowContent)" class="v-badge-content">
5
+ {{ displayContent }}
6
+ </view>
7
+ </view>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { computed,inject } from 'vue';
12
+
13
+ /**
14
+ * v-badge 标徽
15
+ * value 双向绑定值
16
+ * dot 省略显示
17
+ * max 最大值
18
+ * color 标徽颜色
19
+ * size 标徽大小 默认值:medium 可选值small小、medium中、large大
20
+ * position 标徽位置 默认值:right 可选值left左、right右
21
+ */
22
+ const props = defineProps({
23
+ value: {
24
+ type: [Number, String],
25
+ default: 0
26
+ },
27
+ dot: {
28
+ type: Boolean,
29
+ default: false
30
+ },
31
+ max: {
32
+ type: Number,
33
+ default: 99
34
+ },
35
+ color: {
36
+ type: String,
37
+ default: '#ff5357'
38
+ },
39
+ fontColor: {
40
+ type: String,
41
+ default: '#fff'
42
+ },
43
+ size: {
44
+ type: String,
45
+ default: 'medium',
46
+ validator: (value: string) => {
47
+ return ['small', 'medium', 'large'].includes(value);
48
+ }
49
+ },
50
+ position: {
51
+ type: String,
52
+ default: 'right',
53
+ validator: (value: string) => {
54
+ return ['left', 'right'].includes(value);
55
+ }
56
+ }
57
+ });
58
+
59
+
60
+ const config = inject<any>('config');
61
+ const displayContent = computed(() => {
62
+ if (props.dot) return '';
63
+ if (props.max && props.value > props.max) {
64
+ return `${props.max}+`;
65
+ }
66
+ return props.value;
67
+ });
68
+
69
+ const isShowContent = computed(() => {
70
+ if (props.dot) return false;
71
+ if (typeof props.value === 'string') return props.value.length > 0;
72
+ return props.value > 0;
73
+ });
74
+
75
+ const badgeStyle = computed(() => {
76
+ return {
77
+ '--badge-color': props.color
78
+ };
79
+ });
80
+ </script>
81
+
82
+ <style lang="scss" scoped>
83
+ .v-badge {
84
+ position: relative;
85
+ display: inline-flex;
86
+ align-items: center;
87
+
88
+ .v-badge-content {
89
+ position: absolute;
90
+ top: -12rpx;
91
+ right: -12rpx;
92
+ background-color: var(--badge-color, #ff4500);
93
+ color: v-bind('fontColor');
94
+ border-radius: v-bind("config.borderRadius.circle");
95
+ padding: 0 8rpx;
96
+ font-size: v-bind("config.fontSize.hintText");
97
+ height: 30rpx;
98
+ line-height: 30rpx;
99
+ text-align: center;
100
+ min-width: 26rpx;
101
+ transform: scale(0.8);
102
+ }
103
+
104
+ &--dot::after {
105
+ content: '';
106
+ position: absolute;
107
+ top: -6rpx;
108
+ right: -6rpx;
109
+ width: 16rpx;
110
+ height: 16rpx;
111
+ background-color: var(--badge-color, #ff4500);
112
+ border-radius: 50%;
113
+ }
114
+
115
+ &--size-small .v-badge-content {
116
+ font-size: v-bind("config.fontSize.smallText");
117
+ height: 22rpx;
118
+ line-height: 22rpx;
119
+ min-width: 22rpx;
120
+ padding: 6rpx 12rpx;
121
+ transform: scale(0.7);
122
+ }
123
+
124
+ &--size-large .v-badge-content {
125
+ font-size: v-bind("config.fontSize.largeText");
126
+ height: 25rpx;
127
+ line-height: 25rpx;
128
+ min-width: 25px;
129
+ padding: 3rpx 8rpx;
130
+ transform: scale(0.9);
131
+ }
132
+
133
+ &--position-left .v-badge-content {
134
+ left: -12rpx;
135
+ right: auto;
136
+ }
137
+
138
+ &--position-left::after {
139
+ left: -12rpx;
140
+ right: auto;
141
+ }
142
+ }
143
+ </style>
@@ -0,0 +1,273 @@
1
+ <template>
2
+ <button
3
+ :class="['v-button', `v-button--${size} v-button--${type} v-button--${model}`, { 'v-button--loading': loading, 'v-button--disabled': disabled, 'v-button--plain': plain }]"
4
+ :disabled="disabled || loading"
5
+ @click="handleClick"
6
+ >
7
+ <view v-if="loading" class="spinner"></view>
8
+ <slot v-else></slot>
9
+ </button>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { ref, onUnmounted, inject } from 'vue';
14
+ /**
15
+ * v-button 按钮
16
+ * type 按钮类型 默认值:default 可选值default默认、delete删除、succeed成功、info信息、warn警告
17
+ * size 按钮大小 默认值:medium 可选值small小、medium中、large大
18
+ * model 按钮模式 默认值:semicircle圆角按钮 可选值:square方形按钮、circle圆形按钮、border下边框按钮、text文本
19
+ * disabled 是否禁用 true禁用 false不禁用
20
+ * loading 是否加载 true加载 false不加载
21
+ * plain 是否镂空 默认值:false 可选值:true镂空 false不镂空
22
+ * stabilizationTime 防抖时间 默认值0
23
+ * intervalUpdateTime 间隔触发防抖时间
24
+ * degressionTime 递减时间
25
+ */
26
+ const props = defineProps({
27
+ type: {
28
+ type: String,
29
+ default: 'default'
30
+ },
31
+ model: {
32
+ type: String,
33
+ default: 'semicircle',
34
+ validator: (v: string) => ['semicircle', 'square', 'circle', 'border'].includes(v)
35
+ },
36
+ size: {
37
+ type: String,
38
+ default: 'medium',
39
+ validator: (value: string) => ['small', 'medium', 'large'].includes(value)
40
+ },
41
+ disabled: {
42
+ type: Boolean,
43
+ default: false
44
+ },
45
+ loading: {
46
+ type: Boolean,
47
+ default: false
48
+ },
49
+ plain: {
50
+ type: Boolean,
51
+ default: false
52
+ },
53
+ stabilizationTime: {
54
+ type: Number,
55
+ default: 0
56
+ },
57
+ intervalUpdateTime: {
58
+ type: Number,
59
+ default: 1000
60
+ },
61
+ degressionTime: {
62
+ type: Number,
63
+ default: 1000
64
+ }
65
+ });
66
+
67
+ const emit = defineEmits(['click', 'countdown']);
68
+
69
+ const config = inject<any>('config');
70
+ const clickTimer = ref<number | null>(null);
71
+ const countdownTimer = ref<number | null>(null);
72
+ const countdown = ref<number>(props.stabilizationTime);
73
+ const isCountingDown = ref(false);
74
+
75
+ onUnmounted(() => {
76
+ if (clickTimer.value !== null) {
77
+ clearTimeout(clickTimer.value);
78
+ }
79
+ if (countdownTimer.value !== null) {
80
+ clearInterval(countdownTimer.value);
81
+ }
82
+ });
83
+
84
+ const handleClick = (event: MouseEvent) => {
85
+ if (!props.disabled && !props.loading && !isCountingDown.value) {
86
+ if (clickTimer.value !== null) {
87
+ clearTimeout(clickTimer.value);
88
+ }
89
+ if (countdownTimer.value !== null) {
90
+ clearInterval(countdownTimer.value);
91
+ }
92
+ isCountingDown.value = true;
93
+ countdown.value = props.stabilizationTime;
94
+
95
+ emit('click', event);
96
+
97
+ if (countdown.value > 0) {
98
+ emit('countdown', { remaining: countdown.value, total: props.stabilizationTime });
99
+
100
+ countdownTimer.value = window.setInterval(() => {
101
+ if (countdown.value > 0) {
102
+ countdown.value -= props.degressionTime;
103
+ emit('countdown', { remaining: countdown.value, total: props.stabilizationTime });
104
+ }
105
+
106
+ if (countdown.value <= 0) {
107
+ clearInterval(countdownTimer.value!);
108
+ isCountingDown.value = false;
109
+ emit('countdown', { remaining: 0, total: props.stabilizationTime });
110
+ }
111
+ }, props.intervalUpdateTime);
112
+ clickTimer.value = window.setTimeout(() => {
113
+ isCountingDown.value = false;
114
+ }, props.stabilizationTime);
115
+ } else {
116
+ isCountingDown.value = false;
117
+ }
118
+ }
119
+ };
120
+ </script>
121
+
122
+ <style lang="scss">
123
+ .v-button {
124
+ width: 150rpx;
125
+ height: 50rpx;
126
+ border: none;
127
+ cursor: pointer;
128
+ transition: all 0.3s;
129
+ display: flex;
130
+ align-items: center;
131
+ justify-content: center;
132
+ color: white;
133
+
134
+ &:hover {
135
+ opacity: v-bind('config.opacity.hover');
136
+ }
137
+
138
+ &:active {
139
+ opacity: v-bind('config.opacity.click');
140
+ }
141
+
142
+ &--semicircle {
143
+ border-radius: v-bind('config.borderRadius.semicircle');
144
+ }
145
+
146
+ &--square {
147
+ border-radius: v-bind('config.borderRadius.square');
148
+ }
149
+
150
+ &--circle {
151
+ border-radius: v-bind('config.borderRadius.circle');
152
+ }
153
+
154
+ &--border {
155
+ border-radius: 0;
156
+ border-bottom: 1rpx solid v-bind('config.border.color');
157
+ }
158
+
159
+ &--default {
160
+ background-color: v-bind('config.backgroundColor.default') !important;
161
+ }
162
+
163
+ &--delete {
164
+ background-color: v-bind('config.backgroundColor.delete') !important;
165
+ }
166
+
167
+ &--succeed {
168
+ background-color: v-bind('config.backgroundColor.succeed') !important;
169
+ }
170
+
171
+ &--info {
172
+ background-color: v-bind('config.backgroundColor.info') !important;
173
+ }
174
+
175
+ &--warn {
176
+ background-color: v-bind('config.backgroundColor.warn') !important;
177
+ }
178
+
179
+ &--text {
180
+ background-color: v-bind('config.backgroundColor.reversal') !important;
181
+ color: v-bind('config.fontColor.text');
182
+ }
183
+
184
+ &--small {
185
+ width: 120rpx;
186
+ height: 50rpx;
187
+ padding: 14rpx 2rpx;
188
+ font-size: v-bind("config.fontSize.smallText");
189
+ }
190
+
191
+ &--medium {
192
+ width: 150rpx;
193
+ height: 60rpx;
194
+ padding: 15rpx 0;
195
+ font-size: v-bind("config.fontSize.mediumText");
196
+ }
197
+
198
+ &--large {
199
+ width: 170rpx;
200
+ height: 70rpx;
201
+ padding: 20rpx 0;
202
+ font-size: v-bind("config.fontSize.largeText");
203
+ }
204
+
205
+ &--disabled,
206
+ &[disabled] {
207
+ opacity: v-bind('config.opacity.disabled');
208
+ cursor: not-allowed;
209
+ color: v-bind('config.fontColor.reversal') !important;
210
+ }
211
+
212
+ &--loading {
213
+ position: relative;
214
+ overflow: hidden;
215
+ }
216
+
217
+ &--plain {
218
+ background-color: transparent !important;
219
+ border: 1rpx solid;
220
+ color: currentColor;
221
+
222
+ &.v-button--default {
223
+ border-color: v-bind("config.border.default");
224
+ color: v-bind("config.fontColor.default");
225
+ }
226
+
227
+ &.v-button--delete {
228
+ border-color: v-bind("config.border.delete");
229
+ color: v-bind("config.fontColor.delete");
230
+ }
231
+
232
+ &.v-button--succeed {
233
+ border-color: v-bind("config.border.succeed");
234
+ color: v-bind("config.fontColor.succeed");
235
+ }
236
+
237
+ &.v-button--info {
238
+ border-color: v-bind("config.border.info");
239
+ color: v-bind("config.fontColor.info");
240
+ }
241
+
242
+ &.v-button--warn {
243
+ border-color: v-bind("config.border.warn");
244
+ color: v-bind("config.fontColor.warn");
245
+ }
246
+
247
+ &.v-button--text {
248
+ border: none;
249
+ color: v-bind("config.fontColor.text");
250
+ }
251
+ }
252
+
253
+ .spinner {
254
+ width: 20rpx;
255
+ height: 20rpx;
256
+ border: 1rpx solid;
257
+ border-color: v-bind("config.border.color");
258
+ border-radius: 50%;
259
+ border-top-color: white;
260
+ animation: spin 1s ease-in-out infinite;
261
+ }
262
+
263
+ @keyframes spin {
264
+ to {
265
+ transform: rotate(360deg);
266
+ }
267
+ }
268
+ }
269
+
270
+ uni-button:after {
271
+ border: none;
272
+ }
273
+ </style>
@@ -0,0 +1,138 @@
1
+ <template>
2
+ <view class="carousel-box">
3
+ <!-- 左右箭头 -->
4
+ <view class="carousel-nav-buttons" v-show="isEndsButton">
5
+ <view class="carousel-nav-buttons-prev" @click="prevSlide">
6
+ <slot name="prev-button">
7
+ <view class="default-prev-button">←</view>
8
+ </slot>
9
+ </view>
10
+ <view class="carousel-nav-buttons-next" @click="nextSlide">
11
+ <slot name="next-button">
12
+ <view class="default-next-button">→</view>
13
+ </slot>
14
+ </view>
15
+ </view>
16
+
17
+ <!-- 关键改动 1:key 强制重建 -->
18
+ <swiper
19
+ :key="carouselKey"
20
+ :indicator-dots="indicatorDots"
21
+ :autoplay="autoplay"
22
+ :interval="interval"
23
+ :duration="duration"
24
+ :current="currentVal"
25
+ :circular="circular"
26
+ @change="onSwiperChange"
27
+ class="swiper-box"
28
+ >
29
+ <swiper-item v-for="(item, index) in items" :key="index" class="carousel-item">
30
+ <slot :name="'item-' + index" :item="item"></slot>
31
+ </swiper-item>
32
+ </swiper>
33
+ </view>
34
+ </template>
35
+
36
+ <script setup lang="ts">
37
+ import { ref, watch, inject } from 'vue';
38
+
39
+ const props = defineProps({
40
+ items: { type: Array, default: () => [] },
41
+ indicatorDots: { type: Boolean, default: true },
42
+ autoplay: { type: Boolean, default: true },
43
+ interval: { type: Number, default: 5000 },
44
+ duration: { type: Number, default: 400 },
45
+ current: { type: Number, default: 0 },
46
+ isEndsButton: { type: Boolean, default: false },
47
+ circular: { type: Boolean, default: true }
48
+ });
49
+
50
+ const emit = defineEmits(['update:current', 'change']);
51
+ const config = inject<any>('config');
52
+
53
+ const currentVal = ref(props.current);
54
+ const carouselKey = ref(0); // 关键改动 2:刷新 key
55
+
56
+ /* 双向绑定 current */
57
+ watch(
58
+ () => props.current,
59
+ (v) => (currentVal.value = v),
60
+ { immediate: true }
61
+ );
62
+ watch(currentVal, (v) => emit('update:current', v));
63
+
64
+ /* 关键改动 3:节流防止抽搐 */
65
+ let changing = false;
66
+ const onSwiperChange = (e: any) => {
67
+ if (changing) return;
68
+ changing = true;
69
+ const { current } = e.detail;
70
+ currentVal.value = current;
71
+ emit('change', current);
72
+ setTimeout(() => (changing = false), 150);
73
+ };
74
+
75
+ /* 手动切换 */
76
+ const prevSlide = () => {
77
+ const len = props.items.length;
78
+ currentVal.value = currentVal.value <= 0 ? len - 1 : currentVal.value - 1;
79
+ emit('change', currentVal.value);
80
+ };
81
+ const nextSlide = () => {
82
+ const len = props.items.length;
83
+ currentVal.value = currentVal.value >= len - 1 ? 0 : currentVal.value + 1;
84
+ emit('change', currentVal.value);
85
+ };
86
+ </script>
87
+
88
+ <style lang="scss" scoped>
89
+ .carousel-box {
90
+ width: 100%;
91
+ /* 关键改动 4:禁止百分比高度,用固定值 */
92
+ height: 560rpx;
93
+ position: relative;
94
+ }
95
+
96
+ .carousel-nav-buttons {
97
+ position: absolute;
98
+ width: 90%;
99
+ top: 50%;
100
+ transform: translateY(-50%);
101
+ display: flex;
102
+ justify-content: space-between;
103
+ padding: 0 10rpx;
104
+ z-index: 10;
105
+ }
106
+
107
+ .carousel-nav-buttons-prev,
108
+ .carousel-nav-buttons-next {
109
+ display: flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ width: 40rpx;
113
+ height: 40rpx;
114
+ border-radius: 50%;
115
+ background-color: rgba(255, 255, 255, 0.5);
116
+ cursor: pointer;
117
+ user-select: none;
118
+ }
119
+
120
+ .default-prev-button,
121
+ .default-next-button {
122
+ font-size: v-bind('config.fontSize.largeText');
123
+ color: v-bind('config.fontColor.mainText');
124
+ }
125
+
126
+ .swiper-box {
127
+ width: 100%;
128
+ height: 100%;
129
+ }
130
+
131
+ .carousel-item {
132
+ width: 100%;
133
+ height: 100%;
134
+ display: flex;
135
+ justify-content: center;
136
+ align-items: center;
137
+ }
138
+ </style>