v-uni-app-ui 1.0.2 → 1.0.6

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 (65) hide show
  1. package/README.md +127 -0
  2. package/dist/v-uni-app-ui.css +1 -0
  3. package/dist/v-uni-app-ui.es.js +6569 -0
  4. package/dist/v-uni-app-ui.umd.js +7 -0
  5. package/package.json +28 -8
  6. package/components/config.js +0 -123
  7. package/components/layout/v-card/v-card.vue +0 -108
  8. package/components/layout/v-grid/v-grid.vue +0 -162
  9. package/components/layout/v-icon-grid/v-icon-grid.vue +0 -195
  10. package/components/layout/v-infinite-scroll/v-infinite-scroll.vue +0 -172
  11. package/components/layout/v-list/v-list.vue +0 -43
  12. package/components/layout/v-row/v-row.vue +0 -142
  13. package/components/layout/v-waterfall/v-waterfall.vue +0 -79
  14. package/components/model/compound/v-checkbox-group/v-checkbox-group.vue +0 -96
  15. package/components/model/compound/v-console/v-console.js +0 -20
  16. package/components/model/compound/v-console/v-console.vue +0 -299
  17. package/components/model/compound/v-date-time/v-date-time.vue +0 -261
  18. package/components/model/compound/v-dialog/v-dialog.vue +0 -178
  19. package/components/model/compound/v-drum-select-picker/v-drum-select-picker.vue +0 -83
  20. package/components/model/compound/v-form/v-form.vue +0 -226
  21. package/components/model/compound/v-form-item/v-form-item.vue +0 -255
  22. package/components/model/compound/v-image/v-image.vue +0 -357
  23. package/components/model/compound/v-input-desensitize/v-input-desensitize.vue +0 -101
  24. package/components/model/compound/v-page/v-page.vue +0 -11
  25. package/components/model/compound/v-pages/v-pages.vue +0 -141
  26. package/components/model/compound/v-picker-list/v-picker-list.vue +0 -109
  27. package/components/model/compound/v-popup/v-popup.vue +0 -151
  28. package/components/model/compound/v-radio-group/v-radio-group.vue +0 -86
  29. package/components/model/compound/v-select-picker/v-select-picker.vue +0 -202
  30. package/components/model/compound/v-series-picker-list/v-series-picker-list.vue +0 -221
  31. package/components/model/compound/v-series-select-picker/v-series-select-picker.vue +0 -203
  32. package/components/model/compound/v-switch/v-switch.vue +0 -136
  33. package/components/model/compound/v-tabs-page/v-tabs-page.vue +0 -138
  34. package/components/model/native/v-badge/v-badge.vue +0 -143
  35. package/components/model/native/v-button/v-button.vue +0 -273
  36. package/components/model/native/v-carousel/v-carousel.vue +0 -138
  37. package/components/model/native/v-checkbox/v-checkbox.vue +0 -215
  38. package/components/model/native/v-collapse/v-collapse.vue +0 -190
  39. package/components/model/native/v-header-navigation-bar/v-header-navigation-bar.vue +0 -92
  40. package/components/model/native/v-input/v-input.vue +0 -352
  41. package/components/model/native/v-input-code/v-input-code.vue +0 -146
  42. package/components/model/native/v-loading/v-loading.vue +0 -206
  43. package/components/model/native/v-menu/v-menu.vue +0 -222
  44. package/components/model/native/v-menu-slide/v-menu-slide.vue +0 -364
  45. package/components/model/native/v-min-loading/v-min-loading.vue +0 -80
  46. package/components/model/native/v-null/v-null.vue +0 -97
  47. package/components/model/native/v-overlay/v-overlay.vue +0 -96
  48. package/components/model/native/v-pull-up-refresh/v-pull-up-refresh.vue +0 -157
  49. package/components/model/native/v-radio/v-radio.vue +0 -138
  50. package/components/model/native/v-scroll-list/v-scroll-list.vue +0 -169
  51. package/components/model/native/v-steps/v-steps.vue +0 -253
  52. package/components/model/native/v-table/v-table.vue +0 -203
  53. package/components/model/native/v-tabs/v-tabs.vue +0 -235
  54. package/components/model/native/v-tag/v-tag.vue +0 -206
  55. package/components/model/native/v-text/v-text.vue +0 -187
  56. package/components/model/native/v-text-button/v-text-button.vue +0 -139
  57. package/components/model/native/v-textarea/v-textarea.vue +0 -178
  58. package/components/model/native/v-title/v-title.vue +0 -91
  59. package/components/model/native/v-toast/info.png +0 -0
  60. package/components/model/native/v-toast/success.png +0 -0
  61. package/components/model/native/v-toast/v-toast.vue +0 -198
  62. package/components/model/native/v-toast/warn.png +0 -0
  63. package/components/model/native/v-upload-file-button/v-upload-file-button.vue +0 -296
  64. package/components/model/native/v-video/v-video.vue +0 -175
  65. package/components/model/native/v-window/v-window.vue +0 -158
