@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
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@rao2126340634/yt-ui",
3
+ "version": "1.2.20",
4
+ "description": "uniapp vue3 component library for zjyt wechat miniprogram",
5
+ "main": "src/components",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "files": [
10
+ "src",
11
+ "../../README.md"
12
+ ],
13
+ "keywords": [
14
+ "uni-app",
15
+ "vue3",
16
+ "typescript",
17
+ "ui",
18
+ "component",
19
+ "wechat-miniprogram",
20
+ "mobile",
21
+ "lightweight",
22
+ "easycom",
23
+ "uniapp-ui",
24
+ "mini-program",
25
+ "dcloud",
26
+ "vue-component"
27
+ ],
28
+ "author": "",
29
+ "license": "MIT",
30
+ "packageManager": "pnpm@10.23.0",
31
+ "devDependencies": {
32
+ "@dcloudio/types": "^3.4.28",
33
+ "@types/node": "^24.10.2",
34
+ "@vue/tsconfig": "^0.8.1",
35
+ "typescript": "^5.9.3",
36
+ "vue-tsc": "^3.1.5"
37
+ },
38
+ "peerDependencies": {
39
+ "@dcloudio/uni-app": "2.0.2-4080420251103001",
40
+ "sass": "^1.95.0",
41
+ "vue": "^3.5.25"
42
+ }
43
+ }
@@ -0,0 +1,108 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { ImageMode } from '../../types/prop-types'
4
+ import { ThemeColor } from '../../types/theme-types'
5
+
6
+ interface Props {
7
+ theme?: ThemeColor | 'none'
8
+ url?: string
9
+ circle?: boolean
10
+ size?: number
11
+ fit?: ImageMode
12
+ // badge
13
+ enableBadgeTheme: boolean
14
+ showBadge?: boolean
15
+ count?: number
16
+ overflowCount?: number
17
+ dot?: boolean
18
+ offset?: number[]
19
+ }
20
+
21
+ const props = withDefaults(defineProps<Props>(), {
22
+ theme: 'none',
23
+ url: '',
24
+ circle: true,
25
+ size: 60,
26
+ fit: 'aspectFill',
27
+ // badge
28
+ enableBadgeTheme: true,
29
+ showBadge: false,
30
+ count: 0,
31
+ overflowCount: 99,
32
+ dot: false,
33
+ offset: () => []
34
+ })
35
+
36
+ const emit = defineEmits<{
37
+ click: [e: Event]
38
+ load: [{ height: string; width: string }]
39
+ error: [errMsg: string]
40
+ }>()
41
+ const avatarClass = computed(() => {
42
+ return ['yt-avatar', `yt-avatar--theme-${props.theme}`]
43
+ })
44
+ const avatarStyle = computed(() => {
45
+ return {
46
+ borderRadius: props.circle ? '50%' : '0',
47
+ width: `${props.size}px`,
48
+ height: `${props.size}px`
49
+ }
50
+ })
51
+ function handleLoad(e: any) {
52
+ emit('load', e.detail)
53
+ }
54
+ function handleError(e: any) {
55
+ emit('error', e.detail.errMsg)
56
+ }
57
+ function handleClick(e: Event) {
58
+ emit('click', e)
59
+ }
60
+
61
+ defineOptions({
62
+ name: 'YtAvatar'
63
+ })
64
+ </script>
65
+
66
+ <template>
67
+ <view class="yt-avatar--container">
68
+ <view
69
+ :class="avatarClass"
70
+ :style="avatarStyle"
71
+ @click="handleClick"
72
+ >
73
+ <image
74
+ v-if="url"
75
+ class="yt-avatar--image"
76
+ :src="url"
77
+ :mode="fit"
78
+ @load="handleLoad"
79
+ @error="handleError"
80
+ />
81
+ <yt-icon
82
+ v-else
83
+ name="DefaultAvatar"
84
+ :width="props.size"
85
+ :height="props.size"
86
+ />
87
+ </view>
88
+
89
+ <!-- badge -->
90
+ <view
91
+ v-if="showBadge"
92
+ class="yt-avatar--badge"
93
+ >
94
+ <yt-badge
95
+ :theme="enableBadgeTheme ? theme : 'none'"
96
+ :count="count"
97
+ :overflowCount="overflowCount"
98
+ :dot="dot"
99
+ :offset="offset"
100
+ />
101
+ </view>
102
+ </view>
103
+ </template>
104
+
105
+ <style lang="scss" scoped>
106
+ @use '../../styles/components/avatar';
107
+ @use '../../styles/themes';
108
+ </style>
@@ -0,0 +1,65 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { ThemeColor } from '../../types/theme-types'
4
+
5
+ interface Props {
6
+ theme?: ThemeColor
7
+ count?: number
8
+ overflowCount?: number
9
+ dot?: boolean
10
+ offset?: number[]
11
+ zIndex?: number
12
+ }
13
+
14
+ const props = withDefaults(defineProps<Props>(), {
15
+ theme: 'classic',
16
+ count: 0,
17
+ overflowCount: 99,
18
+ dot: false,
19
+ offset: () => [],
20
+ zIndex: 100
21
+ })
22
+
23
+ const badgeClass = computed(() => {
24
+ return [
25
+ 'yt-badge',
26
+ `yt-badge--theme-${props.theme}`,
27
+ {
28
+ 'yt-badge--dot': props.dot,
29
+ 'yt-badge--count': !props.dot,
30
+ 'yt-badge--visible': (!props.dot && props.count) || props.dot
31
+ }
32
+ ]
33
+ })
34
+
35
+ const badgeStyle = computed(() => {
36
+ return {
37
+ zIndex: props.zIndex,
38
+ left: `${props.offset[0]}px` || 0,
39
+ top: `${props.offset[1]}px` || 0
40
+ }
41
+ })
42
+
43
+ const visibleCount = computed(() => {
44
+ return props.count > props.overflowCount ? `${props.overflowCount}+` : props.count
45
+ })
46
+ </script>
47
+
48
+ <template>
49
+ <view
50
+ :class="badgeClass"
51
+ :style="badgeStyle"
52
+ >
53
+ <span
54
+ class="yt-badge--count-text"
55
+ v-if="!props.dot"
56
+ >
57
+ {{ visibleCount }}
58
+ </span>
59
+ </view>
60
+ </template>
61
+
62
+ <style lang="scss" scoped>
63
+ @use '../../styles/components/badge';
64
+ @use '../../styles/themes';
65
+ </style>
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { ThemeColor } from '../../types/theme-types'
4
+ import { ColorType, SizeType } from '../../types/prop-types'
5
+
6
+ interface Props {
7
+ theme?: ThemeColor
8
+ type?: ColorType
9
+ size?: SizeType
10
+ plain?: boolean
11
+ disabled?: boolean
12
+ loading?: boolean
13
+ circle?: boolean
14
+ stretch?: boolean
15
+ }
16
+
17
+ const props = withDefaults(defineProps<Props>(), {
18
+ theme: 'classic',
19
+ type: 'primary',
20
+ size: 'medium',
21
+ plain: false,
22
+ disabled: false,
23
+ loading: false,
24
+ circle: false,
25
+ stretch: false
26
+ })
27
+
28
+ const emit = defineEmits<{
29
+ click: [e: Event]
30
+ }>()
31
+
32
+ const buttonClass = computed(() => {
33
+ return [
34
+ 'yt-button',
35
+ `yt-button--${props.type}${props.theme && props.type === 'primary' ? `-${props.theme}` : ''}`,
36
+ `yt-button--${props.size}`,
37
+ {
38
+ 'yt-button--plain': props.plain,
39
+ 'yt-button--disabled': props.disabled || props.loading,
40
+ 'yt-button--loading': props.loading,
41
+ 'yt-button--circle': props.circle,
42
+ 'yt-button--stretch': props.stretch
43
+ }
44
+ ]
45
+ })
46
+
47
+ function handleClick(e: Event) {
48
+ if (!props.disabled && !props.loading) {
49
+ emit('click', e)
50
+ }
51
+ }
52
+
53
+ defineOptions({
54
+ name: 'YtButton'
55
+ })
56
+ </script>
57
+
58
+ <template>
59
+ <button
60
+ :class="buttonClass"
61
+ :loading="loading"
62
+ hover-class="yt-button--hover"
63
+ @click="handleClick"
64
+ >
65
+ <span
66
+ v-if="loading"
67
+ class="yt-button--loading"
68
+ ></span>
69
+ <span class="yt-button--text">
70
+ <slot></slot>
71
+ </span>
72
+ <view class="yt-button--overlay" />
73
+ </button>
74
+ </template>
75
+
76
+ <style lang="scss" scoped>
77
+ @use '../../styles/components/_button.scss';
78
+ @use '../../styles/_themes.scss';
79
+ </style>
@@ -0,0 +1,327 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref, shallowRef, toRaw } from 'vue'
3
+ import { ThemeColor } from '../../types/theme-types'
4
+ import { Calendar, useCalendar } from '../../hooks/useCalendar'
5
+ import { throttle } from '../../utils/util'
6
+
7
+ const monthNameMap: Record<number, string> = {
8
+ 1: 'January',
9
+ 2: 'February',
10
+ 3: 'March',
11
+ 4: 'April',
12
+ 5: 'May',
13
+ 6: 'June',
14
+ 7: 'July',
15
+ 8: 'August',
16
+ 9: 'September',
17
+ 10: 'October',
18
+ 11: 'November',
19
+ 12: 'December'
20
+ }
21
+
22
+ type MonthData = {
23
+ date: number
24
+ extra: string
25
+ isHoliday: boolean
26
+ }[]
27
+
28
+ interface Props {
29
+ monthData?: MonthData
30
+ loading?: boolean
31
+ theme?: ThemeColor | 'none'
32
+ width?: number | string
33
+ height?: number | string
34
+ gap?: number | string
35
+ borderRadius?: number | string
36
+ start?: string
37
+ end?: string
38
+ }
39
+
40
+ const props = withDefaults(defineProps<Props>(), {
41
+ monthData: () => [],
42
+ loading: false,
43
+ theme: 'none',
44
+ width: '100%',
45
+ height: '100%',
46
+ gap: 6,
47
+ borderRadius: 10,
48
+ start: '',
49
+ end: ''
50
+ })
51
+
52
+ const emit = defineEmits<{
53
+ change: [{ year: number; month: number }]
54
+ dateClick: [{ year: number; month: number; date: number; index: number }]
55
+ }>()
56
+
57
+ const calendarClass = computed(() => {
58
+ return ['yt-calendar', `yt-calendar--theme-${props.theme}`]
59
+ })
60
+ const calendarStyle = computed(() => {
61
+ return {
62
+ width: typeof props.width === 'number' ? `${props.width}px` : props.width,
63
+ height: typeof props.height === 'number' ? `${props.height}px` : props.height,
64
+ '--calendar-item-gap': typeof props.gap === 'number' ? `${props.gap}px` : props.gap,
65
+ borderRadius:
66
+ typeof props.borderRadius === 'number' ? `${props.borderRadius}px` : props.borderRadius
67
+ }
68
+ })
69
+ const calendarOpacity = computed(() => {
70
+ if (!touchState.value.isDragging) return 1
71
+ const value = 1 - Math.abs(touchState.value.curX - touchState.value.startX) * 0.012
72
+ return Math.min(Math.max(0.1, value), 1)
73
+ })
74
+ const calendarGridClass = computed(() => {
75
+ return (item: Calendar, index: number) => {
76
+ const active =
77
+ calendar.isToday(item.year, item.month, item.date) || index === selectedDate.value?.index
78
+ const secondary =
79
+ active && selectedDate.value !== null && item.date !== selectedDate.value.item.date
80
+ return {
81
+ 'yt-calendar--body-grid-item': true,
82
+ 'yt-calendar--body-grid-item-today': active,
83
+ 'yt-calendar--body-grid-item-secondary': secondary,
84
+ 'yt-calendar--body-grid-item-prev-month': item.isPrevMonth,
85
+ 'yt-calendar--body-grid-item-next-month': item.isNextMonth
86
+ }
87
+ }
88
+ })
89
+ const arrowScale = computed(() => {
90
+ if (!touchState.value.isDragging) return { leftScale: 1, rightScale: 1 }
91
+ const deltaX = touchState.value.curX - touchState.value.startX
92
+ const rightScale =
93
+ deltaX < 0 ? 1 + Math.abs(touchState.value.curX - touchState.value.startX) * 0.012 : 1
94
+ const leftScale =
95
+ deltaX > 0 ? 1 + Math.abs(touchState.value.curX - touchState.value.startX) * 0.012 : 1
96
+ return {
97
+ leftScale: Math.min(Math.max(1, leftScale), 1.5),
98
+ rightScale: Math.min(Math.max(1, rightScale), 1.5)
99
+ }
100
+ })
101
+
102
+ const calendar = useCalendar()
103
+ const date = ref<Date>(new Date())
104
+ const state = ref({
105
+ year: date.value.getFullYear(),
106
+ month: date.value.getMonth() + 1
107
+ })
108
+ const touchState = shallowRef({
109
+ curX: 0,
110
+ curY: 0,
111
+ startX: 0,
112
+ startY: 0,
113
+ isDragging: false,
114
+ isLock: false
115
+ })
116
+ const selectedDate = ref<{ item: Calendar; index: number } | null>(null)
117
+
118
+ const calendarData = computed(() => {
119
+ return calendar.getCalendar(state.value.year, state.value.month)
120
+ })
121
+ const backgroundText = computed(() => {
122
+ return `${monthNameMap[state.value.month].slice(0, 3)}.${state.value.month}`
123
+ })
124
+ function getExtra(item: Calendar) {
125
+ const target = props.monthData.find(md => md.date === item.date && item.isCurMonth)
126
+ return target?.extra || ''
127
+ }
128
+ function isHoliday(item: Calendar) {
129
+ const target = props.monthData.find(md => md.date === item.date && item.isCurMonth)
130
+ return target?.isHoliday
131
+ }
132
+ function changeMonth(type: 'prev' | 'next') {
133
+ let newMonth = state.value.month
134
+ let newYear = state.value.year
135
+ newMonth += type === 'prev' ? -1 : 1
136
+ if (newMonth === 0) {
137
+ newMonth = 12
138
+ newYear--
139
+ } else if (newMonth === 13) {
140
+ newMonth = 1
141
+ newYear++
142
+ }
143
+ if (props.start) {
144
+ const startArr = props.start.split('-')
145
+ if (newYear === Number(startArr[0])) newMonth = Math.max(newMonth, Number(startArr[1]))
146
+ newYear = Math.max(newYear, Number(startArr[0]))
147
+ }
148
+ if (props.end) {
149
+ const endArr = props.end.split('-')
150
+ if (newYear === Number(endArr[0])) newMonth = Math.min(newMonth, Number(endArr[1]))
151
+ newYear = Math.min(newYear, Number(endArr[0]))
152
+ }
153
+ if (newYear === state.value.year && newMonth === state.value.month) return
154
+ state.value = {
155
+ year: newYear,
156
+ month: newMonth
157
+ }
158
+ selectedDate.value = null
159
+ emit('change', toRaw(state.value))
160
+ }
161
+ function handleDatePickerChange(str: any) {
162
+ selectedDate.value = null
163
+ const strArr = str.split('-')
164
+ state.value.year = Number(strArr[0])
165
+ state.value.month = Number(strArr[1])
166
+ emit('change', toRaw(state.value))
167
+ }
168
+ function handleTouchStart(e: any) {
169
+ if (props.loading) return
170
+ const touches = e.touches[0]
171
+ touchState.value = {
172
+ ...touchState.value,
173
+ startX: touches.clientX,
174
+ startY: touches.clientY,
175
+ isDragging: false,
176
+ isLock: false,
177
+ curX: 0,
178
+ curY: 0
179
+ }
180
+ }
181
+ function handleTouchMove(e: TouchEvent) {
182
+ if (props.loading || touchState.value.isLock) return
183
+ const touches = e.touches[0]
184
+ if (!touchState.value.isDragging) {
185
+ const absDeltaX = Math.abs(touchState.value.curX - touchState.value.startX)
186
+ const absDeltaY = Math.abs(touchState.value.curY - touchState.value.startY)
187
+ if (absDeltaX < 5 || absDeltaY > absDeltaX) {
188
+ touchState.value = {
189
+ ...touchState.value,
190
+ isLock: true
191
+ }
192
+ return
193
+ }
194
+ }
195
+ touchState.value = {
196
+ ...touchState.value,
197
+ curX: touches.clientX,
198
+ curY: touches.clientY,
199
+ isDragging: true
200
+ }
201
+ }
202
+ function handleTouchEnd() {
203
+ const deltaX = touchState.value.curX - touchState.value.startX
204
+ const deltaY = touchState.value.curY - touchState.value.startY
205
+ if (touchState.value.isDragging && Math.abs(deltaX) > 80 && Math.abs(deltaY) < 50) {
206
+ changeMonth(deltaX > 0 ? 'prev' : 'next')
207
+ }
208
+ touchState.value = {
209
+ curX: 0,
210
+ curY: 0,
211
+ startX: 0,
212
+ startY: 0,
213
+ isDragging: false,
214
+ isLock: false
215
+ }
216
+ }
217
+ function handleDateClick(item: Calendar, index: number) {
218
+ if (selectedDate.value !== null && selectedDate.value.index === index) return
219
+ selectedDate.value = { item, index }
220
+ emit('dateClick', { year: item.year, month: item.month, date: item.date, index })
221
+ }
222
+
223
+ defineOptions({
224
+ name: 'YtCalendar'
225
+ })
226
+ </script>
227
+
228
+ <template>
229
+ <view
230
+ :class="calendarClass"
231
+ :style="calendarStyle"
232
+ >
233
+ <view class="yt-calendar--header">
234
+ <view class="yt-calendar--header-month-text">
235
+ <span class="yt-calendar--header-month-text-number">{{ state.month }}</span>
236
+ <span class="yt-calendar--header-month-text-word">{{ monthNameMap[state.month] }}</span>
237
+ </view>
238
+ <view class="yt-calendar--header-year-month">
239
+ <span
240
+ @click="changeMonth('prev')"
241
+ class="arrow-left"
242
+ >
243
+ {{ '<' }}
244
+ </span>
245
+ <!-- date-picker -->
246
+ <yt-picker
247
+ type="date"
248
+ fields="month"
249
+ @change="handleDatePickerChange"
250
+ :start="start"
251
+ :end="end"
252
+ >
253
+ <span>{{ `${state.year}年${state.month}月` }}</span>
254
+ </yt-picker>
255
+ <span
256
+ @click="changeMonth('next')"
257
+ class="arrow-right"
258
+ >
259
+ {{ '>' }}
260
+ </span>
261
+ </view>
262
+ </view>
263
+ <view
264
+ class="yt-calendar--body"
265
+ @touchstart="handleTouchStart"
266
+ @touchmove="handleTouchMove"
267
+ @touchend="handleTouchEnd"
268
+ @touchcancel="handleTouchEnd"
269
+ >
270
+ <view class="yt-calendar--body-week">
271
+ <span
272
+ v-for="(day, index) in calendar.weekDays"
273
+ :key="index"
274
+ >
275
+ {{ day }}
276
+ </span>
277
+ </view>
278
+ <view
279
+ :class="['yt-calendar--body-grid', { 'yt-calendar--body-grid-loading': props.loading }]"
280
+ >
281
+ <span
282
+ v-for="(item, index) in calendarData"
283
+ :key="index"
284
+ :class="calendarGridClass(item, index)"
285
+ @click="handleDateClick(item, index)"
286
+ >
287
+ <p class="yt-calendar--body-grid-item-date">{{ item.date }}</p>
288
+ <p class="yt-calendar--body-grid-item-extra">
289
+ {{ getExtra(item) }}
290
+ </p>
291
+ <p
292
+ class="yt-calendar--body-grid-item-holiday"
293
+ v-if="isHoliday(item)"
294
+ >
295
+
296
+ </p>
297
+ </span>
298
+ <!-- background-text -->
299
+ <span class="yt-calendar--body-grid-background-text">{{ backgroundText }}</span>
300
+ </view>
301
+ <!-- loading -->
302
+ <yt-loading
303
+ class="yt-calendar--body-loading"
304
+ :theme="theme"
305
+ :visible="loading"
306
+ />
307
+ </view>
308
+ </view>
309
+ </template>
310
+
311
+ <style lang="scss" scoped>
312
+ @use '../../styles/components/_calendar.scss';
313
+ @use '../../styles/_themes.scss';
314
+ @use '../../styles/var' as var;
315
+
316
+ .yt-calendar--body {
317
+ opacity: v-bind(calendarOpacity);
318
+ }
319
+
320
+ .arrow-left {
321
+ transform: scale(v-bind('arrowScale.leftScale'));
322
+ }
323
+
324
+ .arrow-right {
325
+ transform: scale(v-bind('arrowScale.rightScale'));
326
+ }
327
+ </style>