@rao2126340634/yt-ui 1.2.20

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 (120) hide show
  1. package/package.json +43 -0
  2. package/src/components/yt-avatar/yt-avatar.vue +108 -0
  3. package/src/components/yt-badge/yt-badge.vue +65 -0
  4. package/src/components/yt-button/yt-button.vue +79 -0
  5. package/src/components/yt-calendar/yt-calendar.vue +327 -0
  6. package/src/components/yt-card/yt-card.vue +93 -0
  7. package/src/components/yt-checkbox-group/yt-checkbox-group.vue +80 -0
  8. package/src/components/yt-collapse/yt-collapse.vue +115 -0
  9. package/src/components/yt-divider/yt-divider.vue +110 -0
  10. package/src/components/yt-dots/yt-dots.vue +83 -0
  11. package/src/components/yt-empty/yt-empty.vue +43 -0
  12. package/src/components/yt-fab/yt-fab.vue +132 -0
  13. package/src/components/yt-form/yt-form.vue +86 -0
  14. package/src/components/yt-icon/icon-map.ts +52 -0
  15. package/src/components/yt-icon/yt-icon.vue +67 -0
  16. package/src/components/yt-input/yt-input.vue +162 -0
  17. package/src/components/yt-line/yt-line.vue +71 -0
  18. package/src/components/yt-loading/yt-loading.vue +66 -0
  19. package/src/components/yt-movable-view/yt-movable-view.vue +121 -0
  20. package/src/components/yt-notice-bar/yt-notice-bar.vue +91 -0
  21. package/src/components/yt-overlay/yt-overlay.vue +59 -0
  22. package/src/components/yt-picker/yt-picker.vue +111 -0
  23. package/src/components/yt-popup/yt-popup.vue +222 -0
  24. package/src/components/yt-radio-group/yt-radio-group.vue +89 -0
  25. package/src/components/yt-result/yt-result.vue +52 -0
  26. package/src/components/yt-schedule/yt-schedule.vue +737 -0
  27. package/src/components/yt-search/yt-search.vue +127 -0
  28. package/src/components/yt-segment/yt-segment.vue +65 -0
  29. package/src/components/yt-slider/yt-slider.vue +87 -0
  30. package/src/components/yt-steps/yt-steps.vue +90 -0
  31. package/src/components/yt-swiper/yt-swiper.vue +553 -0
  32. package/src/components/yt-switch/yt-switch.vue +76 -0
  33. package/src/components/yt-switch/yt-tabbar/yt-tabbar.vue +198 -0
  34. package/src/components/yt-tabbar/yt-tabbar.vue +198 -0
  35. package/src/components/yt-tag/yt-tag.vue +68 -0
  36. package/src/components/yt-textarea/yt-textarea.vue +172 -0
  37. package/src/components/yt-top-tabbar/yt-top-tabbar.vue +87 -0
  38. package/src/components/yt-virtual-list/yt-virtual-list.vue +140 -0
  39. package/src/configs/scheduleConfig.ts +31 -0
  40. package/src/hooks/useCalendar.ts +79 -0
  41. package/src/hooks/useInterval.ts +28 -0
  42. package/src/hooks/useSchedule.ts +83 -0
  43. package/src/shims.d.ts +4 -0
  44. package/src/static/icons/QRcode.png +0 -0
  45. package/src/static/icons/arrow_down.png +0 -0
  46. package/src/static/icons/arrow_down_white.png +0 -0
  47. package/src/static/icons/arrow_left.png +0 -0
  48. package/src/static/icons/arrow_left_white.png +0 -0
  49. package/src/static/icons/arrow_right.png +0 -0
  50. package/src/static/icons/arrow_right_white.png +0 -0
  51. package/src/static/icons/arrow_up.png +0 -0
  52. package/src/static/icons/arrow_up_white.png +0 -0
  53. package/src/static/icons/calendar.png +0 -0
  54. package/src/static/icons/champion.png +0 -0
  55. package/src/static/icons/close.png +0 -0
  56. package/src/static/icons/community.png +0 -0
  57. package/src/static/icons/community_active.png +0 -0
  58. package/src/static/icons/course.png +0 -0
  59. package/src/static/icons/course_active.png +0 -0
  60. package/src/static/icons/default_avatar.png +0 -0
  61. package/src/static/icons/done.png +0 -0
  62. package/src/static/icons/door_enter.png +0 -0
  63. package/src/static/icons/door_exit.png +0 -0
  64. package/src/static/icons/edit.png +0 -0
  65. package/src/static/icons/empty.png +0 -0
  66. package/src/static/icons/error.png +0 -0
  67. package/src/static/icons/fail.png +0 -0
  68. package/src/static/icons/fail_result.png +0 -0
  69. package/src/static/icons/first_place.png +0 -0
  70. package/src/static/icons/home.png +0 -0
  71. package/src/static/icons/home_active.png +0 -0
  72. package/src/static/icons/hot_topic.png +0 -0
  73. package/src/static/icons/identity.png +0 -0
  74. package/src/static/icons/info_result.png +0 -0
  75. package/src/static/icons/me.png +0 -0
  76. package/src/static/icons/me_active.png +0 -0
  77. package/src/static/icons/official.png +0 -0
  78. package/src/static/icons/passed.png +0 -0
  79. package/src/static/icons/plus.png +0 -0
  80. package/src/static/icons/read.png +0 -0
  81. package/src/static/icons/search.png +0 -0
  82. package/src/static/icons/second_place.png +0 -0
  83. package/src/static/icons/slider.png +0 -0
  84. package/src/static/icons/success_result.png +0 -0
  85. package/src/static/icons/third_place.png +0 -0
  86. package/src/static/icons/time.png +0 -0
  87. package/src/static/icons/unpassed.png +0 -0
  88. package/src/static/icons/winner.png +0 -0
  89. package/src/styles/_anim.scss +130 -0
  90. package/src/styles/_mixins.scss +6 -0
  91. package/src/styles/_theme-utils.scss +11 -0
  92. package/src/styles/_themes.scss +392 -0
  93. package/src/styles/_var.scss +65 -0
  94. package/src/styles/components/_avatar.scss +21 -0
  95. package/src/styles/components/_badge.scss +35 -0
  96. package/src/styles/components/_button.scss +131 -0
  97. package/src/styles/components/_calendar.scss +145 -0
  98. package/src/styles/components/_card.scss +34 -0
  99. package/src/styles/components/_collapse.scss +58 -0
  100. package/src/styles/components/_divider.scss +57 -0
  101. package/src/styles/components/_dots.scss +42 -0
  102. package/src/styles/components/_fab.scss +104 -0
  103. package/src/styles/components/_icon.scss +6 -0
  104. package/src/styles/components/_line.scss +66 -0
  105. package/src/styles/components/_loading.scss +36 -0
  106. package/src/styles/components/_notice-bar.scss +39 -0
  107. package/src/styles/components/_overlay.scss +17 -0
  108. package/src/styles/components/_popup.scss +118 -0
  109. package/src/styles/components/_schedule.scss +324 -0
  110. package/src/styles/components/_search.scss +35 -0
  111. package/src/styles/components/_segment.scss +40 -0
  112. package/src/styles/components/_steps.scss +97 -0
  113. package/src/styles/components/_swiper.scss +122 -0
  114. package/src/styles/components/_tabbar.scss +106 -0
  115. package/src/styles/components/_tag.scss +84 -0
  116. package/src/styles/components/_top-tabbar.scss +40 -0
  117. package/src/types/prop-types.ts +47 -0
  118. package/src/types/theme-types.ts +16 -0
  119. package/src/utils/date.ts +31 -0
  120. package/src/utils/util.ts +72 -0