@@ -1,226 +0,0 @@
1
- <template>
2
- <view class="v-form" :class="[layout]">
3
- <slot></slot>
4
- <slot name="operate" :handleSubmit="handleSubmit" :handleRest="handleRest" :buttonDisabled="submitting" :buttonSize="buttonSize">
5
- <view class="form-actions">
6
- <v-button :disabled="submitting" @click="handleSubmit" :size="buttonSize">
7
- <text v-if="!submitting">{{ submitText }}</text>
8
- <text v-else>提交中...</text>
9
- </v-button>
10
- <v-button type="info" @click="handleRest" :size="buttonSize" v-if="showRest">重置</v-button>
11
- </view>
12
- </slot>
13
- </view>
14
- </template>
15
-
16
- <script lang="ts" setup>
17
- import { ref, provide, reactive, watchEffect, inject } from 'vue';
18
-
19
- type FormItem = {
20
- validate: () => Promise<boolean>;
21
- resetField: () => void;
22
- };
23
-
24
- interface Rules {
25
- required?: boolean;
26
- message?: string;
27
- pattern?: RegExp;
28
- minLength?: number;
29
- maxLength?: number;
30
- validator?: (value: any) => boolean | Promise<boolean>;
31
- compareWith?: string;
32
- min?: number;
33
- max?: number;
34
- type?: 'email' | 'mobile' | 'number' | 'integer';
35
- }
36
-
37
- /**
38
- * v-form 表单
39
- * model 表单数据对象
40
- * rules 验证规则 { field: [{ required: true, message: '提示' }]}
41
- * submitText 提交按钮文字
42
- * layout 表单布局方式 默认值: vertical 可选值: vertical垂直排列 | horizontal水平排列
43
- * labelLayout 标签对齐方式 默认值: top 可选值:
44
- * labelPosition标签垂直方式 默认值: normal 可选值:center
45
- * buttonSize 按钮大小 默认值: medium 可选值:small小medium中large大
46
- * showRest 是否显示重置按钮 默认值true 可选值:true显示 false不显示
47
- * disabled 是否禁用表单 默认值false 可选值:true禁用 false不禁用
48
- * 相关事件:validate 校验、submit提交、rest重置
49
- */
50
-
51
- const props = defineProps({
52
- model: {
53
- type: Object,
54
- required: true,
55
- default: () => ({})
56
- },
57
- rules: {
58
- type: Object as () => Rules,
59
- default: () => ({})
60
- },
61
- submitText: {
62
- type: String,
63
- default: '提交'
64
- },
65
- layout: {
66
- type: String,
67
- default: 'vertical',
68
- validator: (v: string) => ['vertical', 'horizontal'].includes(v)
69
- },
70
- buttonSize: {
71
- type: String,
72
- default: 'medium',
73
- validator: (v: string) => ['small', 'medium', 'large'].includes(v)
74
- },
75
- showRest: {
76
- type: Boolean,
77
- default: true
78
- },
79
- disabled: {
80
- type: Boolean,
81
- default: false
82
- },
83
- labelWidth: {
84
- type: String,
85
- default: '180'
86
- },
87
- labelLayout: {
88
- type: String,
89
- default: 'top'
90
- },
91
- labelLast: {
92
- type: String,
93
- default: 'auto'
94
- },
95
- labelPosition: {
96
- type: String,
97
- default: 'normal'
98
- },
99
- labelSpacing: {
100
- type: Number,
101
- default: 0
102
- }
103
- });
104
-
105
- const emit = defineEmits(['submit', 'validate', 'rest']);
106
-
107
- const formHasError = ref(false);
108
- const config = inject<any>('config');
109
- const formItems = new Map<string, FormItem>();
110
- const submitting = ref(false);
111
- const formData = reactive({ ...props.model });
112
-
113
- provide('form', {
114
- rules: props.rules || {},
115
- formData,
116
- register: (name: string, item: FormItem) => {
117
- formItems.set(name, item);
118
- },
119
- unregister: (name: string) => {
120
- formItems.delete(name);
121
- },
122
- formHasError:formHasError
123
- });
124
-
125
- const handleSubmit = async () => {
126
- submitting.value = true;
127
- formHasError.value = false;
128
- try {
129
- const results = await Promise.all(Array.from(formItems.values()).map((item) => item.validate()));
130
- const isValid = results.every((valid) => valid);
131
-
132
- emit('validate', isValid);
133
- if (isValid) {
134
- emit('submit', formData);
135
- } else {
136
- formHasError.value = true;
137
- }
138
- } finally {
139
- submitting.value = false;
140
- }
141
- };
142
-
143
- const handleRest = () => {
144
- emit('rest', formData);
145
- };
146
-
147
- const resetForm = () => {
148
- formItems.forEach((item) => item.resetField());
149
- };
150
-
151
- defineExpose({
152
- resetForm,
153
- validate: handleSubmit
154
- });
155
-
156
- watchEffect(() => {
157
- Object.assign(formData, props.model);
158
- });
159
- </script>
160
-
161
- <style lang="scss">
162
- .v-form {
163
- $gap: 20rpx;
164
- $error-color: v-bind('config.fontColor.delete');
165
-
166
- &.horizontal {
167
- :deep(.form-item) {
168
- display: flex;
169
- flex-direction: row;
170
-
171
- .label {
172
- margin-right: $gap;
173
- }
174
- }
175
- }
176
-
177
- :deep(.form-item) {
178
- margin-bottom: $gap;
179
- .title {
180
- width: v-bind("props.labelWidth + 'rpx'");
181
- display: flex;
182
- align-items: v-bind('props.labelPosition');
183
-
184
- .label {
185
- width: 80%;
186
- font-size: v-bind('config.fontSize.largeText');
187
- color: v-bind('config.fontColor.subTitle');
188
- text-align: v-bind('props.labelLayout');
189
- text-align-last: v-bind('props.labelLast');
190
- letter-spacing: v-bind("props.labelSpacing + 'rpx'");
191
- }
192
- .required {
193
- width: 20%;
194
- height: 100%;
195
- .v-text {
196
- color: $error-color;
197
- margin-left: 8rpx;
198
- height: 100%;
199
- display: flex !important;
200
- align-items: v-bind('props.labelPosition');
201
- }
202
- }
203
- }
204
- .control {
205
- min-width: 50%;
206
- }
207
-
208
- .error-message {
209
- width: 100%;
210
- height: 40rpx;
211
- margin-top: 8rpx;
212
- .v-text {
213
- color: $error-color;
214
- font-size: v-bind('config.fontSize.mediumText');
215
- }
216
- }
217
- }
218
-
219
- .form-actions {
220
- margin-top: 40rpx;
221
- .v-button {
222
- width: 100%;
223
- }
224
- }
225
- }
226
- </style>
@@ -1,255 +0,0 @@
1
- <template>
2
- <view :class="['form-item', `form-item--border--${borderModel}`, { 'form-item--hint--model': !hintModel }]">
3
- <view class="title">
4
- <view class="required" v-if="requiredPosition == 'left'">
5
- <v-text v-if="isRequired">*</v-text>
6
- </view>
7
- <view v-if="label" class="label">
8
- {{ label }}
9
- </view>
10
- <view class="required" v-if="requiredPosition == 'right'">
11
- <v-text v-if="isRequired">*</v-text>
12
- </view>
13
- </view>
14
- <view class="control">
15
- <slot></slot>
16
- <view class="error-message" v-if="hintModel">
17
- <v-text v-if="error" type="danger">{{ error }}</v-text>
18
- </view>
19
- </view>
20
- <v-toast ref="toast"></v-toast>
21
- </view>
22
- </template>
23
-
24
- <script lang="ts" setup>
25
- import { ref, inject, onMounted, onUnmounted } from 'vue';
26
- import { getCurrentInstance } from 'vue';
27
-
28
- const { proxy } = getCurrentInstance();
29
-
30
- const props = defineProps({
31
- label: {
32
- type: String,
33
- default: ''
34
- },
35
- name: {
36
- type: String,
37
- required: true
38
- },
39
- requiredPosition: {
40
- type: String,
41
- default: 'right'
42
- },
43
- hintModel: {
44
- type: Boolean,
45
- default: true
46
- },
47
- borderModel: {
48
- type: String,
49
- default: 'bottom',
50
- validator: (v) => ['all', 'none', 'bottom', 'top', 'left', 'right', 'ends', 'up-down'].includes(v)
51
- }
52
- });
53
-
54
- const toast = ref(null);
55
- const error = ref('');
56
- const isRequired = ref(false);
57
- const config = inject('config');
58
- const formContext = inject('form', {
59
- rules: {},
60
- formData: {},
61
- register: (name, validate) => {
62
- console.log(name, validate);
63
- },
64
- unregister: (name) => {
65
- console.log(name);
66
- },
67
- formHasError:{
68
- value:false
69
- },
70
- });
71
-
72
- const validate = async () => {
73
- const rules = formContext.rules[props.name] || [];
74
- const value = formContext.formData[props.name];
75
- const formData = formContext.formData;
76
-
77
- for (const rule of rules) {
78
- // 必填校验
79
- if (rule.required) {
80
- let isEmpty = false;
81
- if (value === null || value === undefined || value === '') {
82
- isEmpty = true;
83
- } else if (typeof value === 'string' || typeof value === 'number') {
84
- if (value != null && value !== undefined) {
85
- isEmpty = !value.toString().trim();
86
- }
87
- } else if (Array.isArray(value)) {
88
- isEmpty = value.length === 0;
89
- } else if (typeof value === 'object') {
90
- isEmpty = Object.keys(value).length === 0;
91
- }
92
- if (isEmpty) {
93
- hintModelMessage(rule.message || `${props.label}不能为空`);
94
- return false;
95
- }
96
- }
97
-
98
- // 类型转换校验(非空后执行)
99
- let processedValue = value;
100
- if ((rule.type === 'number' || rule.type === 'integer') && value !== null && value !== undefined) {
101
- processedValue = Number(value);
102
- if (isNaN(processedValue)) {
103
- hintModelMessage(rule.message || `${props.label}必须为数字`);
104
- return false;
105
- }
106
- if (rule.type === 'integer' && !Number.isInteger(processedValue)) {
107
- hintModelMessage(rule.message || `${props.label}必须为整数`);
108
- return false;
109
- }
110
- }
111
-
112
- // 格式校验
113
- if (value !== null && value !== undefined && value !== '') {
114
- if (rule.pattern && !rule.pattern.test(value)) {
115
- hintModelMessage(rule.message || `${props.label}格式错误`);
116
- return false;
117
- }
118
-
119
- // 内置类型校验
120
- if (rule.type === 'email' && !/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(value)) {
121
- hintModelMessage(rule.message || `请输入有效的邮箱地址`);
122
- return false;
123
- }
124
-
125
- if (rule.type === 'mobile' && !/^1[3-9]\d{9}$/.test(value)) {
126
- hintModelMessage(rule.message || `请输入有效的手机号码`);
127
- return false;
128
- }
129
-
130
- // 长度校验
131
- if (rule.minLength !== undefined && value.length < rule.minLength) {
132
- hintModelMessage(rule.message || `${props.label}至少需要${rule.minLength}个字符`);
133
- return false;
134
- }
135
-
136
- if (rule.maxLength !== undefined && value.length > rule.maxLength) {
137
- hintModelMessage(rule.message || `${props.label}不能超过${rule.maxLength}个字符`);
138
- return false;
139
- }
140
-
141
- // 数值范围校验
142
- if (typeof rule.min === 'number' && processedValue < rule.min) {
143
- hintModelMessage(rule.message || `${props.label}不能小于${rule.min}`);
144
- return false;
145
- }
146
-
147
- if (typeof rule.max === 'number' && processedValue > rule.max) {
148
- hintModelMessage(rule.message || `${props.label}不能大于${rule.max}`);
149
- return false;
150
- }
151
-
152
- // 关联字段校验(如密码确认)
153
- if (rule.compareWith) {
154
- const compareValue = formData[rule.compareWith];
155
- if (value !== compareValue) {
156
- hintModelMessage(rule.message || `${props.label}必须与${rule.compareWith}保持一致`);
157
- return false;
158
- }
159
- }
160
-
161
- // 自定义校验函数
162
- if (rule.validator) {
163
- const result = await rule.validator(value, formData);
164
- if (!result) {
165
- hintModelMessage(rule.message || `${props.label}验证未通过`);
166
- return false;
167
- }
168
- }
169
- }
170
- }
171
-
172
- error.value = '';
173
- return true;
174
- };
175
-
176
- const hintModelMessage = (message: string) => {
177
- if (props.hintModel) {
178
- error.value = message;
179
- } else {
180
- if (formContext && formContext.formHasError.value) {
181
- return;
182
- }
183
- if (toast.value) {
184
- toast.value.show({
185
- message: message,
186
- type: 'error',
187
- duration: 3000
188
- });
189
- formContext.formHasError.value = true;
190
- }
191
- }
192
- };
193
-
194
- const resetField = () => {
195
- error.value = '';
196
- formContext.formData[props.name] = null;
197
- };
198
-
199
- // 注册到父表单
200
- onMounted(() => {
201
- if (formContext && formContext.register) {
202
- formContext.register(props.name, { validate, resetField });
203
- if (formContext.rules && formContext.rules[props.name]) {
204
- isRequired.value = formContext.rules[props.name].some((r) => r.required);
205
- }
206
- }
207
- });
208
-
209
- onUnmounted(() => {
210
- if (formContext && formContext.unregister) {
211
- formContext.unregister(props.name);
212
- }
213
- });
214
- </script>
215
- <style lang="scss" scoped>
216
- .form-item {
217
- &--hint--model {
218
- padding-bottom: 20rpx;
219
- }
220
-
221
- &--border--all {
222
- border: 1rpx solid v-bind('config.VFormItem.borderColor');
223
- }
224
-
225
- &--border--none {
226
- border: none;
227
- }
228
-
229
- &--border--bottom {
230
- border-bottom: 1rpx solid v-bind('config.VFormItem.borderColor');
231
- }
232
-
233
- &--border--top {
234
- border-top: 1rpx solid v-bind('config.VFormItem.borderColor');
235
- }
236
-
237
- &--border--left {
238
- border-left: 1rpx solid v-bind('config.VFormItem.borderColor');
239
- }
240
-
241
- &--border--right {
242
- border-right: 1rpx solid v-bind('config.VFormItem.borderColor');
243
- }
244
-
245
- &--border--ends {
246
- border-left: 1rpx solid v-bind('config.VFormItem.borderColor');
247
- border-right: 1rpx solid v-bind('config.VFormItem.borderColor');
248
- }
249
-
250
- &--border--up-down {
251
- border-top: 1rpx solid v-bind('config.VFormItem.borderColor');
252
- border-bottom: 1rpx solid v-bind('config.VFormItem.borderColor');
253
- }
254
- }
255
- </style>