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,206 @@
1
+ <template>
2
+ <view v-if="currentShow" :class="['v-loading', { 'v-loading--full-screen': fullScreen }]" :style="loadingStyle">
3
+ <slot name="spinner">
4
+ <view class="v-loading-spinner">
5
+ <view class="spinner-outer"></view>
6
+ <view class="spinner-inner"></view>
7
+ </view>
8
+ </slot>
9
+ <text v-if="text" class="v-loading-text">{{ text }}</text>
10
+ </view>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import { computed, ref, watch,onMounted,inject } from 'vue';
15
+
16
+ const props = defineProps({
17
+ fullScreen: {
18
+ type: Boolean,
19
+ default: false
20
+ },
21
+ text: {
22
+ type: String,
23
+ default: '',
24
+ required: true
25
+ },
26
+ size: {
27
+ type: String,
28
+ default: 'medium',
29
+ validator: (value: any) => {
30
+ return ['small', 'medium', 'large'].includes(value);
31
+ }
32
+ },
33
+ color: {
34
+ type: String,
35
+ default: '#287afa'
36
+ },
37
+ textColor: {
38
+ type: String,
39
+ default: '#666'
40
+ },
41
+ duration: {
42
+ type: Number,
43
+ default: 0
44
+ },
45
+ show: {
46
+ type: Boolean,
47
+ default: false
48
+ }
49
+ });
50
+
51
+ const emit = defineEmits(['update:show']);
52
+
53
+ const config = inject<any>('config');
54
+ const currentShow = ref(props.show);
55
+ let timer: NodeJS.Timeout | null = null;
56
+
57
+ watch(
58
+ () => props.show,
59
+ (newValue) => {
60
+ currentShow.value = newValue;
61
+ if (newValue) {
62
+ if (timer) {
63
+ clearTimeout(timer);
64
+ }
65
+ if (props.duration > 0) {
66
+ timer = setTimeout(() => {
67
+ currentShow.value = false;
68
+ emit('update:show', false);
69
+ }, props.duration);
70
+ }
71
+ }
72
+ }
73
+ );
74
+
75
+ watch(
76
+ () => props.duration,
77
+ (newValue) => {
78
+ if (currentShow.value && newValue > 0) {
79
+ if (timer) {
80
+ clearTimeout(timer);
81
+ }
82
+ timer = setTimeout(() => {
83
+ currentShow.value = false;
84
+ emit('update:show', false);
85
+ }, newValue);
86
+ }
87
+ }
88
+ );
89
+
90
+ onMounted(()=>{
91
+ if (currentShow.value && props.duration > 0) {
92
+ if (timer) {
93
+ clearTimeout(timer);
94
+ }
95
+ timer = setTimeout(() => {
96
+ currentShow.value = false;
97
+ emit('update:show', false);
98
+ }, props.duration);
99
+ }
100
+ })
101
+
102
+ const loadingStyle = computed(() => {
103
+ return {
104
+ '--loading-color': props.color,
105
+ '--text-color': props.textColor
106
+ };
107
+ });
108
+ </script>
109
+
110
+ <style lang="scss" scoped>
111
+ .v-loading {
112
+ min-height: 150rpx;
113
+ position: relative;
114
+ display: flex;
115
+ flex-direction: column;
116
+ align-items: center;
117
+ justify-content: center;
118
+ padding: 16rpx;
119
+ z-index: 1000;
120
+
121
+ &--full-screen {
122
+ position: fixed;
123
+ top: 0;
124
+ left: 0;
125
+ right: 0;
126
+ bottom: 0;
127
+ background-color: rgba(255, 255, 255, 0.8);
128
+ }
129
+
130
+ .v-loading-spinner {
131
+ width: 80rpx;
132
+ height: 80rpx;
133
+ position: relative;
134
+ margin-bottom: 12rpx;
135
+ }
136
+
137
+ .spinner-outer {
138
+ position: absolute;
139
+ top: 0;
140
+ left: 0;
141
+ right: 0;
142
+ bottom: 0;
143
+ border: 6rpx solid var(--loading-color);
144
+ border-radius: 50%;
145
+ border-top-color: transparent;
146
+ animation: spin 1s linear infinite;
147
+ }
148
+
149
+ .spinner-inner {
150
+ position: absolute;
151
+ top: 20%;
152
+ left: 20%;
153
+ transform: translate(-50%, -50%);
154
+ width: 60%;
155
+ height: 60%;
156
+ border: 6rpx solid var(--loading-color);
157
+ border-radius: 50%;
158
+ border-top-color: transparent;
159
+ animation: spin 0.6s linear infinite reverse;
160
+ }
161
+
162
+ .v-loading-text {
163
+ color: var(--text-color);
164
+ font-size: v-bind("config.fontSize.mediumText");
165
+ }
166
+
167
+ @keyframes spin {
168
+ 0% {
169
+ transform: rotate(0deg);
170
+ }
171
+ 100% {
172
+ transform: rotate(360deg);
173
+ }
174
+ }
175
+
176
+ &--small .v-loading-spinner {
177
+ width: 60rpx;
178
+ height: 60rpx;
179
+ }
180
+
181
+ &--small .spinner-outer {
182
+ border-width: 4rpx;
183
+ }
184
+
185
+ &--small .spinner-inner {
186
+ width: 70%;
187
+ height: 70%;
188
+ border-width: 4rpx;
189
+ }
190
+
191
+ &--large .v-loading-spinner {
192
+ width: 100rpx;
193
+ height: 100rpx;
194
+ }
195
+
196
+ &--large .spinner-outer {
197
+ border-width: 8rpx;
198
+ }
199
+
200
+ &--large .spinner-inner {
201
+ width: 50%;
202
+ height: 50%;
203
+ border-width: 8rpx;
204
+ }
205
+ }
206
+ </style>
@@ -0,0 +1,222 @@
1
+ <template>
2
+ <view
3
+ ref="rootRef"
4
+ class="v-menu-container"
5
+ :class="[`v-menu-container--position--${menuPosition}`, { 'v-slide-x': slideAxis === 'x' }, { 'v-slide-y': slideAxis === 'y' }]"
6
+ @touchstart="onTouchStart"
7
+ @touchmove="onTouchMove"
8
+ @touchend="onTouchEnd"
9
+ @mousedown="onMouseDown"
10
+ @mousemove="onMouseMove"
11
+ @mouseup="onMouseUp"
12
+ >
13
+ <!-- 菜单栏 -->
14
+ <view class="v-menu">
15
+ <view v-for="item in menuItems" :key="item.id" :class="['menu-item', { active: activeMenuId === item.id }]" @click="handleMenuItemClick(item.id, item)">
16
+ {{ item.title }}
17
+ </view>
18
+ </view>
19
+
20
+ <!-- 内容区 -->
21
+ <view class="menu-content">
22
+ <template v-for="(item, index) in menuItems" :key="item.id">
23
+ <view v-show="activeMenuId === item.id">
24
+ <slot :name="`content-${index}`" :item="item">
25
+ </slot>
26
+ </view>
27
+ </template>
28
+ </view>
29
+ </view>
30
+ </template>
31
+
32
+ <script lang="ts" setup>
33
+ import { ref, watch, inject, computed, getCurrentInstance, nextTick,provide } from 'vue';
34
+
35
+
36
+ interface MenuItem {
37
+ id: string;
38
+ title: string;
39
+ }
40
+
41
+ /* ========= props ========= */
42
+ const props = defineProps({
43
+ menuPosition: {
44
+ type: String,
45
+ default: 'left',
46
+ validator: (v: string) => ['left', 'right'].includes(v)
47
+ },
48
+ menuItems: {
49
+ type: Array as () => MenuItem[],
50
+ required: true
51
+ },
52
+ value: {
53
+ type: String,
54
+ default: ''
55
+ },
56
+ isSlide: {
57
+ // 是否启用滑动
58
+ type: Boolean,
59
+ default: false
60
+ },
61
+ slideAxis: {
62
+ // 滑动方向
63
+ type: String as () => 'x' | 'y',
64
+ default: 'x'
65
+ }
66
+ });
67
+
68
+ const emit = defineEmits(['update:value', 'select']);
69
+
70
+ const config = inject<any>('config');
71
+ const activeMenuId = ref(props.value || props.menuItems[0]?.id || '');
72
+
73
+ /* ========= 滑动核心 ========= */
74
+ const rootRef = ref<HTMLElement>();
75
+ const startX = ref(0);
76
+ const startY = ref(0);
77
+ const isTracking = ref(false);
78
+ const SLIDE_THRESHOLD = 50;
79
+
80
+ const currentIndex = computed(() => props.menuItems.findIndex((item) => item.id === activeMenuId.value));
81
+
82
+ const next = () => {
83
+ const len = props.menuItems.length;
84
+ const idx = (currentIndex.value + 1 + len) % len;
85
+ handleMenuItemClick(props.menuItems[idx].id, props.menuItems[idx]);
86
+ };
87
+
88
+ const prev = () => {
89
+ const len = props.menuItems.length;
90
+ const idx = (currentIndex.value - 1 + len) % len;
91
+ handleMenuItemClick(props.menuItems[idx].id, props.menuItems[idx]);
92
+ };
93
+
94
+ const handleMenuItemClick = (id: string, row: MenuItem) => {
95
+ activeMenuId.value = id;
96
+ emit('update:value', id);
97
+ emit('select', id, row);
98
+ };
99
+
100
+ /* 统一手势处理 */
101
+ const onStart = (x: number, y: number) => {
102
+ if (!props.isSlide) return;
103
+ startX.value = x;
104
+ startY.value = y;
105
+ isTracking.value = true;
106
+ };
107
+
108
+ const onMove = (x: number, y: number) => {
109
+ if (!isTracking.value) return;
110
+ const dx = x - startX.value;
111
+ const dy = y - startY.value;
112
+ const dir = props.slideAxis === 'x' ? dx : dy;
113
+ const orth = props.slideAxis === 'x' ? dy : dx;
114
+ if (Math.abs(orth) > Math.abs(dir)) return; // 优先同轴
115
+ };
116
+
117
+ const onEnd = (x: number, y: number) => {
118
+ if (!isTracking.value) return;
119
+ isTracking.value = false;
120
+ const delta = props.slideAxis === 'x' ? x - startX.value : y - startY.value;
121
+ if (delta > SLIDE_THRESHOLD) prev();
122
+ else if (delta < -SLIDE_THRESHOLD) next();
123
+ };
124
+
125
+ /* Touch 事件 */
126
+ const onTouchStart = (e: TouchEvent) => onStart(e.touches[0].clientX, e.touches[0].clientY);
127
+
128
+ const onTouchMove = (e: TouchEvent) => {
129
+ if (!props.isSlide) return;
130
+ onMove(e.touches[0].clientX, e.touches[0].clientY);
131
+ e.preventDefault?.();
132
+ };
133
+
134
+ const onTouchEnd = (e: TouchEvent) => onEnd(e.changedTouches[0].clientX, e.changedTouches[0].clientY);
135
+
136
+ /* Mouse 事件(PC H5) */
137
+ const onMouseDown = (e: MouseEvent) => onStart(e.clientX, e.clientY);
138
+ const onMouseMove = (e: MouseEvent) => {
139
+ if (!isTracking.value) return;
140
+ onMove(e.clientX, e.clientY);
141
+ e.preventDefault?.();
142
+ };
143
+ const onMouseUp = (e: MouseEvent) => onEnd(e.clientX, e.clientY);
144
+
145
+ /* 监听 v-model 外部更新 */
146
+ watch(
147
+ () => props.value,
148
+ (newVal) => {
149
+ if (newVal && newVal !== activeMenuId.value) {
150
+ activeMenuId.value = newVal;
151
+ }
152
+ }
153
+ );
154
+
155
+ /* H5:禁止横向/纵向滚动冲突 */
156
+ const isH5 = typeof plus === 'undefined';
157
+ if (isH5) {
158
+ nextTick(() => {
159
+ const el = (getCurrentInstance()?.proxy as any)?.$el as HTMLElement;
160
+ if (el) {
161
+ el.style.overscrollBehaviorX = props.slideAxis === 'x' ? 'contain' : 'auto';
162
+ el.style.overscrollBehaviorY = props.slideAxis === 'y' ? 'contain' : 'auto';
163
+ }
164
+ });
165
+ }
166
+ </script>
167
+
168
+ <style lang="scss" scoped>
169
+ .v-menu-container {
170
+ display: flex;
171
+ width: 100%;
172
+ height: 100%;
173
+ touch-action: pan-y; /* 允许垂直滚动,水平自己接管 */
174
+
175
+
176
+
177
+ &--position--right {
178
+ flex-direction: row-reverse;
179
+ }
180
+ }
181
+
182
+ .v-menu {
183
+ display: flex;
184
+ flex-direction: column;
185
+ background-color: v-bind('config.VMenu.backgroundColor');
186
+ border-right: 1rpx solid v-bind('config.border.color');
187
+ width: 200rpx;
188
+ height: 100%;
189
+ overflow-y: auto;
190
+ transition: all 0.3s ease;
191
+ border-radius: 6rpx 0 0 6rpx;
192
+ }
193
+
194
+ .menu-item {
195
+ padding: 12rpx 16rpx;
196
+ cursor: pointer;
197
+ color: v-bind('config.fontColor.subTitle');
198
+ transition: all 0.2s ease;
199
+ border-bottom: 1rpx solid transparent;
200
+
201
+ &:hover {
202
+ background-color: v-bind('config.VMenu.highlightBackgroundColor');
203
+ }
204
+
205
+ &.active {
206
+ background-color: v-bind('config.VMenu.highlightBackgroundColor');
207
+ color: v-bind('config.fontColor.mainText');
208
+ font-weight: bold;
209
+ border-left: 6rpx solid v-bind('config.border.color');
210
+ }
211
+ }
212
+
213
+ .menu-content {
214
+ flex: 1;
215
+ padding: 16rpx;
216
+ background-color: v-bind('config.VMenu.reversal');
217
+ min-height: 600rpx;
218
+ overflow: auto;
219
+ box-shadow: -2rpx 0 5px rgba(0, 0, 0, 0.05);
220
+ }
221
+
222
+ </style>