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,172 @@
1
+ <template>
2
+ <scroll-view
3
+ class="infinite-scroll-container"
4
+ :scroll-y="true"
5
+ :style="containerStyle"
6
+ @scroll="handleScroll"
7
+ :scroll-with-animation="false"
8
+ @scrolltolower="handleScrollToLower"
9
+ >
10
+ <slot />
11
+ <!-- 加载状态提示 -->
12
+ <template v-if="$slots.loading">
13
+ <slot name="loading" />
14
+ </template>
15
+ <template v-else-if="loading">
16
+ <view class="status-text">加载中...</view>
17
+ </template>
18
+ <template v-if="$slots.null">
19
+ <slot name="null" />
20
+ </template>
21
+ <template v-else-if="noMore">
22
+ <view class="status-text">没有更多了</view>
23
+ </template>
24
+ </scroll-view>
25
+ </template>
26
+
27
+ <script lang="ts" setup>
28
+ import { ref, onMounted, onUnmounted, nextTick,inject } from 'vue';
29
+
30
+ const props = defineProps({
31
+ load: {
32
+ type: Function,
33
+ required: true
34
+ },
35
+ disabled: {
36
+ type: Boolean,
37
+ default: false
38
+ },
39
+ immediate: {
40
+ type: Boolean,
41
+ default: true
42
+ },
43
+ distance: {
44
+ type: Number,
45
+ default: 200
46
+ },
47
+ throttle: {
48
+ type: Number,
49
+ default: 200
50
+ },
51
+ containerStyle: {
52
+ type: Object,
53
+ default: () => ({})
54
+ },
55
+ noMore: {
56
+ type: Boolean,
57
+ default: false
58
+ },
59
+ timeout: {
60
+ type: Number,
61
+ default: 5000
62
+ }
63
+ });
64
+
65
+ const config = inject('config');
66
+ const loading = ref(false);
67
+ const lastCall = ref(0);
68
+ const isFirstLoad = ref(true);
69
+ const lastScrollHeight = ref(0);
70
+ let timeoutId: NodeJS.Timeout | null = null;
71
+
72
+ // 节流函数
73
+ const throttle = (fn: Function, delay: number) => {
74
+ let lastTime = 0;
75
+ return function (this: any, ...args: any[]) {
76
+ const now = Date.now();
77
+ if (now - lastTime >= delay) {
78
+ fn.apply(this, args);
79
+ lastTime = now;
80
+ }
81
+ };
82
+ };
83
+
84
+ // 处理滚动事件
85
+ const handleScroll = throttle((event: any) => {
86
+ const { scrollTop, scrollHeight } = event.detail;
87
+ lastScrollHeight.value = scrollHeight;
88
+
89
+ // 只有当内容高度变化时才处理
90
+ if (scrollHeight !== lastScrollHeight.value) {
91
+ checkLoadMore(scrollTop, scrollHeight);
92
+ }
93
+ }, props.throttle);
94
+
95
+ // 处理滚动到底部事件(作为兜底)
96
+ const handleScrollToLower = () => {
97
+ if (!loading.value && !props.noMore && !props.disabled) {
98
+ loadMore();
99
+ }
100
+ };
101
+
102
+ // 检查是否需要加载更多
103
+ const checkLoadMore = (scrollTop: number, scrollHeight: number) => {
104
+ const now = Date.now();
105
+ if (now - lastCall.value < props.throttle) return;
106
+ lastCall.value = now;
107
+
108
+ // 计算距离底部的距离
109
+ const triggerPosition = scrollTop + props.distance >= scrollHeight - props.distance;
110
+
111
+ if (triggerPosition) {
112
+ loadMore();
113
+ }
114
+ };
115
+
116
+ // 加载更多数据
117
+ const loadMore = async () => {
118
+ if (props.disabled || loading.value || props.noMore) return;
119
+
120
+ loading.value = true;
121
+
122
+ try {
123
+ await props.load();
124
+ // 等待DOM更新
125
+ await nextTick();
126
+ } catch (e) {
127
+ console.error('加载失败:', e);
128
+ } finally {
129
+ loading.value = false;
130
+ isFirstLoad.value = false;
131
+ }
132
+ };
133
+
134
+ // 初始化检查
135
+ const checkInitialLoad = () => {
136
+ // 不使用节点查询,改为延迟执行首次加载
137
+ if (props.immediate && isFirstLoad.value) {
138
+ setTimeout(() => {
139
+ loadMore();
140
+ }, 100);
141
+ }
142
+ };
143
+
144
+ onMounted(() => {
145
+ checkInitialLoad();
146
+ });
147
+
148
+ onUnmounted(() => {
149
+ if (timeoutId) {
150
+ clearTimeout(timeoutId);
151
+ }
152
+ });
153
+ </script>
154
+
155
+ <style lang="scss">
156
+ .infinite-scroll-container {
157
+ box-sizing: border-box;
158
+ height: 100%;
159
+ position: relative;
160
+
161
+ .status-text {
162
+ padding: 20rpx;
163
+ text-align: center;
164
+ color: v-bind('config.fontColor.mainText');
165
+ font-size: v-bind('config.fontSize.mediumText');
166
+
167
+ &:last-child {
168
+ padding-bottom: 40rpx;
169
+ }
170
+ }
171
+ }
172
+ </style>
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <view :class="['v-list', `v-list--border-${borderMode}`]">
3
+ <slot></slot>
4
+ </view>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed,inject } from 'vue';
9
+
10
+ const props = defineProps({
11
+ borderMode: {
12
+ type: String,
13
+ default: 'all',
14
+ validator: (value: any) => {
15
+ return ['all', 'none', 'item'].includes(value);
16
+ }
17
+ }
18
+ });
19
+
20
+ const config = inject<any>('config');
21
+ </script>
22
+
23
+ <style lang="scss" scoped>
24
+ .v-list {
25
+ width: 100%;
26
+ background-color: v-bind("config.backgroundColor.reversal");
27
+
28
+ &--border-all {
29
+ border: 1rpx solid v-bind("config.border.color");
30
+ border-radius: 4rpx;
31
+ }
32
+
33
+ &--border-none {
34
+ border: none;
35
+ }
36
+
37
+ &--border-item {
38
+ ::v-deep .v-list-item:not(:last-child) {
39
+ border-bottom: 1px solid v-bind("config.border.color");
40
+ }
41
+ }
42
+ }
43
+ </style>
@@ -0,0 +1,142 @@
1
+ <template>
2
+ <view :class="['v-row', `v-row--justify-${justify}`, `v-row--align-${align}`, `v-row--border-${borderMode}`]" :style="rowStyle">
3
+ <slot></slot>
4
+ </view>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed, inject } from 'vue';
9
+
10
+ /**
11
+ * v-row 行
12
+ * gutter 子元素之间的间距 Number 0 -
13
+ * justify 水平排列方式 String 'start' 'start', 'end', 'center', 'space-between', 'space-around', 'space-evenly'
14
+ * align 垂直排列方式 String 'top' 'top', 'center', 'bottom', 'stretch', 'baseline'
15
+ * borderMode 边框模式 String 'all' 'all', 'none', 'bottom', 'top', 'left', 'right', 'ends', 'up-down'
16
+ */
17
+ const props = defineProps({
18
+ gutter: {
19
+ type: Number,
20
+ default: 0
21
+ },
22
+ justify: {
23
+ type: String,
24
+ default: 'start',
25
+ validator: (value: any) => {
26
+ return ['start', 'end', 'center', 'space-between', 'space-around', 'space-evenly'].includes(value);
27
+ }
28
+ },
29
+ align: {
30
+ type: String,
31
+ default: 'top',
32
+ validator: (value: string) => {
33
+ return ['top', 'center', 'bottom', 'stretch', 'baseline'].includes(value);
34
+ }
35
+ },
36
+ borderMode: {
37
+ type: String,
38
+ default: 'none',
39
+ validator: (value: string) => {
40
+ return ['all', 'none', 'bottom', 'top', 'left', 'right', 'ends', 'up-down'].includes(value);
41
+ }
42
+ }
43
+ });
44
+
45
+ const config = inject<any>('config');
46
+
47
+ const rowStyle = computed(() => {
48
+ const style: Record<string, string | number> = {};
49
+ if (props.gutter) {
50
+ style.marginLeft = `-${props.gutter / 2}rpx`;
51
+ style.marginRight = `-${props.gutter / 2}rpx`;
52
+ }
53
+ return style;
54
+ });
55
+ </script>
56
+
57
+ <style lang="scss" scoped>
58
+ .v-row {
59
+ display: flex;
60
+ flex-wrap: wrap;
61
+ box-sizing: border-box;
62
+ border: 1rpx solid v-bind('config.border.color');
63
+ border-radius: 4rpx;
64
+
65
+ // 边框模式
66
+ &--border-none {
67
+ border: none;
68
+ }
69
+
70
+ &--border-bottom {
71
+ border-bottom: 1rpx solid v-bind('config.border.color');
72
+ }
73
+
74
+ &--border-top {
75
+ border-top: 1rpx solid v-bind('config.border.color');
76
+ }
77
+
78
+ &--border-left {
79
+ border-left: 1rpx solid v-bind('config.border.color');
80
+ }
81
+
82
+ &--border-right {
83
+ border-right: 1rpx solid v-bind('config.border.color');
84
+ }
85
+
86
+ &--border-ends {
87
+ border-left: 1rpx solid v-bind('config.border.color');
88
+ border-right: 1rpx solid v-bind('config.border.color');
89
+ }
90
+
91
+ &--border-up-down {
92
+ border-top: 1rpx solid v-bind('config.border.color');
93
+ border-bottom: 1rpx solid v-bind('config.border.color');
94
+ }
95
+
96
+ // 对齐方式
97
+ &--justify-start {
98
+ justify-content: flex-start;
99
+ }
100
+
101
+ &--justify-end {
102
+ justify-content: flex-end;
103
+ }
104
+
105
+ &--justify-center {
106
+ justify-content: center;
107
+ }
108
+
109
+ &--justify-space-between {
110
+ justify-content: space-between;
111
+ }
112
+
113
+ &--justify-space-around {
114
+ justify-content: space-around;
115
+ }
116
+
117
+ &--justify-space-evenly {
118
+ justify-content: space-evenly;
119
+ }
120
+
121
+ // 垂直对齐
122
+ &--align-top {
123
+ align-items: flex-start;
124
+ }
125
+
126
+ &--align-center {
127
+ align-items: center;
128
+ }
129
+
130
+ &--align-bottom {
131
+ align-items: flex-end;
132
+ }
133
+
134
+ &--align-stretch {
135
+ align-items: stretch;
136
+ }
137
+
138
+ &--align-baseline {
139
+ align-items: baseline;
140
+ }
141
+ }
142
+ </style>
@@ -0,0 +1,79 @@
1
+ <template>
2
+ <view class="v-waterfall">
3
+ <view v-for="(column, columnIndex) in columnsList" :key="columnIndex" class="column">
4
+ <view v-for="(item, itemIndex) in column" :key="itemIndex" class="waterfall-item">
5
+ <slot name="item" :item="item" />
6
+ </view>
7
+ </view>
8
+ </view>
9
+ </template>
10
+
11
+ <script lang="ts" setup>
12
+ import { ref, watch } from 'vue';
13
+
14
+ interface WaterfallItem {
15
+ height: number;
16
+ content: any;
17
+ }
18
+
19
+ const props = defineProps({
20
+ items: {
21
+ type: Array as () => WaterfallItem[],
22
+ default: () => []
23
+ },
24
+ columns: {
25
+ type: Number,
26
+ default: 2
27
+ },
28
+ columnWidth: {
29
+ type: String,
30
+ default: '50%'
31
+ }
32
+ });
33
+
34
+ const columnsList = ref<WaterfallItem[][]>([]);
35
+
36
+ const splitIntoColumns = () => {
37
+ const cols = Array.from({ length: props.columns }, () => []);
38
+ props.items.forEach((item, index) => {
39
+ const columnIndex = index % props.columns;
40
+ cols[columnIndex].push(item);
41
+ });
42
+ columnsList.value = cols;
43
+ };
44
+
45
+ splitIntoColumns();
46
+
47
+ watch(
48
+ () => props.items,
49
+ () => {
50
+ splitIntoColumns();
51
+ },
52
+ { deep: true }
53
+ );
54
+
55
+ watch(
56
+ () => props.columns,
57
+ () => {
58
+ splitIntoColumns();
59
+ }
60
+ );
61
+ </script>
62
+
63
+ <style lang="scss" scoped>
64
+ .v-waterfall {
65
+ display: flex;
66
+ flex-wrap: wrap;
67
+ }
68
+
69
+ .column {
70
+ width: v-bind('100 / props.columns + "%"');
71
+ box-sizing: border-box;
72
+ padding: 0 5rpx;
73
+ }
74
+
75
+ .waterfall-item {
76
+ margin-bottom: 5rpx;
77
+ padding: 10rpx 0;
78
+ }
79
+ </style>
@@ -0,0 +1,96 @@
1
+ <template>
2
+ <view :class="['v-checkbox-group', vertical ? 'vertical' : 'horizontal']" :style="groupStyle" role="checkboxgroup">
3
+ <slot />
4
+ </view>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import { provide, computed, ref, watch } from 'vue';
9
+
10
+ /**
11
+ * v-checkbox-group 复选按钮组
12
+ * value 双向绑定
13
+ * vertical 布局方向 默认值:false 可选值:true竖 false:行
14
+ * disabled 是否禁用 默认值:false 可选值:false不禁用 true禁用
15
+ * indeterminate 是否开启全选/半选功能 默认值:false 可选值:false不开启 true开启
16
+ * groupStyle 自定义样式
17
+ * 相关事件:change
18
+ */
19
+ const props = defineProps({
20
+ value: {
21
+ type: Array as () => Array<string | number | object>,
22
+ default: () => []
23
+ },
24
+ vertical: {
25
+ type: Boolean,
26
+ default: false
27
+ },
28
+ disabled: {
29
+ type: Boolean,
30
+ default: false
31
+ },
32
+ indeterminate: {
33
+ type: Boolean,
34
+ default: false
35
+ },
36
+ groupStyle: {
37
+ type: Object,
38
+ default: () => ({})
39
+ }
40
+ });
41
+
42
+ const emit = defineEmits(['update:value', 'change']);
43
+
44
+ const checkedValues = ref([...props.value]);
45
+
46
+ provide('checkboxGroup', {
47
+ name: 'vCheckboxGroup',
48
+ checkedValues,
49
+ disabled: computed(() => props.disabled),
50
+ indeterminate: computed(() => props.indeterminate)
51
+ });
52
+
53
+ watch(
54
+ () => props.value,
55
+ (newVal) => {
56
+ checkedValues.value = [...newVal];
57
+ }
58
+ );
59
+
60
+ const updateValues = (newValues: any[]) => {
61
+ checkedValues.value = newValues;
62
+ emit('update:value', newValues);
63
+ emit('change', newValues);
64
+ };
65
+
66
+ defineExpose({
67
+ updateValues
68
+ });
69
+ </script>
70
+
71
+ <style lang="scss">
72
+ .v-checkbox-group {
73
+ &.horizontal {
74
+ display: flex;
75
+ flex-wrap: wrap;
76
+
77
+ .v-checkbox {
78
+ margin-right: 32rpx;
79
+
80
+ &:last-child {
81
+ margin-right: 0;
82
+ }
83
+ }
84
+ }
85
+
86
+ &.vertical {
87
+ .v-checkbox {
88
+ margin-bottom: 24rpx;
89
+
90
+ &:last-child {
91
+ margin-bottom: 0;
92
+ }
93
+ }
94
+ }
95
+ }
96
+ </style>
@@ -0,0 +1,20 @@
1
+ export function initVConsole() {
2
+ const STORAGE_KEY = 'v-console-logs';
3
+ const logs = [];
4
+
5
+ ['log', 'info', 'warn', 'error', 'debug'].forEach((type) => {
6
+ const original = console[type];
7
+ console[type] = (...args) => {
8
+ original.apply(console, args);
9
+ const time = new Date().toLocaleTimeString();
10
+ const msg = args.map((v) => (typeof v === 'object' ? JSON.stringify(v) : String(v))).join(' ');
11
+ logs.unshift({
12
+ type,
13
+ msg,
14
+ time,
15
+ blockLines: 2
16
+ });
17
+ uni.setStorageSync(STORAGE_KEY, logs);
18
+ };
19
+ });
20
+ }