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,357 +0,0 @@
1
- <template>
2
- <view class="v-image-container" @click="handleClick">
3
- <!-- 根据环境选择不同的标签 -->
4
- <template v-if="!isError">
5
- <image
6
- v-if="isAppPlus"
7
- ref="imgElement"
8
- :src="finalSrc"
9
- :class="['v-image', `v-image--${model}`, { 'v-image--loading': isLoading, 'v-image--disabled': disabled }]"
10
- @click="handlePreview"
11
- @load="handleLoad"
12
- @error="handleError"
13
- />
14
- <img
15
- v-else
16
- ref="imgElement"
17
- :src="finalSrc"
18
- :class="['v-image', `v-image--${model}`, { 'v-image--loading': isLoading, 'v-image--disabled': disabled }]"
19
- @click="handlePreview"
20
- @load="handleLoad"
21
- @error="handleError"
22
- />
23
- </template>
24
-
25
- <slot name="loading" v-if="isLoading">
26
- <view class="v-image-loading">
27
- <v-loading text="加载中……" show />
28
- </view>
29
- </slot>
30
- <slot name="error" v-if="isError">
31
- <view class="v-image-error">
32
- <text class="error-text">加载失败</text>
33
- </view>
34
- </slot>
35
- </view>
36
- <v-overlay v-model:value="showPreview" @click="closePreview" v-if="showPreview">
37
- <image v-if="isAppPlus" :src="finalPreviewSrc" :class="['v-image-preview__img', `v-image-preview__img--${previewModel}`]" @click.stop />
38
- <img v-else :src="finalPreviewSrc" :class="['v-image-preview__img', `v-image-preview__img--${previewModel}`]" @click.stop />
39
- </v-overlay>
40
- </template>
41
-
42
- <script lang="ts" setup>
43
- import { ref, watch, onMounted, onUnmounted, inject, computed } from 'vue';
44
- const props = defineProps({
45
- src: {
46
- type: String,
47
- default: '',
48
- required: true
49
- },
50
- width: {
51
- type: String,
52
- default: '100%'
53
- },
54
- height: {
55
- type: String,
56
- default: '100%'
57
- },
58
- model: {
59
- type: String,
60
- default: 'square',
61
- validator: (val: string) => ['circle', 'rounded', 'square'].includes(val)
62
- },
63
- borderRadius: {
64
- type: String,
65
- default: '10rpx'
66
- },
67
- previewWidth: {
68
- type: String,
69
- default: 'auto'
70
- },
71
- previewHeight: {
72
- type: String,
73
- default: 'auto'
74
- },
75
- enablePreview: {
76
- type: Boolean,
77
- default: false
78
- },
79
- previewModel: {
80
- type: String,
81
- default: 'square',
82
- validator: (val: string) => ['circle', 'rounded', 'square'].includes(val)
83
- },
84
- previewBorderRadius: {
85
- type: String,
86
- default: '10rpx'
87
- },
88
- loading: {
89
- type: Boolean,
90
- default: false
91
- },
92
- disabled: {
93
- type: Boolean,
94
- default: false
95
- },
96
- lazy: {
97
- type: Boolean,
98
- default: false
99
- },
100
- threshold: {
101
- type: Number,
102
- default: 0.1
103
- },
104
- delay: {
105
- type: Number,
106
- default: 800
107
- },
108
- isLoading: {
109
- type: Boolean,
110
- default: false
111
- },
112
- retryCount: {
113
- type: Number,
114
- default: 3
115
- },
116
- fit: {
117
- type: String,
118
- default: 'cover'
119
- },
120
- previewFit: {
121
- type: String,
122
- default: ''
123
- }
124
- });
125
-
126
- const emit = defineEmits(['update:enablePreview', 'update:loading', 'handleLoading', 'handError', 'click']);
127
-
128
- const config = inject<any>('config');
129
- const showPreview = ref(false);
130
- const isLoading = ref(props.isLoading);
131
- const isError = ref(false);
132
- const imgElement = ref<HTMLElement | null>(null);
133
- const displaySrc = ref<string | null>(null);
134
-
135
- let lazyLoadTimer: any = null;
136
- let scrollHandler: any = null;
137
- const retryCount = ref(0);
138
- const maxRetryCount = ref(props.retryCount);
139
-
140
- // 判断是否是 App 环境
141
- const isAppPlus = ref(false);
142
-
143
- // 判断是否是 HTTP 或 HTTPS 链接
144
- const isHttpOrHttps = (url: string) => {
145
- return /^https?:\/\//.test(url);
146
- };
147
- // 动态计算图片路径
148
- const finalSrc = computed(() => {
149
- if (!props.src) return '';
150
- return isHttpOrHttps(props.src) ? props.src : config.VImage.prefix + props.src;
151
- });
152
-
153
- const finalPreviewSrc = computed(() => {
154
- if (!props.src) return '';
155
- return isHttpOrHttps(props.src) ? props.src : config.VImage.prefix + props.src;
156
- });
157
-
158
- onMounted(() => {
159
- // 判断当前环境
160
- if (process.env.VUE_APP_PLATFORM === 'app-plus') {
161
- isAppPlus.value = true;
162
- } else {
163
- isAppPlus.value = false;
164
- }
165
-
166
- if (props.lazy && props.src && window) {
167
- window.addEventListener('scroll', handleScroll);
168
- handleScroll();
169
- } else {
170
- displaySrc.value = finalSrc.value;
171
- }
172
- });
173
-
174
- const handleScroll = () => {
175
- if (!props.lazy || !props.src || !imgElement.value) return;
176
-
177
- const rect = imgElement.value.getBoundingClientRect();
178
- const isVisible = rect.top < (window.innerHeight || document.documentElement.clientHeight) && rect.bottom > 0;
179
-
180
- if (isVisible) {
181
- loadImage();
182
- }
183
- };
184
-
185
- const loadImage = () => {
186
- clearTimeout(lazyLoadTimer);
187
- lazyLoadTimer = setTimeout(() => {
188
- displaySrc.value = finalSrc.value;
189
- if (displaySrc.value && displaySrc.value.trim()) {
190
- isLoading.value = false
191
- isError.value = false;
192
- }
193
- }, props.delay);
194
- };
195
-
196
- const handleLoad = () => {
197
- isLoading.value = false;
198
- emit('handleLoading');
199
- retryCount.value = 0; // 成功后重置重试计数
200
- };
201
-
202
- const handleRetry = () => {
203
- if (retryCount.value < maxRetryCount.value) {
204
- retryCount.value++;
205
- displaySrc.value = null;
206
- isLoading.value = true;
207
- isError.value = false;
208
- loadImage();
209
- emit('handError', retryCount.value);
210
- } else {
211
- emit('handError', retryCount.value);
212
- }
213
- };
214
-
215
- const handleError = () => {
216
- isLoading.value = false;
217
- isError.value = true;
218
- handleRetry();
219
- };
220
-
221
- const handlePreview = () => {
222
- if (props.enablePreview && props.src && !props.disabled) {
223
- showPreview.value = true;
224
- }
225
- };
226
-
227
- const handleClick = () => {
228
- emit('click');
229
- };
230
-
231
- const closePreview = () => {
232
- showPreview.value = false;
233
- };
234
-
235
- watch(
236
- () => showPreview.value,
237
- (newValue) => {
238
- emit('update:enablePreview', newValue);
239
- }
240
- );
241
-
242
- watch(
243
- () => isLoading.value,
244
- () => {
245
- emit('update:loading', isLoading.value);
246
- }
247
- );
248
-
249
- watch(
250
- () => props.src,
251
- (newSrc) => {
252
- if (!props.lazy) {
253
- displaySrc.value = finalSrc.value;
254
- if (newSrc) {
255
- isLoading.value = true;
256
- isError.value = false;
257
- }
258
- } else {
259
- if (imgElement.value && window) {
260
- loadImage();
261
- }
262
- }
263
- }
264
- );
265
-
266
- onUnmounted(() => {
267
- clearTimeout(lazyLoadTimer);
268
- if (window) {
269
- window.removeEventListener('scroll', handleScroll);
270
- }
271
- });
272
- </script>
273
-
274
- <style lang="scss" scoped>
275
- .v-image-container {
276
- width: 100%;
277
- height: 100%;
278
- display: flex;
279
- justify-content: center;
280
- align-items: center;
281
- position: relative;
282
- }
283
-
284
- .v-image {
285
- max-width: 100%;
286
- max-height: 100%;
287
- width: v-bind('props.width');
288
- height: v-bind('props.height');
289
- object-fit: v-bind('fit');
290
-
291
- &--circle {
292
- border-radius: 50%;
293
- }
294
-
295
- &--rounded {
296
- border-radius: v-bind('props.borderRadius');
297
- }
298
-
299
- &--square {
300
- border-radius: 0;
301
- }
302
-
303
- &--loading,
304
- &--disabled {
305
- opacity: v-bind('config.opacity.disabled');
306
- }
307
- }
308
-
309
- .v-image-preview__img {
310
- max-width: 90%;
311
- max-height: 90%;
312
- object-fit: v-bind('previewFit');
313
-
314
- &--circle {
315
- border-radius: 50%;
316
- }
317
-
318
- &--rounded {
319
- border-radius: v-bind('props.previewBorderRadius');
320
- }
321
-
322
- &--square {
323
- border-radius: 0;
324
- }
325
- }
326
-
327
- .v-image-loading {
328
- position: absolute;
329
- top: 0;
330
- left: 0;
331
- width: 100%;
332
- height: 100%;
333
- display: flex;
334
- justify-content: center;
335
- align-items: center;
336
- z-index: 1;
337
- }
338
-
339
- .v-image-error {
340
- position: absolute;
341
- top: 0;
342
- left: 0;
343
- width: 100%;
344
- height: 100%;
345
- display: flex;
346
- flex-direction: column;
347
- justify-content: center;
348
- align-items: center;
349
- z-index: 1;
350
-
351
- .error-text {
352
- font-size: v-bind('config.fontSize.smallText');
353
- color: v-bind('config.fontColor.subTitle');
354
- margin-top: 12rpx;
355
- }
356
- }
357
- </style>
@@ -1,101 +0,0 @@
1
- <template>
2
- <v-input
3
- :value="displayValue"
4
- v-bind="$props"
5
- class="desensitize-input"
6
- @update:value="onInput"
7
- @focus="emit('focus', $event)"
8
- @blur="emit('blur', $event)"
9
- @confirm="emit('confirm', $event)"
10
- >
11
- <template #left><slot name="left" /></template>
12
- <template #right><slot name="right" /></template>
13
- </v-input>
14
- </template>
15
-
16
- <script setup lang="ts">
17
- import { ref, watch, computed } from 'vue';
18
-
19
- type BuiltInRule = 'name' | 'phone' | 'idcard';
20
- type CustomRule = (raw: string) => string;
21
- type Rule = BuiltInRule | CustomRule;
22
-
23
- const props = defineProps({
24
- modelValue: { type: String, default: '' },
25
- size: { type: String, default: 'medium' },
26
- placeholder: { type: [String, Array], default: '' },
27
- maxlength: { type: Number, default: null },
28
- disabled: { type: Boolean, default: false },
29
- showCounter: { type: Boolean, default: false },
30
- borderModel: { type: String, default: 'all' },
31
- combinationConfig: { type: Object, default: () => ({ isShow: false }) },
32
- autoFocus: { type: Boolean, default: false },
33
- realType: { type: String, default: 'text' },
34
- desensitize: { type: Boolean, default: true },
35
- rule: { type: [String, Function] as () => Rule, default: 'phone' }
36
- });
37
-
38
- const emit = defineEmits(['update:modelValue', 'focus', 'blur', 'confirm']);
39
-
40
- /* ========= 内置规则 ========= */
41
- const builtInRules: Record<BuiltInRule, (v: string) => string> = {
42
- /* 姓名:保留首字母和末字母,中间全星号 */
43
- name: (v) => (v.length <= 2 ? v.slice(-1).padStart(v.length, '*') : v[0] + '*'.repeat(v.length - 2) + v.slice(-1)),
44
-
45
- /* 手机:保留前三后四,中间星号 */
46
- phone: (v) => {
47
- const digits = v.replace(/\D/g, '');
48
- if (digits.length < 7) return v;
49
-
50
- let result = '';
51
- let digitIndex = 0;
52
-
53
- for (let i = 0; i < v.length; i++) {
54
- if (/\d/.test(v[i])) {
55
- if (digitIndex < 3) {
56
- result += v[i];
57
- } else if (digitIndex >= digits.length - 4) {
58
- result += v[i];
59
- } else {
60
- result += '*';
61
- }
62
- digitIndex++;
63
- } else {
64
- result += v[i];
65
- }
66
- }
67
- return result;
68
- },
69
-
70
- /* 身份证:保留前 4 后 4,中间全星号 */
71
- idcard: (v) => {
72
- const len = v.length;
73
- if (len < 8) return v;
74
- return v.slice(0, 4) + '*'.repeat(len - 8) + v.slice(-4);
75
- }
76
- };
77
-
78
- /* ========= 脱敏显示值 ========= */
79
- const displayValue = computed(() => {
80
- if (!props.desensitize) return props.modelValue;
81
-
82
- const rule = props.rule;
83
- if (typeof rule === 'function') {
84
- return rule(props.modelValue);
85
- }
86
- return builtInRules[rule as BuiltInRule](props.modelValue);
87
- });
88
-
89
- /* ========= 输入事件:直接传递原始值 ========= */
90
- const onInput = (val: string) => {
91
- // 注意:这里传入的val是脱敏后的显示值,我们需要直接更新原始值
92
- emit('update:modelValue', val);
93
- };
94
- </script>
95
-
96
- <style scoped>
97
- .desensitize-input :deep(input) {
98
- font-family: monospace;
99
- line-height: 1.5;
100
- }
101
- </style>
@@ -1,11 +0,0 @@
1
- <template>
2
- <view>
3
-
4
- </view>
5
- </template>
6
-
7
- <script lang="ts" setup>
8
- </script>
9
-
10
- <style lang="scss" scoped>
11
- </style>
@@ -1,141 +0,0 @@
1
- <template>
2
- <view class="v-pages">
3
- <view class="pages-container">
4
- <v-button :disabled="currentPage <= 1" @click="changePage(currentPage - 1)" size="small">上一页</v-button>
5
- <template v-for="(page, index) in pageList" :key="index">
6
- <view v-if="page === '...'" class="page-item" :class="{ active: currentPage === page }" style="cursor: default; color: #ccc">
7
- {{ page }}
8
- </view>
9
- <view v-else class="page-item" :class="{ active: page === currentPage }" @click="changePage(page)">
10
- {{ page }}
11
- </view>
12
- </template>
13
- <v-button :disabled="currentPage >= totalPages" @click="changePage(currentPage + 1)" size="small">下一页</v-button>
14
- </view>
15
- </view>
16
- </template>
17
-
18
- <script setup lang="ts">
19
- import { computed, inject } from 'vue';
20
-
21
- const props = defineProps({
22
- total: {
23
- type: Number,
24
- default: 0
25
- },
26
- pageSize: {
27
- type: Number,
28
- default: 10
29
- },
30
- currentPage: {
31
- type: Number,
32
- default: 1
33
- },
34
- pageRange: {
35
- type: Number,
36
- default: 3
37
- }
38
- });
39
-
40
- const emit = defineEmits(['update:currentPage', 'change']);
41
-
42
- const config = inject<any>('config');
43
- const totalPages = computed(() => {
44
- return Math.ceil(props.total / props.pageSize);
45
- });
46
-
47
- const pageList = computed(() => {
48
- const list = [];
49
- const totalPage = totalPages.value;
50
- const maxPagesToShow = props.pageRange * 2 + 1;
51
-
52
- if (totalPage <= maxPagesToShow) {
53
- for (let i = 1; i <= totalPage; i++) {
54
- list.push(i);
55
- }
56
- } else {
57
- const startPage = Math.max(1, props.currentPage - props.pageRange);
58
- const endPage = Math.min(totalPage, props.currentPage + props.pageRange);
59
-
60
- if (startPage > 1) {
61
- list.push(1);
62
- if (startPage > 2) {
63
- list.push('...');
64
- }
65
- }
66
-
67
- for (let i = startPage; i <= endPage; i++) {
68
- list.push(i);
69
- }
70
-
71
- if (endPage < totalPage - 1) {
72
- list.push('...');
73
- if (endPage < totalPage) {
74
- list.push(totalPage);
75
- }
76
- }
77
- }
78
-
79
- return list;
80
- });
81
-
82
- const changePage = (page: number) => {
83
- if (page < 1 || page > totalPages.value) return;
84
- emit('update:currentPage', page);
85
- emit('change', page);
86
- };
87
- </script>
88
-
89
- <style lang="scss" scoped>
90
- .v-pages {
91
- display: flex;
92
- justify-content: center;
93
- padding: 20rpx 0;
94
- }
95
-
96
- .pages-container {
97
- display: flex;
98
- align-items: center;
99
- justify-content: center;
100
- }
101
-
102
- .page-item {
103
- width: 45rpx;
104
- height: 45rpx;
105
- display: flex;
106
- align-items: center;
107
- justify-content: center;
108
- margin: 0 5rpx;
109
- border-radius: 6rpx;
110
- background-color: v-bind('config.backgroundColor.reversal');
111
- cursor: pointer;
112
- transition: all 0.3s;
113
- border: 1px solid v-bind('config.border.color');
114
-
115
- &:hover {
116
- border-color: v-bind('config.border.default');
117
- }
118
-
119
- &:disabled {
120
- cursor: not-allowed;
121
- opacity: v-bind('config.opacity.disabled');
122
- border-color: v-bind('config.border.color');
123
- }
124
-
125
- &.active {
126
- background-color: v-bind('config.backgroundColor.default');
127
- color: v-bind('config.fontColor.reversal');
128
- border-color: v-bind('config.border.default');
129
- }
130
-
131
- &.page-item--no-border {
132
- border: none;
133
- box-shadow: none;
134
- background-color: transparent;
135
- }
136
-
137
- &.page-item--no-bg {
138
- background-color: transparent;
139
- }
140
- }
141
- </style>
@@ -1,109 +0,0 @@
1
- <template>
2
- <view>
3
- <v-popup v-model:value="isShow" :title="title" :scrollHeight="scrollHeight" @close="handleClose" @confirm="handleConfirm">
4
- <view class="popup-content">
5
- <view v-for="(item, index) in safeList" :key="index" class="picker-item" :class="{ active: item.value === selectedValue }" @click="handleSelect(item)">
6
- {{ item.label }}
7
- </view>
8
- <view v-show="safeList.length === 0">
9
- <slot name="is-null">
10
- <v-null text="暂无数据"></v-null>
11
- </slot>
12
- </view>
13
- </view>
14
- </v-popup>
15
- </view>
16
- </template>
17
-
18
- <script setup lang="ts">
19
- import { ref, computed, watch,inject } from 'vue';
20
-
21
- interface PickerItem {
22
- label: string;
23
- value: string | number;
24
- }
25
- /**
26
- * v-picker 拾取器
27
- * value 是否显示拾取器 默认值:false 可选值true显示、false隐藏
28
- * list 拾取器数据列表
29
- * title 拾取器标题
30
- * selected 默认选中项 默认值:null
31
- * scrollHeight 拾取器滚动区域高度 默认值:400
32
- * 相关事件:confirm 选中事件 select选择事件
33
- */
34
- const props = defineProps({
35
- value: {
36
- type: Boolean,
37
- default: false,
38
- required: true
39
- },
40
- list: {
41
- type: Array as () => PickerItem[],
42
- required: true
43
- },
44
- title: {
45
- type: String,
46
- default: ''
47
- },
48
- selected: {
49
- type: [String, Number],
50
- default: null
51
- },
52
- scrollHeight: {
53
- type: Number,
54
- default: 400
55
- }
56
- });
57
-
58
- const emit = defineEmits(['update:value', 'close', 'confirm', 'select']);
59
-
60
- const isShow = ref(props.value);
61
- // 安全列表计算属性
62
- const safeList = computed(() => props.list || []);
63
- const selectedValue = ref(
64
- props.selected !== null && props.selected !== undefined
65
- ? props.selected
66
- : (safeList.value.length > 0 && safeList.value[0].value !== undefined ? safeList.value[0].value : '')
67
- );
68
- const config = inject<any>('config');
69
- const selectedItem = computed(() => {
70
- return safeList.value.find((item) => item.value === selectedValue.value) || null;
71
- });
72
-
73
- watch(
74
- () => props.value,
75
- (newValue) => {
76
- isShow.value = newValue;
77
- }
78
- );
79
-
80
- function handleClose() {
81
- isShow.value = false;
82
- emit('update:value', false);
83
- emit('close', false);
84
- }
85
-
86
- function handleSelect(item: PickerItem) {
87
- selectedValue.value = item.value;
88
- emit('select', item);
89
- }
90
-
91
- function handleConfirm() {
92
- isShow.value = false;
93
- emit('confirm', selectedItem.value);
94
- emit('update:value', false);
95
- }
96
- </script>
97
- <style lang="scss">
98
- .picker-item {
99
- padding: 20rpx 30rpx;
100
- font-size: v-bind("config.fontSize.mediumText");
101
- color: v-bind("config.fontColor.mainText");
102
- border-bottom: 1rpx solid v-bind("config.border.color");
103
- }
104
-
105
- .picker-item.active {
106
- color: v-bind("config.fontColor.default");
107
- font-weight: 500;
108
- }
109
- </style>