@@ -0,0 +1,553 @@
1
+ <script setup lang="ts">
2
+ import { computed, onUnmounted, ref, shallowRef, watch } from 'vue'
3
+ import { ThemeColor } from '../../types/theme-types'
4
+ import { useInterval } from '../../hooks/useInterval'
5
+
6
+ interface Props {
7
+ theme?: ThemeColor | 'none'
8
+ disabled?: boolean
9
+ showBgColor?: boolean
10
+ list: any[]
11
+ modelValue?: number | null
12
+ width?: number | string
13
+ height?: number | string
14
+ direction?: 'horizontal' | 'vertical'
15
+ duration?: number
16
+ showArrow?: boolean
17
+ arrowColor?: 'light' | 'dark'
18
+ arrowSize?: number
19
+ showDots?: boolean
20
+ dotsSize?: number
21
+ maxDots?: number
22
+ dotsSide?: 'left' | 'right'
23
+ dotsOffset?: number | string
24
+ loop?: boolean
25
+ autoplay?: boolean
26
+ interval?: number
27
+ type?: 'default' | 'card'
28
+ activeCardScale?: number
29
+ inactiveCardScale?: number
30
+ cardGap?: number
31
+ borderRadius?: number | string
32
+ }
33
+
34
+ const props = withDefaults(defineProps<Props>(), {
35
+ theme: 'none',
36
+ disabled: false,
37
+ showBgColor: false,
38
+ list: () => [],
39
+ modelValue: null,
40
+ width: '100%',
41
+ height: '100%',
42
+ direction: 'horizontal',
43
+ duration: 500,
44
+ showArrow: false,
45
+ arrowColor: 'light',
46
+ arrowSize: 16,
47
+ showDots: false,
48
+ dotsSize: 32,
49
+ maxDots: 7,
50
+ dotsSide: 'right',
51
+ dotsOffset: '15%',
52
+ loop: true,
53
+ autoplay: false,
54
+ interval: 2000,
55
+ type: 'default',
56
+ activeCardScale: 0.8,
57
+ inactiveCardScale: 0.6,
58
+ cardGap: 25,
59
+ borderRadius: 0
60
+ })
61
+
62
+ const { pause, resume, clear } = useInterval(
63
+ () => {
64
+ if (props.autoplay && !isAnimating && props.list.length > 1) {
65
+ if (!props.loop && curIndex.value === visibleList.value.length - 1) {
66
+ curIndex.value = 0
67
+ return
68
+ }
69
+ handleNext()
70
+ }
71
+ },
72
+ props.interval,
73
+ { immediate: props.autoplay }
74
+ )
75
+
76
+ const emit = defineEmits<{
77
+ click: [e: Event, index: number]
78
+ change: [index: number]
79
+ 'update:modelValue': [modelValue: number]
80
+ }>()
81
+
82
+ function handleClick(e: Event) {
83
+ emit('click', e, realCurIndex.value)
84
+ }
85
+
86
+ // swiper
87
+ const curIndex = ref(props.loop && props.list.length > 1 ? 1 : 0)
88
+ const enableTransition = ref(true)
89
+ let isAnimating = false
90
+ let endAnimTimer: NodeJS.Timeout | null = null
91
+ const MIN_SWIPE_THRESHOLD = 50
92
+ // autoplay
93
+ let resumeTimer: NodeJS.Timeout | null = null
94
+ const touchState = shallowRef({
95
+ startX: 0,
96
+ startY: 0,
97
+ isSwiping: false,
98
+ isLock: false,
99
+ deltaX: 0,
100
+ deltaY: 0
101
+ })
102
+
103
+ const maxSwiperDots = computed(() => {
104
+ return Math.min(props.list.length, props.maxDots)
105
+ })
106
+ const isHorizontal = computed(() => {
107
+ return props.direction === 'horizontal'
108
+ })
109
+ const isArrowDark = computed(() => {
110
+ return props.arrowColor === 'dark'
111
+ })
112
+ const isCard = computed(() => {
113
+ return props.type === 'card'
114
+ })
115
+ const swiperClass = computed(() => {
116
+ return [
117
+ 'yt-swiper',
118
+ `yt-swiper--theme-${props.showBgColor ? props.theme : 'none'}`,
119
+ { 'yt-swiper--card': isCard.value }
120
+ ]
121
+ })
122
+ const swiperStyle = computed(() => ({
123
+ '--swiper-width': typeof props.width === 'number' ? `${props.width}px` : props.width,
124
+ '--swiper-height': typeof props.height === 'number' ? `${props.height}px` : props.height,
125
+ '--swiper-dots-offset':
126
+ typeof props.dotsOffset === 'number' ? `${props.dotsOffset}%` : props.dotsOffset,
127
+ '--swiper-border-radius':
128
+ typeof props.borderRadius === 'number' ? `${props.borderRadius}px` : props.borderRadius
129
+ }))
130
+ const swiperContainerClass = computed(() => {
131
+ return ['yt-swiper--container', `yt-swiper--container-${props.direction}`]
132
+ })
133
+ const swiperContainerStyle = computed(() => {
134
+ return {
135
+ '--swiper-translate-index': curIndex.value,
136
+ '--swiper-translate-duration': enableTransition.value ? `${props.duration * 0.001}s` : '0s'
137
+ }
138
+ })
139
+ const draggerTransform = ref('translate3d(0, 0, 0)')
140
+ function updateDraggerTransform() {
141
+ const { deltaX, deltaY } = touchState.value
142
+ if (!touchState.value.isSwiping || props.disabled) {
143
+ draggerTransform.value = 'translate3d(0, 0, 0)'
144
+ return
145
+ }
146
+ draggerTransform.value = isHorizontal.value
147
+ ? `translate3d(${deltaX}px, 0, 0)`
148
+ : `translate3d(0, ${deltaY}px, 0)`
149
+ }
150
+ const swiperDraggerStyle = computed(() => {
151
+ return {
152
+ transform: draggerTransform.value
153
+ }
154
+ })
155
+ const swiperDraggerClass = computed(() => ({
156
+ 'yt-swiper--dragger': true,
157
+ 'yt-swiper--dragger-swiping': touchState.value.isSwiping
158
+ }))
159
+ const arrowStyle = computed(() => {
160
+ return {
161
+ flexDirection: (isHorizontal.value ? 'row' : 'column') as 'row' | 'column'
162
+ }
163
+ })
164
+ const realCurIndex = computed(() => getRealIndex(curIndex.value))
165
+
166
+ /**
167
+ * swiper相关函数
168
+ */
169
+ function canAnimate() {
170
+ return props.list.length > 1 && !isAnimating
171
+ }
172
+ function endAnim() {
173
+ if (endAnimTimer) clearTimeout(endAnimTimer)
174
+ endAnimTimer = setTimeout(() => {
175
+ isAnimating = false
176
+ }, props.duration)
177
+ }
178
+ async function handleLoopJump(targetIndex: number, wait: boolean = true) {
179
+ const execute = async () => {
180
+ enableTransition.value = false
181
+ curIndex.value = targetIndex
182
+ // 等待3帧 更新位置以及确保完成
183
+ setTimeout(() => {
184
+ enableTransition.value = true
185
+ isAnimating = false
186
+ }, 50)
187
+ }
188
+ if (wait) {
189
+ setTimeout(async () => {
190
+ await execute()
191
+ }, props.duration)
192
+ } else {
193
+ await execute()
194
+ }
195
+ }
196
+ function handlePrev() {
197
+ if (!canAnimate()) return
198
+ if (!props.loop && curIndex.value === 0) return
199
+ if (props.disabled) return
200
+
201
+ isAnimating = true
202
+ curIndex.value--
203
+ emit('change', realCurIndex.value)
204
+ emit('update:modelValue', realCurIndex.value)
205
+ if (props.loop) {
206
+ if (curIndex.value === 0) {
207
+ handleLoopJump(visibleList.value.length - 2)
208
+ } else {
209
+ endAnim()
210
+ }
211
+ } else {
212
+ if (curIndex.value > 0) {
213
+ setTimeout(() => {
214
+ isAnimating = false
215
+ }, props.duration)
216
+ } else {
217
+ isAnimating = false
218
+ }
219
+ }
220
+ }
221
+ function handleNext() {
222
+ if (!canAnimate()) return
223
+ if (!props.loop && curIndex.value === props.list.length - 1) return
224
+ if (props.disabled) return
225
+
226
+ isAnimating = true
227
+ curIndex.value++
228
+ emit('change', realCurIndex.value)
229
+ emit('update:modelValue', realCurIndex.value)
230
+ if (props.loop) {
231
+ if (curIndex.value === visibleList.value.length - 1) {
232
+ handleLoopJump(1)
233
+ } else {
234
+ endAnim()
235
+ }
236
+ } else {
237
+ if (curIndex.value < props.list.length - 1) {
238
+ setTimeout(() => {
239
+ isAnimating = false
240
+ }, props.duration)
241
+ } else {
242
+ isAnimating = false
243
+ }
244
+ }
245
+ }
246
+ const visibleList = shallowRef<any[]>([])
247
+ watch(
248
+ () => [props.list, props.loop] as const,
249
+ ([list, loop]) => {
250
+ const oldLength = visibleList.value.length
251
+ if (!list.length) {
252
+ visibleList.value = []
253
+ } else if (loop && list.length > 1) {
254
+ visibleList.value = [list[list.length - 1], ...list, list[0]]
255
+ if (oldLength === 0) curIndex.value = 1 // 异步数据传入时重置index
256
+ } else {
257
+ visibleList.value = list // 直接引用
258
+ }
259
+ },
260
+ { immediate: true }
261
+ )
262
+ function getRealIndex(displayIndex: number) {
263
+ if (!props.loop || props.list.length <= 1) {
264
+ return displayIndex
265
+ }
266
+ if (displayIndex === 0) return props.list.length - 1
267
+ if (displayIndex === visibleList.value.length - 1) return 0
268
+ return displayIndex - 1
269
+ }
270
+ function getCardStyle(index: number) {
271
+ const isCurrentCard = isCurrentActive(index)
272
+ const isAdjacent = index === curIndex.value + 1 || index === curIndex.value - 1
273
+ const diff = index - curIndex.value
274
+ const rotateType = isHorizontal.value ? 'rotateY' : 'rotateX'
275
+ let translateValue = '0'
276
+ if (index === curIndex.value + 1) translateValue = `-${props.cardGap}%`
277
+ if (index === curIndex.value - 1) translateValue = `${props.cardGap}%`
278
+ // transform
279
+ const translate = isCurrentCard
280
+ ? 'translate(0, 0)'
281
+ : isHorizontal.value
282
+ ? `translateX(${translateValue})`
283
+ : `translateY(${translateValue})`
284
+ const rotateValue = isCurrentCard ? '0deg' : `${diff * (isHorizontal.value ? -8 : 8)}deg`
285
+ const translateZ = isCurrentCard ? '0px' : '-50px'
286
+ const scale = isCurrentCard
287
+ ? Math.min(props.activeCardScale, 1)
288
+ : Math.min(props.inactiveCardScale, 1)
289
+ const cardTransform = isCard.value
290
+ ? `${translate} translateZ(${translateZ}) ${rotateType}(${rotateValue}) scale(${scale})`
291
+ : 'none'
292
+ // shadow
293
+ const boxShadow =
294
+ isCard.value && (isCurrentCard || isAdjacent)
295
+ ? '0 2px 4px rgba(0, 0, 0, 0.2), 0 8px 16px rgba(0, 0, 0, 0.2)'
296
+ : 'none'
297
+ // border-radius
298
+ const borderRadius =
299
+ typeof props.borderRadius === 'number' ? `${props.borderRadius}px` : props.borderRadius
300
+
301
+ return {
302
+ '--swiper-container-item-transform': cardTransform,
303
+ '--swiper-container-item-z-index': isCurrentCard ? '1' : '0',
304
+ '--swiper-container-item-box-shadow': boxShadow,
305
+ '--swiper-container-item-border-radius': borderRadius
306
+ }
307
+ }
308
+ function isCurrentActive(index: number) {
309
+ return (
310
+ index === curIndex.value ||
311
+ (curIndex.value === visibleList.value.length - 1 && index === 1) ||
312
+ (curIndex.value === 0 && index === visibleList.value.length - 2)
313
+ )
314
+ }
315
+ const arrowVisible = computed(() => {
316
+ if (props.list.length <= 1) return { start: false, end: false }
317
+ if (props.loop) return { start: true, end: true }
318
+ const start = curIndex.value !== 0
319
+ const end = curIndex.value !== props.list.length - 1
320
+ return { start, end }
321
+ })
322
+ function shouldShow(displayIndex: number) {
323
+ const current = curIndex.value
324
+ const total = visibleList.value.length
325
+ // 当前页面
326
+ if (displayIndex === current) return true
327
+ // 相邻页面
328
+ if (Math.abs(displayIndex - current) === 1) return true
329
+ // 循环边界相邻
330
+ if (props.loop && props.list.length > 1) {
331
+ return (
332
+ (current === 0 && (displayIndex === total - 1 || displayIndex === total - 2)) ||
333
+ (current === total - 1 && (displayIndex === 0 || displayIndex === 1))
334
+ )
335
+ }
336
+ return false
337
+ }
338
+ /** */
339
+
340
+ /**
341
+ * autoplay,touch相关函数
342
+ */
343
+ function handleTouchStart(e: TouchEvent) {
344
+ if (props.list.length <= 1 || props.disabled) return
345
+ const touch = e.touches[0]
346
+ touchState.value = {
347
+ startX: touch.clientX,
348
+ startY: touch.clientY,
349
+ isSwiping: false,
350
+ isLock: false,
351
+ deltaX: 0,
352
+ deltaY: 0
353
+ }
354
+ pause()
355
+ }
356
+ function handleTouchMove(e: TouchEvent) {
357
+ if (props.list.length <= 1 || props.disabled) return
358
+ if (touchState.value.isLock) return
359
+ const touches = e.touches[0]
360
+ const deltaX = touches.clientX - touchState.value.startX
361
+ const deltaY = touches.clientY - touchState.value.startY
362
+ if (!touchState.value.isSwiping) {
363
+ const absDeltaX = Math.abs(deltaX)
364
+ const absDeltaY = Math.abs(deltaY)
365
+ if (absDeltaX < 5 && absDeltaY < 5) return
366
+ if (isHorizontal.value ? absDeltaY > absDeltaX : absDeltaX > absDeltaY) {
367
+ touchState.value = {
368
+ ...touchState.value,
369
+ isLock: true
370
+ }
371
+ return
372
+ }
373
+ }
374
+ touchState.value = {
375
+ ...touchState.value,
376
+ deltaX,
377
+ deltaY,
378
+ isSwiping: true
379
+ }
380
+ updateDraggerTransform()
381
+ }
382
+ function handleTouchEnd() {
383
+ draggerTransform.value = 'translate3d(0, 0, 0)'
384
+
385
+ // autoplay
386
+ if (resumeTimer) {
387
+ clearTimeout(resumeTimer)
388
+ resumeTimer = null
389
+ }
390
+ if (!touchState.value.isSwiping) {
391
+ if (props.autoplay) resume()
392
+ } else {
393
+ resumeTimer = setTimeout(() => {
394
+ if (props.autoplay) resume()
395
+ resumeTimer = null
396
+ }, 500)
397
+ }
398
+
399
+ const { deltaX, deltaY } = touchState.value
400
+ const isH = isHorizontal.value
401
+ if (touchState.value.isSwiping) {
402
+ if ((isH && deltaX < -MIN_SWIPE_THRESHOLD) || (!isH && deltaY < -MIN_SWIPE_THRESHOLD)) {
403
+ handleNext()
404
+ } else if ((isH && deltaX > MIN_SWIPE_THRESHOLD) || (!isH && deltaY > MIN_SWIPE_THRESHOLD)) {
405
+ handlePrev()
406
+ }
407
+ }
408
+ touchState.value = {
409
+ startX: 0,
410
+ startY: 0,
411
+ isSwiping: false,
412
+ isLock: false,
413
+ deltaX: 0,
414
+ deltaY: 0
415
+ }
416
+ }
417
+ /** */
418
+
419
+ /**
420
+ * dots相关函数
421
+ */
422
+ const dotsClass = computed(() => {
423
+ return [
424
+ 'yt-swiper--dots',
425
+ `yt-swiper--dots-${props.direction}`,
426
+ {
427
+ 'yt-swiper--dots-vertical-left': !isHorizontal.value && props.dotsSide === 'left',
428
+ 'yt-swiper--dots-vertical-right': !isHorizontal.value && props.dotsSide === 'right'
429
+ }
430
+ ]
431
+ })
432
+
433
+ watch(
434
+ () => props.modelValue,
435
+ (newIndex: number | null) => {
436
+ // 判断当前索引是否是emit更新又传进子组件来的,如果是则return,防止再次执行jump阻断动画播放
437
+ if (newIndex === null || newIndex === realCurIndex.value) return
438
+ let targetIndex = newIndex
439
+ if (props.loop) targetIndex++
440
+ const safeIndex = Math.min(visibleList.value.length - 1, Math.max(0, targetIndex))
441
+ handleLoopJump(safeIndex, false)
442
+ }
443
+ )
444
+
445
+ onUnmounted(() => {
446
+ if (resumeTimer) {
447
+ clearTimeout(resumeTimer)
448
+ resumeTimer = null
449
+ }
450
+ if (endAnimTimer) {
451
+ clearTimeout(endAnimTimer)
452
+ endAnimTimer = null
453
+ }
454
+ clear() // clear useInterval()
455
+ })
456
+
457
+ defineOptions({
458
+ name: 'YtSwiper'
459
+ })
460
+ </script>
461
+
462
+ <template>
463
+ <view
464
+ :class="swiperClass"
465
+ :style="swiperStyle"
466
+ @touchstart="handleTouchStart"
467
+ @touchmove="handleTouchMove"
468
+ @touchend="handleTouchEnd"
469
+ @touchcancel="handleTouchEnd"
470
+ >
471
+ <view
472
+ :class="swiperDraggerClass"
473
+ :style="swiperDraggerStyle"
474
+ >
475
+ <!-- Item Container -->
476
+ <view
477
+ :class="swiperContainerClass"
478
+ :style="swiperContainerStyle"
479
+ >
480
+ <view
481
+ class="yt-swiper--container-item"
482
+ v-for="(item, index) in visibleList"
483
+ :key="index"
484
+ @click="handleClick"
485
+ :style="getCardStyle(index)"
486
+ >
487
+ <view v-show="shouldShow(index)">
488
+ <slot
489
+ name="swiper-item"
490
+ :item="item"
491
+ :index="getRealIndex(index)"
492
+ />
493
+ </view>
494
+ </view>
495
+ </view>
496
+ </view>
497
+
498
+ <!-- Arrow -->
499
+ <view
500
+ v-if="showArrow"
501
+ class="yt-swiper--arrow"
502
+ :style="arrowStyle"
503
+ >
504
+ <yt-icon
505
+ v-if="isHorizontal && arrowVisible.start"
506
+ class="yt-swiper--arrow-left"
507
+ :name="isArrowDark ? 'ArrowLeft' : 'ArrowLeftWhite'"
508
+ :size="arrowSize"
509
+ @click="handlePrev"
510
+ />
511
+ <yt-icon
512
+ v-if="isHorizontal && arrowVisible.end"
513
+ class="yt-swiper--arrow-right"
514
+ :name="isArrowDark ? 'ArrowRight' : 'ArrowRightWhite'"
515
+ :size="arrowSize"
516
+ @click="handleNext"
517
+ />
518
+ <yt-icon
519
+ v-if="!isHorizontal && arrowVisible.start"
520
+ class="yt-swiper--arrow-up"
521
+ :name="isArrowDark ? 'ArrowUp' : 'ArrowUpWhite'"
522
+ :size="arrowSize"
523
+ @click="handlePrev"
524
+ />
525
+ <yt-icon
526
+ v-if="!isHorizontal && arrowVisible.end"
527
+ class="yt-swiper--arrow-down"
528
+ :name="isArrowDark ? 'ArrowDown' : 'ArrowDownWhite'"
529
+ :size="arrowSize"
530
+ @click="handleNext"
531
+ />
532
+ </view>
533
+
534
+ <!-- Dots -->
535
+ <view
536
+ v-if="showDots"
537
+ :class="dotsClass"
538
+ >
539
+ <yt-dots
540
+ :theme="theme"
541
+ :total="maxSwiperDots"
542
+ :size="props.dotsSize"
543
+ :activeIndex="realCurIndex"
544
+ :direction="props.direction"
545
+ />
546
+ </view>
547
+ </view>
548
+ </template>
549
+
550
+ <style lang="scss" scoped>
551
+ @use '../../styles/components/_swiper.scss';
552
+ @use '../../styles/_themes.scss';
553
+ </style>
@@ -0,0 +1,76 @@
1
+ <script setup lang="ts">
2
+ import { inject, onMounted, onUnmounted, ref, watch } from 'vue'
3
+
4
+ interface Props {
5
+ name?: string
6
+ modelValue?: boolean | null
7
+ checked?: boolean
8
+ disabled?: boolean
9
+ type?: 'switch' | 'checkbox'
10
+ }
11
+
12
+ const props = withDefaults(defineProps<Props>(), {
13
+ name: '',
14
+ modelValue: null,
15
+ checked: false,
16
+ disabled: false,
17
+ type: 'switch'
18
+ })
19
+
20
+ const currentValue = ref<any>(false)
21
+
22
+ const emit = defineEmits<{
23
+ 'update:modelValue': [modelValue: boolean]
24
+ change: [value: boolean]
25
+ }>()
26
+
27
+ function handleChange(e: any) {
28
+ currentValue.value = e.detail.value
29
+ emit('update:modelValue', e.detail.value)
30
+ emit('change', e.detail.value)
31
+ }
32
+
33
+ watch(
34
+ () => [props.modelValue, props.checked],
35
+ ([newModel, newChecked]) => {
36
+ const targetValue = newModel !== null ? !!newModel : !!newChecked
37
+ if (currentValue.value !== targetValue) {
38
+ currentValue.value = targetValue
39
+ }
40
+ },
41
+ { immediate: true }
42
+ )
43
+
44
+ defineOptions({
45
+ name: 'YtSwitch'
46
+ })
47
+
48
+ const registerField: any = inject('registerField', () => {})
49
+ const unregisterField: any = inject('unregisterField', () => {})
50
+ const getValue = () => {
51
+ return currentValue.value
52
+ }
53
+ const setValue = (value: any) => {
54
+ currentValue.value = !!value
55
+ emit('update:modelValue', !!value)
56
+ }
57
+ onMounted(() => {
58
+ if (registerField && props.name) {
59
+ registerField(props.name, getValue, setValue)
60
+ }
61
+ })
62
+ onUnmounted(() => {
63
+ if (unregisterField && props.name) {
64
+ unregisterField(props.name)
65
+ }
66
+ })
67
+ </script>
68
+
69
+ <template>
70
+ <switch
71
+ :checked="currentValue"
72
+ @change="handleChange"
73
+ :type="type"
74
+ :disabled="disabled"
75
+ />
76
+ </template>