uview-pro 0.3.0 → 0.3.1

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 (249) hide show
  1. package/changelog.md +584 -574
  2. package/components/common/props.ts +22 -22
  3. package/components/u-action-sheet/types.ts +37 -37
  4. package/components/u-action-sheet/u-action-sheet.vue +178 -178
  5. package/components/u-alert-tips/types.ts +41 -41
  6. package/components/u-alert-tips/u-alert-tips.vue +238 -238
  7. package/components/u-avatar/types.ts +36 -36
  8. package/components/u-avatar/u-avatar.vue +217 -217
  9. package/components/u-avatar-cropper/types.ts +23 -23
  10. package/components/u-avatar-cropper/u-avatar-cropper.vue +297 -297
  11. package/components/u-avatar-cropper/weCropper.d.ts +62 -62
  12. package/components/u-avatar-cropper/weCropper.js +1281 -1281
  13. package/components/u-avatar-cropper/weCropper.ts +1276 -1276
  14. package/components/u-back-top/types.ts +36 -36
  15. package/components/u-back-top/u-back-top.vue +140 -140
  16. package/components/u-badge/types.ts +38 -38
  17. package/components/u-badge/u-badge.vue +183 -183
  18. package/components/u-button/types.ts +66 -66
  19. package/components/u-button/u-button.vue +579 -579
  20. package/components/u-calendar/types.ts +75 -75
  21. package/components/u-calendar/u-calendar.vue +793 -793
  22. package/components/u-car-keyboard/types.ts +14 -14
  23. package/components/u-car-keyboard/u-car-keyboard.vue +262 -262
  24. package/components/u-card/types.ts +61 -61
  25. package/components/u-card/u-card.vue +209 -209
  26. package/components/u-cell-group/types.ts +19 -19
  27. package/components/u-cell-group/u-cell-group.vue +60 -60
  28. package/components/u-cell-item/types.ts +56 -56
  29. package/components/u-cell-item/u-cell-item.vue +226 -226
  30. package/components/u-checkbox/types.ts +33 -33
  31. package/components/u-checkbox/u-checkbox.vue +282 -282
  32. package/components/u-checkbox-group/types.ts +34 -34
  33. package/components/u-checkbox-group/u-checkbox-group.vue +130 -130
  34. package/components/u-circle-progress/types.ts +54 -54
  35. package/components/u-circle-progress/u-circle-progress.vue +206 -206
  36. package/components/u-city-select/types.ts +22 -22
  37. package/components/u-city-select/u-city-select.vue +276 -276
  38. package/components/u-col/types.ts +32 -32
  39. package/components/u-col/u-col.vue +142 -142
  40. package/components/u-collapse/types.ts +33 -33
  41. package/components/u-collapse/u-collapse.vue +190 -190
  42. package/components/u-collapse-item/types.ts +27 -27
  43. package/components/u-collapse-item/u-collapse-item.vue +290 -290
  44. package/components/u-column-notice/types.ts +50 -50
  45. package/components/u-column-notice/u-column-notice.vue +222 -222
  46. package/components/u-count-down/types.ts +44 -44
  47. package/components/u-count-down/u-count-down.vue +286 -286
  48. package/components/u-count-to/types.ts +34 -34
  49. package/components/u-count-to/u-count-to.vue +266 -266
  50. package/components/u-divider/types.ts +33 -33
  51. package/components/u-divider/u-divider.vue +145 -145
  52. package/components/u-dropdown/types.ts +34 -34
  53. package/components/u-dropdown/u-dropdown.vue +330 -330
  54. package/components/u-dropdown-item/types.ts +29 -29
  55. package/components/u-dropdown-item/u-dropdown-item.vue +120 -120
  56. package/components/u-empty/types.ts +38 -38
  57. package/components/u-empty/u-empty.vue +103 -103
  58. package/components/u-field/types.ts +71 -71
  59. package/components/u-field/u-field.vue +388 -388
  60. package/components/u-form/types.ts +29 -29
  61. package/components/u-form/u-form.vue +130 -130
  62. package/components/u-form-item/types.ts +72 -72
  63. package/components/u-form-item/u-form-item.vue +447 -447
  64. package/components/u-full-screen/types.ts +16 -16
  65. package/components/u-full-screen/u-full-screen.vue +103 -103
  66. package/components/u-gap/types.ts +20 -20
  67. package/components/u-gap/u-gap.vue +50 -50
  68. package/components/u-grid/types.ts +21 -21
  69. package/components/u-grid/u-grid.vue +91 -91
  70. package/components/u-grid-item/types.ts +16 -16
  71. package/components/u-grid-item/u-grid-item.vue +130 -130
  72. package/components/u-icon/types.ts +61 -61
  73. package/components/u-icon/u-icon.vue +296 -296
  74. package/components/u-image/types.ts +51 -51
  75. package/components/u-image/u-image.vue +239 -239
  76. package/components/u-index-anchor/types.ts +16 -16
  77. package/components/u-index-anchor/u-index-anchor.vue +94 -94
  78. package/components/u-index-list/types.ts +43 -43
  79. package/components/u-index-list/u-index-list.vue +352 -352
  80. package/components/u-input/types.ts +137 -137
  81. package/components/u-input/u-input.vue +288 -288
  82. package/components/u-keyboard/types.ts +40 -40
  83. package/components/u-keyboard/u-keyboard.vue +178 -178
  84. package/components/u-lazy-load/types.ts +37 -37
  85. package/components/u-lazy-load/u-lazy-load.vue +246 -246
  86. package/components/u-line/types.ts +44 -44
  87. package/components/u-line/u-line.vue +68 -68
  88. package/components/u-line-progress/types.ts +58 -58
  89. package/components/u-line-progress/u-line-progress.vue +126 -126
  90. package/components/u-link/types.ts +43 -43
  91. package/components/u-link/u-link.vue +84 -84
  92. package/components/u-loading/types.ts +35 -35
  93. package/components/u-loading/u-loading.vue +105 -105
  94. package/components/u-loading-popup/types.ts +26 -26
  95. package/components/u-loading-popup/u-loading-popup.vue +253 -253
  96. package/components/u-loadmore/types.ts +79 -79
  97. package/components/u-loadmore/u-loadmore.vue +156 -156
  98. package/components/u-mask/types.ts +40 -40
  99. package/components/u-mask/u-mask.vue +113 -113
  100. package/components/u-message-input/types.ts +74 -74
  101. package/components/u-message-input/u-message-input.vue +281 -281
  102. package/components/u-modal/types.ts +118 -118
  103. package/components/u-modal/u-modal.vue +220 -220
  104. package/components/u-navbar/types.ts +103 -103
  105. package/components/u-navbar/u-navbar.vue +251 -251
  106. package/components/u-no-network/image.ts +2 -2
  107. package/components/u-no-network/types.ts +28 -28
  108. package/components/u-no-network/u-no-network.vue +303 -303
  109. package/components/u-notice-bar/types.ts +111 -111
  110. package/components/u-notice-bar/u-notice-bar.vue +189 -189
  111. package/components/u-number-box/types.ts +42 -42
  112. package/components/u-number-box/u-number-box.vue +321 -321
  113. package/components/u-number-keyboard/types.ts +26 -26
  114. package/components/u-number-keyboard/u-number-keyboard.vue +188 -188
  115. package/components/u-picker/types.ts +123 -123
  116. package/components/u-picker/u-picker.vue +685 -685
  117. package/components/u-popup/types.ts +59 -59
  118. package/components/u-popup/u-popup.vue +385 -385
  119. package/components/u-radio/types.ts +27 -27
  120. package/components/u-radio/u-radio.vue +279 -279
  121. package/components/u-radio-group/types.ts +31 -31
  122. package/components/u-radio-group/u-radio-group.vue +96 -96
  123. package/components/u-rate/types.ts +42 -42
  124. package/components/u-rate/u-rate.vue +249 -249
  125. package/components/u-read-more/types.ts +37 -37
  126. package/components/u-read-more/u-read-more.vue +172 -172
  127. package/components/u-root-portal/u-root-portal.vue +56 -56
  128. package/components/u-row/types.ts +22 -22
  129. package/components/u-row/u-row.vue +105 -105
  130. package/components/u-row-notice/types.ts +41 -41
  131. package/components/u-row-notice/u-row-notice.vue +256 -256
  132. package/components/u-safe-bottom/u-safe-bottom.vue +57 -57
  133. package/components/u-search/types.ts +55 -55
  134. package/components/u-search/u-search.vue +279 -279
  135. package/components/u-section/types.ts +34 -34
  136. package/components/u-section/u-section.vue +150 -150
  137. package/components/u-select/types.ts +45 -45
  138. package/components/u-select/u-select.vue +388 -388
  139. package/components/u-skeleton/types.ts +22 -22
  140. package/components/u-skeleton/u-skeleton.vue +231 -231
  141. package/components/u-slider/types.ts +34 -34
  142. package/components/u-slider/u-slider.vue +255 -255
  143. package/components/u-status-bar/u-status-bar.vue +74 -74
  144. package/components/u-steps/types.ts +30 -30
  145. package/components/u-steps/u-steps.vue +181 -181
  146. package/components/u-sticky/types.ts +24 -24
  147. package/components/u-sticky/u-sticky.vue +178 -178
  148. package/components/u-subsection/types.ts +38 -38
  149. package/components/u-subsection/u-subsection.vue +339 -339
  150. package/components/u-swipe-action/types.ts +52 -52
  151. package/components/u-swipe-action/u-swipe-action.vue +276 -276
  152. package/components/u-swiper/types.ts +49 -49
  153. package/components/u-swiper/u-swiper.vue +308 -308
  154. package/components/u-switch/types.ts +30 -30
  155. package/components/u-switch/u-switch.vue +150 -150
  156. package/components/u-tabbar/types.ts +38 -38
  157. package/components/u-tabbar/u-tabbar.vue +315 -315
  158. package/components/u-table/types.ts +27 -27
  159. package/components/u-table/u-table.vue +67 -67
  160. package/components/u-tabs/types.ts +53 -53
  161. package/components/u-tabs/u-tabs.vue +302 -302
  162. package/components/u-tabs-swiper/types.ts +55 -55
  163. package/components/u-tabs-swiper/u-tabs-swiper.vue +409 -409
  164. package/components/u-tag/types.ts +39 -39
  165. package/components/u-tag/u-tag.vue +268 -268
  166. package/components/u-td/types.ts +14 -14
  167. package/components/u-td/u-td.vue +98 -98
  168. package/components/u-text/types.ts +72 -72
  169. package/components/u-text/u-text.vue +343 -343
  170. package/components/u-th/types.ts +14 -14
  171. package/components/u-th/u-th.vue +92 -92
  172. package/components/u-time-line/u-time-line.vue +53 -53
  173. package/components/u-time-line-item/types.ts +16 -16
  174. package/components/u-time-line-item/u-time-line-item.vue +90 -90
  175. package/components/u-toast/types.ts +38 -38
  176. package/components/u-toast/u-toast.vue +240 -240
  177. package/components/u-top-tips/types.ts +16 -16
  178. package/components/u-top-tips/u-top-tips.vue +130 -130
  179. package/components/u-tr/types.ts +11 -11
  180. package/components/u-tr/u-tr.vue +39 -39
  181. package/components/u-upload/types.ts +82 -82
  182. package/components/u-upload/u-upload.vue +568 -568
  183. package/components/u-verification-code/types.ts +24 -24
  184. package/components/u-verification-code/u-verification-code.vue +176 -176
  185. package/components/u-waterfall/types.ts +18 -18
  186. package/components/u-waterfall/u-waterfall.vue +187 -187
  187. package/iconfont.css +913 -913
  188. package/index.scss +25 -25
  189. package/index.ts +38 -38
  190. package/libs/config/config.ts +26 -26
  191. package/libs/config/zIndex.ts +37 -37
  192. package/libs/css/color.scss +155 -155
  193. package/libs/css/common.scss +178 -178
  194. package/libs/css/style.components.scss +16 -16
  195. package/libs/css/style.h5.scss +8 -8
  196. package/libs/css/style.mp.scss +72 -72
  197. package/libs/css/style.nvue.scss +15 -15
  198. package/libs/css/style.vue.scss +188 -188
  199. package/libs/function/$parent.ts +24 -24
  200. package/libs/function/addUnit.ts +13 -13
  201. package/libs/function/color.ts +37 -37
  202. package/libs/function/colorGradient.ts +139 -139
  203. package/libs/function/debounce.ts +28 -28
  204. package/libs/function/deepClone.ts +39 -39
  205. package/libs/function/deepMerge.ts +35 -35
  206. package/libs/function/getParent.ts +63 -63
  207. package/libs/function/getRect.ts +26 -26
  208. package/libs/function/guid.ts +42 -42
  209. package/libs/function/md5.ts +403 -403
  210. package/libs/function/parent.ts +21 -21
  211. package/libs/function/queryParams.ts +64 -64
  212. package/libs/function/random.ts +16 -16
  213. package/libs/function/randomArray.ts +11 -11
  214. package/libs/function/route.ts +118 -118
  215. package/libs/function/styleUtils.ts +83 -83
  216. package/libs/function/sys.ts +15 -15
  217. package/libs/function/test.ts +289 -289
  218. package/libs/function/throttle.ts +31 -31
  219. package/libs/function/timeFormat.ts +55 -55
  220. package/libs/function/timeFrom.ts +48 -48
  221. package/libs/function/toast.ts +14 -14
  222. package/libs/function/trim.ts +21 -21
  223. package/libs/function/type2icon.ts +39 -39
  224. package/libs/hooks/index.ts +4 -4
  225. package/libs/hooks/useCompRelation.ts +364 -364
  226. package/libs/hooks/useComponent.ts +759 -759
  227. package/libs/hooks/useEmitter.ts +79 -79
  228. package/libs/hooks/useParent.ts +33 -33
  229. package/libs/hooks/useRect.ts +33 -33
  230. package/libs/index.ts +337 -337
  231. package/libs/request/auto-http.ts +76 -76
  232. package/libs/request/index.ts +242 -242
  233. package/libs/store/index.ts +88 -88
  234. package/libs/util/async-validator.d.ts +75 -75
  235. package/libs/util/async-validator.js +1 -1
  236. package/libs/util/calendar.d.ts +57 -57
  237. package/libs/util/emitter.ts +112 -112
  238. package/libs/util/eventBus.ts +86 -86
  239. package/libs/util/logger.ts +364 -364
  240. package/libs/util/mitt.ts +118 -118
  241. package/libs/util/parent.ts +20 -20
  242. package/package.json +4 -4
  243. package/readme.md +241 -241
  244. package/theme.scss +38 -38
  245. package/types/components.d.ts +97 -97
  246. package/types/global.d.ts +331 -331
  247. package/types/ignore-errors.d.ts +30 -30
  248. package/types/index.d.ts +19 -19
  249. package/types/uni-app.d.ts +63 -63
@@ -1,793 +1,793 @@
1
- <template>
2
- <view v-if="props.isPage" class="u-calendar" :class="props.customClass" :style="$u.toStyle(customStyle)">
3
- <!-- <view class="u-calendar__header">
4
- <view class="u-calendar__header__text" v-if="!slots.tooltip">
5
- {{ toolTip }}
6
- </view>
7
- <slot v-else name="tooltip" />
8
- </view> -->
9
- <view class="u-calendar__action u-flex u-row-center">
10
- <view class="u-calendar__action__icon">
11
- <u-icon
12
- v-if="changeYear"
13
- name="arrow-left-double"
14
- :color="yearArrowColor"
15
- @click="changeYearHandler(0)"
16
- ></u-icon>
17
- </view>
18
- <view class="u-calendar__action__icon">
19
- <u-icon
20
- v-if="changeMonth"
21
- name="arrow-left"
22
- :color="monthArrowColor"
23
- @click="changeMonthHandler(0)"
24
- ></u-icon>
25
- </view>
26
- <view class="u-calendar__action__text">{{ showTitle }}</view>
27
- <view class="u-calendar__action__icon">
28
- <u-icon
29
- v-if="changeMonth"
30
- name="arrow-right"
31
- :color="monthArrowColor"
32
- @click="changeMonthHandler(1)"
33
- ></u-icon>
34
- </view>
35
- <view class="u-calendar__action__icon">
36
- <u-icon
37
- v-if="changeYear"
38
- name="arrow-right-double"
39
- :color="yearArrowColor"
40
- @click="changeYearHandler(1)"
41
- ></u-icon>
42
- </view>
43
- </view>
44
- <view class="u-calendar__week-day">
45
- <view class="u-calendar__week-day__text" v-for="(item, index) in weekDayZh" :key="index">{{ item }}</view>
46
- </view>
47
- <view class="u-calendar__content">
48
- <!-- 前置空白部分 -->
49
- <block v-for="(item, index) in weekdayArr" :key="index">
50
- <view class="u-calendar__content__item"></view>
51
- </block>
52
- <view
53
- class="u-calendar__content__item"
54
- :class="{
55
- 'u-hover-class': openDisAbled(year, month, index + 1),
56
- 'u-calendar__content--start-date':
57
- (mode == 'range' && startDate == `${year}-${month}-${index + 1}`) || mode == 'date',
58
- 'u-calendar__content--end-date':
59
- (mode == 'range' && endDate == `${year}-${month}-${index + 1}`) || mode == 'date'
60
- }"
61
- :style="{ backgroundColor: getColor(index, 1) }"
62
- v-for="(item, index) in daysArr"
63
- :key="index"
64
- @tap="dateClick(index)"
65
- >
66
- <view class="u-calendar__content__item__inner" :style="{ color: getColor(index, 2) }">
67
- <view>{{ index + 1 }}</view>
68
- </view>
69
- <view
70
- class="u-calendar__content__item__tips"
71
- :style="{ color: activeColor }"
72
- v-if="mode == 'range' && startDate == `${year}-${month}-${index + 1}` && startDate != endDate"
73
- >
74
- {{ startText }}
75
- </view>
76
- <view
77
- class="u-calendar__content__item__tips"
78
- :style="{ color: activeColor }"
79
- v-if="mode == 'range' && endDate == `${year}-${month}-${index + 1}`"
80
- >
81
- {{ endText }}
82
- </view>
83
- <view
84
- v-if="
85
- props.showLunar &&
86
- !(mode == 'range' && startDate == `${year}-${month}-${index + 1}` && startDate != endDate) &&
87
- !(mode == 'range' && endDate == `${year}-${month}-${index + 1}`)
88
- "
89
- class="u-calendar__content__item__tips"
90
- :style="{ color: getColor(index, 2) }"
91
- >
92
- {{ lunarArr[index]?.dayCn === '初一' ? lunarArr[index].monthCn : (lunarArr[index]?.dayCn ?? '') }}
93
- </view>
94
- </view>
95
- <view class="u-calendar__content__bg-month">{{ month }}</view>
96
- </view>
97
- <!-- <view class="u-calendar__bottom">
98
- <view class="u-calendar__bottom__choose">
99
- <text>{{ mode == 'date' ? activeDate : startDate }}</text>
100
- <text v-if="endDate">至{{ endDate }}</text>
101
- </view>
102
- <view class="u-calendar__bottom__btn">
103
- <u-button :type="btnType" shape="circle" size="default" @click="btnFix(false)">确定</u-button>
104
- </view>
105
- </view> -->
106
- </view>
107
- <u-popup
108
- v-else
109
- :maskCloseAble="maskCloseAble"
110
- mode="bottom"
111
- :popup="false"
112
- v-model="popupValue"
113
- length="auto"
114
- :safeAreaInsetBottom="safeAreaInsetBottom"
115
- @close="close"
116
- :z-index="uZIndex"
117
- :border-radius="borderRadius"
118
- :closeable="closeable"
119
- >
120
- <view class="u-calendar" :class="props.customClass" :style="$u.toStyle(customStyle)">
121
- <view class="u-calendar__header">
122
- <view class="u-calendar__header__text" v-if="!slots.tooltip">
123
- {{ toolTip }}
124
- </view>
125
- <slot v-else name="tooltip" />
126
- </view>
127
- <view class="u-calendar__action u-flex u-row-center">
128
- <view class="u-calendar__action__icon">
129
- <u-icon
130
- v-if="changeYear"
131
- name="arrow-left-double"
132
- :color="yearArrowColor"
133
- @click="changeYearHandler(0)"
134
- ></u-icon>
135
- </view>
136
- <view class="u-calendar__action__icon">
137
- <u-icon
138
- v-if="changeMonth"
139
- name="arrow-left"
140
- :color="monthArrowColor"
141
- @click="changeMonthHandler(0)"
142
- ></u-icon>
143
- </view>
144
- <view class="u-calendar__action__text">{{ showTitle }}</view>
145
- <view class="u-calendar__action__icon">
146
- <u-icon
147
- v-if="changeMonth"
148
- name="arrow-right"
149
- :color="monthArrowColor"
150
- @click="changeMonthHandler(1)"
151
- ></u-icon>
152
- </view>
153
- <view class="u-calendar__action__icon">
154
- <u-icon
155
- v-if="changeYear"
156
- name="arrow-right-double"
157
- :color="yearArrowColor"
158
- @click="changeYearHandler(1)"
159
- ></u-icon>
160
- </view>
161
- </view>
162
- <view class="u-calendar__week-day">
163
- <view class="u-calendar__week-day__text" v-for="(item, index) in weekDayZh" :key="index">
164
- {{ item }}
165
- </view>
166
- </view>
167
- <view class="u-calendar__content">
168
- <!-- 前置空白部分 -->
169
- <block v-for="(item, index) in weekdayArr" :key="index">
170
- <view class="u-calendar__content__item"></view>
171
- </block>
172
- <view
173
- class="u-calendar__content__item"
174
- :class="{
175
- 'u-hover-class': openDisAbled(year, month, index + 1),
176
- 'u-calendar__content--start-date':
177
- (mode == 'range' && startDate == `${year}-${month}-${index + 1}`) || mode == 'date',
178
- 'u-calendar__content--end-date':
179
- (mode == 'range' && endDate == `${year}-${month}-${index + 1}`) || mode == 'date'
180
- }"
181
- :style="{ backgroundColor: getColor(index, 1) }"
182
- v-for="(item, index) in daysArr"
183
- :key="index"
184
- @tap="dateClick(index)"
185
- >
186
- <view class="u-calendar__content__item__inner" :style="{ color: getColor(index, 2) }">
187
- <view>{{ index + 1 }}</view>
188
- </view>
189
- <view
190
- class="u-calendar__content__item__tips"
191
- :style="{ color: activeColor }"
192
- v-if="mode == 'range' && startDate == `${year}-${month}-${index + 1}` && startDate != endDate"
193
- >
194
- {{ startText }}
195
- </view>
196
- <view
197
- class="u-calendar__content__item__tips"
198
- :style="{ color: activeColor }"
199
- v-if="mode == 'range' && endDate == `${year}-${month}-${index + 1}`"
200
- >
201
- {{ endText }}
202
- </view>
203
- <view
204
- v-if="
205
- props.showLunar &&
206
- !(
207
- mode == 'range' &&
208
- startDate == `${year}-${month}-${index + 1}` &&
209
- startDate != endDate
210
- ) &&
211
- !(mode == 'range' && endDate == `${year}-${month}-${index + 1}`)
212
- "
213
- class="u-calendar__content__item__tips"
214
- :style="{ color: getColor(index, 2) }"
215
- >
216
- {{
217
- lunarArr[index]?.dayCn === '初一' ? lunarArr[index].monthCn : (lunarArr[index]?.dayCn ?? '')
218
- }}
219
- </view>
220
- </view>
221
- <view class="u-calendar__content__bg-month">{{ month }}</view>
222
- </view>
223
- <view class="u-calendar__bottom">
224
- <view class="u-calendar__bottom__choose">
225
- <text>{{ mode == 'date' ? activeDate : startDate }}</text>
226
- <text v-if="endDate">至{{ endDate }}</text>
227
- </view>
228
- <view class="u-calendar__bottom__btn">
229
- <u-button :type="btnType" shape="circle" size="default" @click="btnFix(false)">确定</u-button>
230
- </view>
231
- </view>
232
- </view>
233
- </u-popup>
234
- </template>
235
-
236
- <script lang="ts">
237
- export default {
238
- name: 'u-calendar',
239
- options: {
240
- addGlobalClass: true,
241
- // #ifndef MP-TOUTIAO
242
- virtualHost: true,
243
- // #endif
244
- styleIsolation: 'shared'
245
- }
246
- };
247
- </script>
248
-
249
- <script setup lang="ts">
250
- import { ref, computed, watch, onMounted, useSlots } from 'vue';
251
- import { $u } from '../..';
252
- import { CalendarProps, type CalendarEmits } from './types';
253
- import Calendar from '../../libs/util/calendar';
254
-
255
- /**
256
- * calendar 日历
257
- * @description 此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中。
258
- * @tutorial https://uviewpro.cn/zh/components/calendar.html
259
- * @property {String} mode 选择日期的模式,date-为单个日期,range-为选择日期范围
260
- * @property {Boolean} v-model 布尔值变量,用于控制日历的弹出与收起
261
- * @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
262
- * @property {Boolean} change-year 是否显示顶部的切换年份方向的按钮(默认true)
263
- * @property {Boolean} change-month 是否显示顶部的切换月份方向的按钮(默认true)
264
- * @property {String Number} max-year 可切换的最大年份(默认2050)
265
- * @property {String Number} min-year 可切换的最小年份(默认1950)
266
- * @property {String Number} min-date 最小可选日期(默认1950-01-01)
267
- * @property {String Number} max-date 最大可选日期(默认当前日期)
268
- * @property {String Number} 弹窗顶部左右两边的圆角值,单位rpx(默认20)
269
- * @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭日历(默认true)
270
- * @property {String} month-arrow-color 月份切换按钮箭头颜色(默认#606266)
271
- * @property {String} year-arrow-color 年份切换按钮箭头颜色(默认#909399)
272
- * @property {String} color 日期字体的默认颜色(默认#303133)
273
- * @property {String} active-bg-color 起始/结束日期按钮的背景色(默认#2979ff)
274
- * @property {String Number} z-index 弹出时的z-index值(默认10075)
275
- * @property {String} active-color 起始/结束日期按钮的字体颜色(默认#ffffff)
276
- * @property {String} range-bg-color 起始/结束日期之间的区域的背景颜色(默认rgba(41,121,255,0.13))
277
- * @property {String} range-color 选择范围内字体颜色(默认#2979ff)
278
- * @property {String} start-text 起始日期底部的提示文字(默认 '开始')
279
- * @property {String} end-text 结束日期底部的提示文字(默认 '结束')
280
- * @property {String} btn-type 底部确定按钮的主题(默认 'primary')
281
- * @property {String} toolTip 顶部提示文字,如设置名为tooltip的slot,此参数将失效(默认 '选择日期')
282
- * @property {Boolean} closeable 是否显示右上角的关闭图标(默认true)
283
- * @example <u-calendar v-model="show" :mode="mode"></u-calendar>
284
- */
285
-
286
- const props = defineProps(CalendarProps);
287
- const emit = defineEmits<CalendarEmits>();
288
- const slots = useSlots();
289
-
290
- // 组件内部状态
291
- // 星期几,值为1-7
292
- const weekday = ref(1);
293
- const weekdayArr = ref<number[]>([]);
294
- const days = ref(0);
295
- const daysArr = ref<number[]>([]);
296
- const lunarArr = ref<any[]>([]);
297
- const showTitle = ref('');
298
- const year = ref(2020);
299
- const month = ref(0);
300
- // 当前月有多少天
301
- const day = ref(0);
302
- const startYear = ref(0);
303
- const startMonth = ref(0);
304
- const startDay = ref(0);
305
- const endYear = ref(0);
306
- const endMonth = ref(0);
307
- const endDay = ref(0);
308
- const today = ref('');
309
- const activeDate = ref('');
310
- const startDate = ref('');
311
- const endDate = ref('');
312
- const isStart = ref(true);
313
- const min = ref<{ year: number; month: number; day: number } | null>(null);
314
- const max = ref<{ year: number; month: number; day: number } | null>(null);
315
- const weekDayZh = ref(['日', '一', '二', '三', '四', '五', '六']);
316
-
317
- const dataChange = computed(() => `${props.mode}-${props.minDate}-${props.maxDate}`);
318
- const lunarChange = computed(() => props.showLunar);
319
- // 如果用户有传递z-index值,优先使用
320
- const uZIndex = computed(() => (props.zIndex ? props.zIndex : $u.zIndex.popup));
321
- const popupValue = computed({
322
- get: () => props.modelValue,
323
- set: (val: boolean) => emit('update:modelValue', val)
324
- });
325
-
326
- watch([dataChange, lunarChange], () => {
327
- init();
328
- });
329
-
330
- onMounted(() => {
331
- init();
332
- });
333
-
334
- /**
335
- * 获取日期颜色
336
- * @param index
337
- * @param type 1 背景色 2 字体色
338
- */
339
- function getColor(index: number, type: number) {
340
- let color = type == 1 ? '' : props.color;
341
- let dayNum = index + 1;
342
- let date = `${year.value}-${month.value}-${dayNum}`;
343
- let timestamp = new Date(date.replace(/\-/g, '/')).getTime();
344
- let start = startDate.value.replace(/\-/g, '/');
345
- let end = endDate.value.replace(/\-/g, '/');
346
- if ((props.isActiveCurrent && activeDate.value == date) || startDate.value == date || endDate.value == date) {
347
- color = type == 1 ? props.activeBgColor : props.activeColor;
348
- } else if (endDate.value && timestamp > new Date(start).getTime() && timestamp < new Date(end).getTime()) {
349
- color = type == 1 ? props.rangeBgColor : props.rangeColor;
350
- }
351
- return color;
352
- }
353
-
354
- /**
355
- * 初始化日历数据
356
- */
357
- function init() {
358
- let now = new Date();
359
- let minDateObj = new Date(String(props.minDate));
360
- let maxDateObj = new Date(String(props.maxDate || ''));
361
- if (isNaN(maxDateObj.getTime())) maxDateObj = new Date();
362
- if (now < minDateObj) now = minDateObj;
363
- if (now > maxDateObj) now = maxDateObj;
364
- year.value = now.getFullYear();
365
- month.value = now.getMonth() + 1;
366
- day.value = now.getDate();
367
- today.value = `${now.getFullYear()}-${month.value}-${day.value}`;
368
- activeDate.value = today.value;
369
- min.value = initDate(String(props.minDate));
370
- max.value = initDate(String(props.maxDate) || today.value);
371
- startDate.value = '';
372
- startYear.value = 0;
373
- startMonth.value = 0;
374
- startDay.value = 0;
375
- endYear.value = 0;
376
- endMonth.value = 0;
377
- endDay.value = 0;
378
- endDate.value = '';
379
- isStart.value = true;
380
- changeData();
381
- }
382
-
383
- /**
384
- * 日期字符串转对象
385
- */
386
- function initDate(date: string) {
387
- let fdate = date.split('-');
388
- return {
389
- year: Number(fdate[0] || 1920),
390
- month: Number(fdate[1] || 1),
391
- day: Number(fdate[2] || 1)
392
- };
393
- }
394
-
395
- /**
396
- * 判断日期是否可选
397
- */
398
- function openDisAbled(yearNum: number, monthNum: number, dayNum: number) {
399
- let bool = true;
400
- let date = `${yearNum}/${monthNum}/${dayNum}`;
401
- // let today = this.today.replace(/\-/g, '/');
402
- let minStr = min.value ? `${min.value.year}/${min.value.month}/${min.value.day}` : '';
403
- let maxStr = max.value ? `${max.value.year}/${max.value.month}/${max.value.day}` : '';
404
- let timestamp = new Date(date).getTime();
405
- if (min.value && max.value && timestamp >= new Date(minStr).getTime() && timestamp <= new Date(maxStr).getTime()) {
406
- bool = false;
407
- }
408
- return bool;
409
- }
410
-
411
- /**
412
- * 生成数组
413
- */
414
- function generateArray(start: number, end: number) {
415
- return Array.from(new Array(end + 1).keys()).slice(start);
416
- }
417
-
418
- /**
419
- * 格式化数字
420
- */
421
- function formatNum(num: number) {
422
- return num < 10 ? '0' + num : num + '';
423
- }
424
-
425
- /**
426
- * 获取某月天数
427
- */
428
- function getMonthDay(yearNum: number, monthNum: number) {
429
- return new Date(yearNum, monthNum, 0).getDate();
430
- }
431
-
432
- /**
433
- * 获取某月第一天星期几
434
- */
435
- function getWeekday(yearNum: number, monthNum: number) {
436
- let date = new Date(`${yearNum}/${monthNum}/01 00:00:00`);
437
- return date.getDay();
438
- }
439
-
440
- /**
441
- * 检查年份是否超出范围
442
- */
443
- function checkRange(yearNum: number) {
444
- let overstep = false;
445
- if (yearNum < Number(props.minYear) || yearNum > Number(props.maxYear)) {
446
- uni.showToast({ title: '日期超出范围啦~', icon: 'none' });
447
- overstep = true;
448
- }
449
- return overstep;
450
- }
451
-
452
- /**
453
- * 切换月份
454
- */
455
- function changeMonthHandler(isAdd: number) {
456
- if (isAdd) {
457
- let m = month.value + 1;
458
- let y = m > 12 ? year.value + 1 : year.value;
459
- if (!checkRange(y)) {
460
- month.value = m > 12 ? 1 : m;
461
- year.value = y;
462
- changeData();
463
- }
464
- } else {
465
- let m = month.value - 1;
466
- let y = m < 1 ? year.value - 1 : year.value;
467
- if (!checkRange(y)) {
468
- month.value = m < 1 ? 12 : m;
469
- year.value = y;
470
- changeData();
471
- }
472
- }
473
- }
474
-
475
- /**
476
- * 切换年份
477
- */
478
- function changeYearHandler(isAdd: number) {
479
- let y = isAdd ? year.value + 1 : year.value - 1;
480
- if (!checkRange(y)) {
481
- year.value = y;
482
- changeData();
483
- }
484
- }
485
-
486
- /**
487
- * 更新日历数据
488
- */
489
- function changeData() {
490
- days.value = getMonthDay(year.value, month.value);
491
- daysArr.value = generateArray(1, days.value);
492
- weekday.value = getWeekday(year.value, month.value);
493
- weekdayArr.value = generateArray(1, weekday.value);
494
- showTitle.value = `${year.value}年${month.value}月`;
495
- if (props.showLunar) {
496
- lunarArr.value = [];
497
- daysArr.value.forEach(d => {
498
- lunarArr.value.push(getLunar(year.value, month.value, d));
499
- });
500
- }
501
- if (props.isChange && props.mode == 'date') {
502
- btnFix(true);
503
- }
504
- }
505
-
506
- /**
507
- * 获取农历
508
- */
509
- function getLunar(year, month, day) {
510
- const val = Calendar.solar2lunar(year, month, day);
511
- return {
512
- dayCn: val.IDayCn,
513
- weekCn: val.ncWeek,
514
- monthCn: val.IMonthCn,
515
- day: val.lDay,
516
- week: val.nWeek,
517
- month: val.lMonth,
518
- year: val.lYear
519
- };
520
- }
521
-
522
- /**
523
- * 日期点击事件
524
- */
525
- function dateClick(dayIdx: number) {
526
- if (props.isPage) {
527
- return;
528
- }
529
- const d = dayIdx + 1;
530
- if (!openDisAbled(year.value, month.value, d)) {
531
- day.value = d;
532
- let date = `${year.value}-${month.value}-${d}`;
533
- if (props.mode == 'date') {
534
- activeDate.value = date;
535
- } else {
536
- let compare =
537
- new Date(date.replace(/\-/g, '/')).getTime() < new Date(startDate.value.replace(/\-/g, '/')).getTime();
538
- if (isStart.value || compare) {
539
- startDate.value = date;
540
- startYear.value = year.value;
541
- startMonth.value = month.value;
542
- startDay.value = day.value;
543
- endYear.value = 0;
544
- endMonth.value = 0;
545
- endDay.value = 0;
546
- endDate.value = '';
547
- activeDate.value = '';
548
- isStart.value = false;
549
- } else {
550
- endDate.value = date;
551
- endYear.value = year.value;
552
- endMonth.value = month.value;
553
- endDay.value = day.value;
554
- isStart.value = true;
555
- }
556
- }
557
- }
558
- }
559
-
560
- /**
561
- * 关闭弹窗
562
- */
563
- function close() {
564
- emit('input', false);
565
- emit('update:modelValue', false);
566
- }
567
-
568
- /**
569
- * 获取星期文本
570
- */
571
- function getWeekText(date: string) {
572
- const d = new Date(`${date.replace(/\-/g, '/')} 00:00:00`);
573
- let week = d.getDay();
574
- return '星期' + ['日', '一', '二', '三', '四', '五', '六'][week];
575
- }
576
-
577
- /**
578
- * 确定按钮事件
579
- */
580
- function btnFix(show: boolean) {
581
- if (!show) {
582
- close();
583
- }
584
- if (props.mode == 'date') {
585
- let arr = activeDate.value.split('-');
586
- let y = props.isChange ? year.value : Number(arr[0]);
587
- let m = props.isChange ? month.value : Number(arr[1]);
588
- let d = props.isChange ? day.value : Number(arr[2]);
589
- let daysNum = getMonthDay(y, m);
590
- let result = `${y}-${formatNum(m)}-${formatNum(d)}`;
591
- let weekText = getWeekText(result);
592
- let isToday = false;
593
- if (`${y}-${m}-${d}` == today.value) {
594
- // 今天
595
- isToday = true;
596
- }
597
- const lunar = props.showLunar ? getLunar(y, m, d) : null;
598
- emit('change', {
599
- year: y,
600
- month: m,
601
- day: d,
602
- days: daysNum,
603
- result: result,
604
- week: weekText,
605
- isToday: isToday,
606
- lunar: lunar
607
- // switch: show //是否是切换年月操作
608
- });
609
- } else {
610
- if (!startDate.value || !endDate.value) return;
611
- let startMonthStr = formatNum(startMonth.value);
612
- let startDayStr = formatNum(startDay.value);
613
- let startDateStr = `${startYear.value}-${startMonthStr}-${startDayStr}`;
614
- let startWeek = getWeekText(startDateStr);
615
- let endMonthStr = formatNum(endMonth.value);
616
- let endDayStr = formatNum(endDay.value);
617
- let endDateStr = `${endYear.value}-${endMonthStr}-${endDayStr}`;
618
- let endWeek = getWeekText(endDateStr);
619
- let startLunar = null;
620
- let endLunar = null;
621
- if (props.showLunar) {
622
- startLunar = getLunar(startYear.value, startMonth.value, startDay.value);
623
- endLunar = getLunar(endYear.value, endMonth.value, endDay.value);
624
- }
625
- emit('change', {
626
- startYear: startYear.value,
627
- startMonth: startMonth.value,
628
- startDay: startDay.value,
629
- startDate: startDateStr,
630
- startWeek: startWeek,
631
- endYear: endYear.value,
632
- endMonth: endMonth.value,
633
- endDay: endDay.value,
634
- endDate: endDateStr,
635
- endWeek: endWeek,
636
- startLunar: startLunar,
637
- endLunar: endLunar
638
- });
639
- }
640
- }
641
- </script>
642
-
643
- <style scoped lang="scss">
644
- @import '../../libs/css/style.components.scss';
645
-
646
- .u-calendar {
647
- color: $u-content-color;
648
-
649
- &__header {
650
- width: 100%;
651
- box-sizing: border-box;
652
- font-size: 30rpx;
653
- background-color: #fff;
654
- color: $u-main-color;
655
-
656
- &__text {
657
- margin-top: 30rpx;
658
- padding: 0 60rpx;
659
- @include vue-flex;
660
- justify-content: center;
661
- align-items: center;
662
- }
663
- }
664
-
665
- &__action {
666
- padding: 40rpx 0 40rpx 0;
667
-
668
- &__icon {
669
- margin: 0 16rpx;
670
- }
671
-
672
- &__text {
673
- padding: 0 16rpx;
674
- color: $u-main-color;
675
- font-size: 32rpx;
676
- line-height: 32rpx;
677
- font-weight: bold;
678
- }
679
- }
680
-
681
- &__week-day {
682
- @include vue-flex;
683
- align-items: center;
684
- justify-content: center;
685
- padding: 6px 0;
686
- overflow: hidden;
687
-
688
- &__text {
689
- flex: 1;
690
- text-align: center;
691
- }
692
- }
693
-
694
- &__content {
695
- width: 100%;
696
- @include vue-flex;
697
- flex-wrap: wrap;
698
- padding: 6px 0;
699
- box-sizing: border-box;
700
- background-color: #fff;
701
- position: relative;
702
-
703
- &--end-date {
704
- border-top-right-radius: 8rpx;
705
- border-bottom-right-radius: 8rpx;
706
- }
707
-
708
- &--start-date {
709
- border-top-left-radius: 8rpx;
710
- border-bottom-left-radius: 8rpx;
711
- }
712
-
713
- &__item {
714
- width: 14.2857%;
715
- @include vue-flex;
716
- align-items: center;
717
- justify-content: center;
718
- padding: 6px 0;
719
- overflow: hidden;
720
- position: relative;
721
- z-index: 2;
722
-
723
- &__inner {
724
- height: 84rpx;
725
- @include vue-flex;
726
- align-items: center;
727
- justify-content: center;
728
- flex-direction: column;
729
- font-size: 32rpx;
730
- position: relative;
731
- border-radius: 50%;
732
-
733
- &__desc {
734
- width: 100%;
735
- font-size: 24rpx;
736
- line-height: 24rpx;
737
- transform: scale(0.75);
738
- transform-origin: center center;
739
- position: absolute;
740
- left: 0;
741
- text-align: center;
742
- bottom: 2rpx;
743
- }
744
- }
745
-
746
- &__tips {
747
- width: 100%;
748
- font-size: 24rpx;
749
- line-height: 24rpx;
750
- position: absolute;
751
- left: 0;
752
- transform: scale(0.8);
753
- transform-origin: center center;
754
- text-align: center;
755
- bottom: 8rpx;
756
- z-index: 2;
757
- }
758
- }
759
-
760
- &__bg-month {
761
- position: absolute;
762
- font-size: 130px;
763
- line-height: 130px;
764
- left: 50%;
765
- top: 50%;
766
- transform: translate(-50%, -50%);
767
- color: #e4e7ed;
768
- z-index: 1;
769
- }
770
- }
771
-
772
- &__bottom {
773
- width: 100%;
774
- @include vue-flex;
775
- align-items: center;
776
- justify-content: center;
777
- flex-direction: column;
778
- background-color: #fff;
779
- padding: 0 40rpx 30rpx;
780
- box-sizing: border-box;
781
- font-size: 24rpx;
782
- color: $u-tips-color;
783
-
784
- &__choose {
785
- height: 50rpx;
786
- }
787
-
788
- &__btn {
789
- width: 100%;
790
- }
791
- }
792
- }
793
- </style>
1
+ <template>
2
+ <view v-if="props.isPage" class="u-calendar" :class="props.customClass" :style="$u.toStyle(customStyle)">
3
+ <!-- <view class="u-calendar__header">
4
+ <view class="u-calendar__header__text" v-if="!slots.tooltip">
5
+ {{ toolTip }}
6
+ </view>
7
+ <slot v-else name="tooltip" />
8
+ </view> -->
9
+ <view class="u-calendar__action u-flex u-row-center">
10
+ <view class="u-calendar__action__icon">
11
+ <u-icon
12
+ v-if="changeYear"
13
+ name="arrow-left-double"
14
+ :color="yearArrowColor"
15
+ @click="changeYearHandler(0)"
16
+ ></u-icon>
17
+ </view>
18
+ <view class="u-calendar__action__icon">
19
+ <u-icon
20
+ v-if="changeMonth"
21
+ name="arrow-left"
22
+ :color="monthArrowColor"
23
+ @click="changeMonthHandler(0)"
24
+ ></u-icon>
25
+ </view>
26
+ <view class="u-calendar__action__text">{{ showTitle }}</view>
27
+ <view class="u-calendar__action__icon">
28
+ <u-icon
29
+ v-if="changeMonth"
30
+ name="arrow-right"
31
+ :color="monthArrowColor"
32
+ @click="changeMonthHandler(1)"
33
+ ></u-icon>
34
+ </view>
35
+ <view class="u-calendar__action__icon">
36
+ <u-icon
37
+ v-if="changeYear"
38
+ name="arrow-right-double"
39
+ :color="yearArrowColor"
40
+ @click="changeYearHandler(1)"
41
+ ></u-icon>
42
+ </view>
43
+ </view>
44
+ <view class="u-calendar__week-day">
45
+ <view class="u-calendar__week-day__text" v-for="(item, index) in weekDayZh" :key="index">{{ item }}</view>
46
+ </view>
47
+ <view class="u-calendar__content">
48
+ <!-- 前置空白部分 -->
49
+ <block v-for="(item, index) in weekdayArr" :key="index">
50
+ <view class="u-calendar__content__item"></view>
51
+ </block>
52
+ <view
53
+ class="u-calendar__content__item"
54
+ :class="{
55
+ 'u-hover-class': openDisAbled(year, month, index + 1),
56
+ 'u-calendar__content--start-date':
57
+ (mode == 'range' && startDate == `${year}-${month}-${index + 1}`) || mode == 'date',
58
+ 'u-calendar__content--end-date':
59
+ (mode == 'range' && endDate == `${year}-${month}-${index + 1}`) || mode == 'date'
60
+ }"
61
+ :style="{ backgroundColor: getColor(index, 1) }"
62
+ v-for="(item, index) in daysArr"
63
+ :key="index"
64
+ @tap="dateClick(index)"
65
+ >
66
+ <view class="u-calendar__content__item__inner" :style="{ color: getColor(index, 2) }">
67
+ <view>{{ index + 1 }}</view>
68
+ </view>
69
+ <view
70
+ class="u-calendar__content__item__tips"
71
+ :style="{ color: activeColor }"
72
+ v-if="mode == 'range' && startDate == `${year}-${month}-${index + 1}` && startDate != endDate"
73
+ >
74
+ {{ startText }}
75
+ </view>
76
+ <view
77
+ class="u-calendar__content__item__tips"
78
+ :style="{ color: activeColor }"
79
+ v-if="mode == 'range' && endDate == `${year}-${month}-${index + 1}`"
80
+ >
81
+ {{ endText }}
82
+ </view>
83
+ <view
84
+ v-if="
85
+ props.showLunar &&
86
+ !(mode == 'range' && startDate == `${year}-${month}-${index + 1}` && startDate != endDate) &&
87
+ !(mode == 'range' && endDate == `${year}-${month}-${index + 1}`)
88
+ "
89
+ class="u-calendar__content__item__tips"
90
+ :style="{ color: getColor(index, 2) }"
91
+ >
92
+ {{ lunarArr[index]?.dayCn === '初一' ? lunarArr[index].monthCn : (lunarArr[index]?.dayCn ?? '') }}
93
+ </view>
94
+ </view>
95
+ <view class="u-calendar__content__bg-month">{{ month }}</view>
96
+ </view>
97
+ <!-- <view class="u-calendar__bottom">
98
+ <view class="u-calendar__bottom__choose">
99
+ <text>{{ mode == 'date' ? activeDate : startDate }}</text>
100
+ <text v-if="endDate">至{{ endDate }}</text>
101
+ </view>
102
+ <view class="u-calendar__bottom__btn">
103
+ <u-button :type="btnType" shape="circle" size="default" @click="btnFix(false)">确定</u-button>
104
+ </view>
105
+ </view> -->
106
+ </view>
107
+ <u-popup
108
+ v-else
109
+ :maskCloseAble="maskCloseAble"
110
+ mode="bottom"
111
+ :popup="false"
112
+ v-model="popupValue"
113
+ length="auto"
114
+ :safeAreaInsetBottom="safeAreaInsetBottom"
115
+ @close="close"
116
+ :z-index="uZIndex"
117
+ :border-radius="borderRadius"
118
+ :closeable="closeable"
119
+ >
120
+ <view class="u-calendar" :class="props.customClass" :style="$u.toStyle(customStyle)">
121
+ <view class="u-calendar__header">
122
+ <view class="u-calendar__header__text" v-if="!slots.tooltip">
123
+ {{ toolTip }}
124
+ </view>
125
+ <slot v-else name="tooltip" />
126
+ </view>
127
+ <view class="u-calendar__action u-flex u-row-center">
128
+ <view class="u-calendar__action__icon">
129
+ <u-icon
130
+ v-if="changeYear"
131
+ name="arrow-left-double"
132
+ :color="yearArrowColor"
133
+ @click="changeYearHandler(0)"
134
+ ></u-icon>
135
+ </view>
136
+ <view class="u-calendar__action__icon">
137
+ <u-icon
138
+ v-if="changeMonth"
139
+ name="arrow-left"
140
+ :color="monthArrowColor"
141
+ @click="changeMonthHandler(0)"
142
+ ></u-icon>
143
+ </view>
144
+ <view class="u-calendar__action__text">{{ showTitle }}</view>
145
+ <view class="u-calendar__action__icon">
146
+ <u-icon
147
+ v-if="changeMonth"
148
+ name="arrow-right"
149
+ :color="monthArrowColor"
150
+ @click="changeMonthHandler(1)"
151
+ ></u-icon>
152
+ </view>
153
+ <view class="u-calendar__action__icon">
154
+ <u-icon
155
+ v-if="changeYear"
156
+ name="arrow-right-double"
157
+ :color="yearArrowColor"
158
+ @click="changeYearHandler(1)"
159
+ ></u-icon>
160
+ </view>
161
+ </view>
162
+ <view class="u-calendar__week-day">
163
+ <view class="u-calendar__week-day__text" v-for="(item, index) in weekDayZh" :key="index">
164
+ {{ item }}
165
+ </view>
166
+ </view>
167
+ <view class="u-calendar__content">
168
+ <!-- 前置空白部分 -->
169
+ <block v-for="(item, index) in weekdayArr" :key="index">
170
+ <view class="u-calendar__content__item"></view>
171
+ </block>
172
+ <view
173
+ class="u-calendar__content__item"
174
+ :class="{
175
+ 'u-hover-class': openDisAbled(year, month, index + 1),
176
+ 'u-calendar__content--start-date':
177
+ (mode == 'range' && startDate == `${year}-${month}-${index + 1}`) || mode == 'date',
178
+ 'u-calendar__content--end-date':
179
+ (mode == 'range' && endDate == `${year}-${month}-${index + 1}`) || mode == 'date'
180
+ }"
181
+ :style="{ backgroundColor: getColor(index, 1) }"
182
+ v-for="(item, index) in daysArr"
183
+ :key="index"
184
+ @tap="dateClick(index)"
185
+ >
186
+ <view class="u-calendar__content__item__inner" :style="{ color: getColor(index, 2) }">
187
+ <view>{{ index + 1 }}</view>
188
+ </view>
189
+ <view
190
+ class="u-calendar__content__item__tips"
191
+ :style="{ color: activeColor }"
192
+ v-if="mode == 'range' && startDate == `${year}-${month}-${index + 1}` && startDate != endDate"
193
+ >
194
+ {{ startText }}
195
+ </view>
196
+ <view
197
+ class="u-calendar__content__item__tips"
198
+ :style="{ color: activeColor }"
199
+ v-if="mode == 'range' && endDate == `${year}-${month}-${index + 1}`"
200
+ >
201
+ {{ endText }}
202
+ </view>
203
+ <view
204
+ v-if="
205
+ props.showLunar &&
206
+ !(
207
+ mode == 'range' &&
208
+ startDate == `${year}-${month}-${index + 1}` &&
209
+ startDate != endDate
210
+ ) &&
211
+ !(mode == 'range' && endDate == `${year}-${month}-${index + 1}`)
212
+ "
213
+ class="u-calendar__content__item__tips"
214
+ :style="{ color: getColor(index, 2) }"
215
+ >
216
+ {{
217
+ lunarArr[index]?.dayCn === '初一' ? lunarArr[index].monthCn : (lunarArr[index]?.dayCn ?? '')
218
+ }}
219
+ </view>
220
+ </view>
221
+ <view class="u-calendar__content__bg-month">{{ month }}</view>
222
+ </view>
223
+ <view class="u-calendar__bottom">
224
+ <view class="u-calendar__bottom__choose">
225
+ <text>{{ mode == 'date' ? activeDate : startDate }}</text>
226
+ <text v-if="endDate">至{{ endDate }}</text>
227
+ </view>
228
+ <view class="u-calendar__bottom__btn">
229
+ <u-button :type="btnType" shape="circle" size="default" @click="btnFix(false)">确定</u-button>
230
+ </view>
231
+ </view>
232
+ </view>
233
+ </u-popup>
234
+ </template>
235
+
236
+ <script lang="ts">
237
+ export default {
238
+ name: 'u-calendar',
239
+ options: {
240
+ addGlobalClass: true,
241
+ // #ifndef MP-TOUTIAO
242
+ virtualHost: true,
243
+ // #endif
244
+ styleIsolation: 'shared'
245
+ }
246
+ };
247
+ </script>
248
+
249
+ <script setup lang="ts">
250
+ import { ref, computed, watch, onMounted, useSlots } from 'vue';
251
+ import { $u } from '../..';
252
+ import { CalendarProps, type CalendarEmits } from './types';
253
+ import Calendar from '../../libs/util/calendar';
254
+
255
+ /**
256
+ * calendar 日历
257
+ * @description 此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中。
258
+ * @tutorial https://uviewpro.cn/zh/components/calendar.html
259
+ * @property {String} mode 选择日期的模式,date-为单个日期,range-为选择日期范围
260
+ * @property {Boolean} v-model 布尔值变量,用于控制日历的弹出与收起
261
+ * @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
262
+ * @property {Boolean} change-year 是否显示顶部的切换年份方向的按钮(默认true)
263
+ * @property {Boolean} change-month 是否显示顶部的切换月份方向的按钮(默认true)
264
+ * @property {String Number} max-year 可切换的最大年份(默认2050)
265
+ * @property {String Number} min-year 可切换的最小年份(默认1950)
266
+ * @property {String Number} min-date 最小可选日期(默认1950-01-01)
267
+ * @property {String Number} max-date 最大可选日期(默认当前日期)
268
+ * @property {String Number} 弹窗顶部左右两边的圆角值,单位rpx(默认20)
269
+ * @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭日历(默认true)
270
+ * @property {String} month-arrow-color 月份切换按钮箭头颜色(默认#606266)
271
+ * @property {String} year-arrow-color 年份切换按钮箭头颜色(默认#909399)
272
+ * @property {String} color 日期字体的默认颜色(默认#303133)
273
+ * @property {String} active-bg-color 起始/结束日期按钮的背景色(默认#2979ff)
274
+ * @property {String Number} z-index 弹出时的z-index值(默认10075)
275
+ * @property {String} active-color 起始/结束日期按钮的字体颜色(默认#ffffff)
276
+ * @property {String} range-bg-color 起始/结束日期之间的区域的背景颜色(默认rgba(41,121,255,0.13))
277
+ * @property {String} range-color 选择范围内字体颜色(默认#2979ff)
278
+ * @property {String} start-text 起始日期底部的提示文字(默认 '开始')
279
+ * @property {String} end-text 结束日期底部的提示文字(默认 '结束')
280
+ * @property {String} btn-type 底部确定按钮的主题(默认 'primary')
281
+ * @property {String} toolTip 顶部提示文字,如设置名为tooltip的slot,此参数将失效(默认 '选择日期')
282
+ * @property {Boolean} closeable 是否显示右上角的关闭图标(默认true)
283
+ * @example <u-calendar v-model="show" :mode="mode"></u-calendar>
284
+ */
285
+
286
+ const props = defineProps(CalendarProps);
287
+ const emit = defineEmits<CalendarEmits>();
288
+ const slots = useSlots();
289
+
290
+ // 组件内部状态
291
+ // 星期几,值为1-7
292
+ const weekday = ref(1);
293
+ const weekdayArr = ref<number[]>([]);
294
+ const days = ref(0);
295
+ const daysArr = ref<number[]>([]);
296
+ const lunarArr = ref<any[]>([]);
297
+ const showTitle = ref('');
298
+ const year = ref(2020);
299
+ const month = ref(0);
300
+ // 当前月有多少天
301
+ const day = ref(0);
302
+ const startYear = ref(0);
303
+ const startMonth = ref(0);
304
+ const startDay = ref(0);
305
+ const endYear = ref(0);
306
+ const endMonth = ref(0);
307
+ const endDay = ref(0);
308
+ const today = ref('');
309
+ const activeDate = ref('');
310
+ const startDate = ref('');
311
+ const endDate = ref('');
312
+ const isStart = ref(true);
313
+ const min = ref<{ year: number; month: number; day: number } | null>(null);
314
+ const max = ref<{ year: number; month: number; day: number } | null>(null);
315
+ const weekDayZh = ref(['日', '一', '二', '三', '四', '五', '六']);
316
+
317
+ const dataChange = computed(() => `${props.mode}-${props.minDate}-${props.maxDate}`);
318
+ const lunarChange = computed(() => props.showLunar);
319
+ // 如果用户有传递z-index值,优先使用
320
+ const uZIndex = computed(() => (props.zIndex ? props.zIndex : $u.zIndex.popup));
321
+ const popupValue = computed({
322
+ get: () => props.modelValue,
323
+ set: (val: boolean) => emit('update:modelValue', val)
324
+ });
325
+
326
+ watch([dataChange, lunarChange], () => {
327
+ init();
328
+ });
329
+
330
+ onMounted(() => {
331
+ init();
332
+ });
333
+
334
+ /**
335
+ * 获取日期颜色
336
+ * @param index
337
+ * @param type 1 背景色 2 字体色
338
+ */
339
+ function getColor(index: number, type: number) {
340
+ let color = type == 1 ? '' : props.color;
341
+ let dayNum = index + 1;
342
+ let date = `${year.value}-${month.value}-${dayNum}`;
343
+ let timestamp = new Date(date.replace(/\-/g, '/')).getTime();
344
+ let start = startDate.value.replace(/\-/g, '/');
345
+ let end = endDate.value.replace(/\-/g, '/');
346
+ if ((props.isActiveCurrent && activeDate.value == date) || startDate.value == date || endDate.value == date) {
347
+ color = type == 1 ? props.activeBgColor : props.activeColor;
348
+ } else if (endDate.value && timestamp > new Date(start).getTime() && timestamp < new Date(end).getTime()) {
349
+ color = type == 1 ? props.rangeBgColor : props.rangeColor;
350
+ }
351
+ return color;
352
+ }
353
+
354
+ /**
355
+ * 初始化日历数据
356
+ */
357
+ function init() {
358
+ let now = new Date();
359
+ let minDateObj = new Date(String(props.minDate));
360
+ let maxDateObj = new Date(String(props.maxDate || ''));
361
+ if (isNaN(maxDateObj.getTime())) maxDateObj = new Date();
362
+ if (now < minDateObj) now = minDateObj;
363
+ if (now > maxDateObj) now = maxDateObj;
364
+ year.value = now.getFullYear();
365
+ month.value = now.getMonth() + 1;
366
+ day.value = now.getDate();
367
+ today.value = `${now.getFullYear()}-${month.value}-${day.value}`;
368
+ activeDate.value = today.value;
369
+ min.value = initDate(String(props.minDate));
370
+ max.value = initDate(String(props.maxDate) || today.value);
371
+ startDate.value = '';
372
+ startYear.value = 0;
373
+ startMonth.value = 0;
374
+ startDay.value = 0;
375
+ endYear.value = 0;
376
+ endMonth.value = 0;
377
+ endDay.value = 0;
378
+ endDate.value = '';
379
+ isStart.value = true;
380
+ changeData();
381
+ }
382
+
383
+ /**
384
+ * 日期字符串转对象
385
+ */
386
+ function initDate(date: string) {
387
+ let fdate = date.split('-');
388
+ return {
389
+ year: Number(fdate[0] || 1920),
390
+ month: Number(fdate[1] || 1),
391
+ day: Number(fdate[2] || 1)
392
+ };
393
+ }
394
+
395
+ /**
396
+ * 判断日期是否可选
397
+ */
398
+ function openDisAbled(yearNum: number, monthNum: number, dayNum: number) {
399
+ let bool = true;
400
+ let date = `${yearNum}/${monthNum}/${dayNum}`;
401
+ // let today = this.today.replace(/\-/g, '/');
402
+ let minStr = min.value ? `${min.value.year}/${min.value.month}/${min.value.day}` : '';
403
+ let maxStr = max.value ? `${max.value.year}/${max.value.month}/${max.value.day}` : '';
404
+ let timestamp = new Date(date).getTime();
405
+ if (min.value && max.value && timestamp >= new Date(minStr).getTime() && timestamp <= new Date(maxStr).getTime()) {
406
+ bool = false;
407
+ }
408
+ return bool;
409
+ }
410
+
411
+ /**
412
+ * 生成数组
413
+ */
414
+ function generateArray(start: number, end: number) {
415
+ return Array.from(new Array(end + 1).keys()).slice(start);
416
+ }
417
+
418
+ /**
419
+ * 格式化数字
420
+ */
421
+ function formatNum(num: number) {
422
+ return num < 10 ? '0' + num : num + '';
423
+ }
424
+
425
+ /**
426
+ * 获取某月天数
427
+ */
428
+ function getMonthDay(yearNum: number, monthNum: number) {
429
+ return new Date(yearNum, monthNum, 0).getDate();
430
+ }
431
+
432
+ /**
433
+ * 获取某月第一天星期几
434
+ */
435
+ function getWeekday(yearNum: number, monthNum: number) {
436
+ let date = new Date(`${yearNum}/${monthNum}/01 00:00:00`);
437
+ return date.getDay();
438
+ }
439
+
440
+ /**
441
+ * 检查年份是否超出范围
442
+ */
443
+ function checkRange(yearNum: number) {
444
+ let overstep = false;
445
+ if (yearNum < Number(props.minYear) || yearNum > Number(props.maxYear)) {
446
+ uni.showToast({ title: '日期超出范围啦~', icon: 'none' });
447
+ overstep = true;
448
+ }
449
+ return overstep;
450
+ }
451
+
452
+ /**
453
+ * 切换月份
454
+ */
455
+ function changeMonthHandler(isAdd: number) {
456
+ if (isAdd) {
457
+ let m = month.value + 1;
458
+ let y = m > 12 ? year.value + 1 : year.value;
459
+ if (!checkRange(y)) {
460
+ month.value = m > 12 ? 1 : m;
461
+ year.value = y;
462
+ changeData();
463
+ }
464
+ } else {
465
+ let m = month.value - 1;
466
+ let y = m < 1 ? year.value - 1 : year.value;
467
+ if (!checkRange(y)) {
468
+ month.value = m < 1 ? 12 : m;
469
+ year.value = y;
470
+ changeData();
471
+ }
472
+ }
473
+ }
474
+
475
+ /**
476
+ * 切换年份
477
+ */
478
+ function changeYearHandler(isAdd: number) {
479
+ let y = isAdd ? year.value + 1 : year.value - 1;
480
+ if (!checkRange(y)) {
481
+ year.value = y;
482
+ changeData();
483
+ }
484
+ }
485
+
486
+ /**
487
+ * 更新日历数据
488
+ */
489
+ function changeData() {
490
+ days.value = getMonthDay(year.value, month.value);
491
+ daysArr.value = generateArray(1, days.value);
492
+ weekday.value = getWeekday(year.value, month.value);
493
+ weekdayArr.value = generateArray(1, weekday.value);
494
+ showTitle.value = `${year.value}年${month.value}月`;
495
+ if (props.showLunar) {
496
+ lunarArr.value = [];
497
+ daysArr.value.forEach(d => {
498
+ lunarArr.value.push(getLunar(year.value, month.value, d));
499
+ });
500
+ }
501
+ if (props.isChange && props.mode == 'date') {
502
+ btnFix(true);
503
+ }
504
+ }
505
+
506
+ /**
507
+ * 获取农历
508
+ */
509
+ function getLunar(year, month, day) {
510
+ const val = Calendar.solar2lunar(year, month, day);
511
+ return {
512
+ dayCn: val.IDayCn,
513
+ weekCn: val.ncWeek,
514
+ monthCn: val.IMonthCn,
515
+ day: val.lDay,
516
+ week: val.nWeek,
517
+ month: val.lMonth,
518
+ year: val.lYear
519
+ };
520
+ }
521
+
522
+ /**
523
+ * 日期点击事件
524
+ */
525
+ function dateClick(dayIdx: number) {
526
+ if (props.isPage) {
527
+ return;
528
+ }
529
+ const d = dayIdx + 1;
530
+ if (!openDisAbled(year.value, month.value, d)) {
531
+ day.value = d;
532
+ let date = `${year.value}-${month.value}-${d}`;
533
+ if (props.mode == 'date') {
534
+ activeDate.value = date;
535
+ } else {
536
+ let compare =
537
+ new Date(date.replace(/\-/g, '/')).getTime() < new Date(startDate.value.replace(/\-/g, '/')).getTime();
538
+ if (isStart.value || compare) {
539
+ startDate.value = date;
540
+ startYear.value = year.value;
541
+ startMonth.value = month.value;
542
+ startDay.value = day.value;
543
+ endYear.value = 0;
544
+ endMonth.value = 0;
545
+ endDay.value = 0;
546
+ endDate.value = '';
547
+ activeDate.value = '';
548
+ isStart.value = false;
549
+ } else {
550
+ endDate.value = date;
551
+ endYear.value = year.value;
552
+ endMonth.value = month.value;
553
+ endDay.value = day.value;
554
+ isStart.value = true;
555
+ }
556
+ }
557
+ }
558
+ }
559
+
560
+ /**
561
+ * 关闭弹窗
562
+ */
563
+ function close() {
564
+ emit('input', false);
565
+ emit('update:modelValue', false);
566
+ }
567
+
568
+ /**
569
+ * 获取星期文本
570
+ */
571
+ function getWeekText(date: string) {
572
+ const d = new Date(`${date.replace(/\-/g, '/')} 00:00:00`);
573
+ let week = d.getDay();
574
+ return '星期' + ['日', '一', '二', '三', '四', '五', '六'][week];
575
+ }
576
+
577
+ /**
578
+ * 确定按钮事件
579
+ */
580
+ function btnFix(show: boolean) {
581
+ if (!show) {
582
+ close();
583
+ }
584
+ if (props.mode == 'date') {
585
+ let arr = activeDate.value.split('-');
586
+ let y = props.isChange ? year.value : Number(arr[0]);
587
+ let m = props.isChange ? month.value : Number(arr[1]);
588
+ let d = props.isChange ? day.value : Number(arr[2]);
589
+ let daysNum = getMonthDay(y, m);
590
+ let result = `${y}-${formatNum(m)}-${formatNum(d)}`;
591
+ let weekText = getWeekText(result);
592
+ let isToday = false;
593
+ if (`${y}-${m}-${d}` == today.value) {
594
+ // 今天
595
+ isToday = true;
596
+ }
597
+ const lunar = props.showLunar ? getLunar(y, m, d) : null;
598
+ emit('change', {
599
+ year: y,
600
+ month: m,
601
+ day: d,
602
+ days: daysNum,
603
+ result: result,
604
+ week: weekText,
605
+ isToday: isToday,
606
+ lunar: lunar
607
+ // switch: show //是否是切换年月操作
608
+ });
609
+ } else {
610
+ if (!startDate.value || !endDate.value) return;
611
+ let startMonthStr = formatNum(startMonth.value);
612
+ let startDayStr = formatNum(startDay.value);
613
+ let startDateStr = `${startYear.value}-${startMonthStr}-${startDayStr}`;
614
+ let startWeek = getWeekText(startDateStr);
615
+ let endMonthStr = formatNum(endMonth.value);
616
+ let endDayStr = formatNum(endDay.value);
617
+ let endDateStr = `${endYear.value}-${endMonthStr}-${endDayStr}`;
618
+ let endWeek = getWeekText(endDateStr);
619
+ let startLunar = null;
620
+ let endLunar = null;
621
+ if (props.showLunar) {
622
+ startLunar = getLunar(startYear.value, startMonth.value, startDay.value);
623
+ endLunar = getLunar(endYear.value, endMonth.value, endDay.value);
624
+ }
625
+ emit('change', {
626
+ startYear: startYear.value,
627
+ startMonth: startMonth.value,
628
+ startDay: startDay.value,
629
+ startDate: startDateStr,
630
+ startWeek: startWeek,
631
+ endYear: endYear.value,
632
+ endMonth: endMonth.value,
633
+ endDay: endDay.value,
634
+ endDate: endDateStr,
635
+ endWeek: endWeek,
636
+ startLunar: startLunar,
637
+ endLunar: endLunar
638
+ });
639
+ }
640
+ }
641
+ </script>
642
+
643
+ <style scoped lang="scss">
644
+ @import '../../libs/css/style.components.scss';
645
+
646
+ .u-calendar {
647
+ color: $u-content-color;
648
+
649
+ &__header {
650
+ width: 100%;
651
+ box-sizing: border-box;
652
+ font-size: 30rpx;
653
+ background-color: #fff;
654
+ color: $u-main-color;
655
+
656
+ &__text {
657
+ margin-top: 30rpx;
658
+ padding: 0 60rpx;
659
+ @include vue-flex;
660
+ justify-content: center;
661
+ align-items: center;
662
+ }
663
+ }
664
+
665
+ &__action {
666
+ padding: 40rpx 0 40rpx 0;
667
+
668
+ &__icon {
669
+ margin: 0 16rpx;
670
+ }
671
+
672
+ &__text {
673
+ padding: 0 16rpx;
674
+ color: $u-main-color;
675
+ font-size: 32rpx;
676
+ line-height: 32rpx;
677
+ font-weight: bold;
678
+ }
679
+ }
680
+
681
+ &__week-day {
682
+ @include vue-flex;
683
+ align-items: center;
684
+ justify-content: center;
685
+ padding: 6px 0;
686
+ overflow: hidden;
687
+
688
+ &__text {
689
+ flex: 1;
690
+ text-align: center;
691
+ }
692
+ }
693
+
694
+ &__content {
695
+ width: 100%;
696
+ @include vue-flex;
697
+ flex-wrap: wrap;
698
+ padding: 6px 0;
699
+ box-sizing: border-box;
700
+ background-color: #fff;
701
+ position: relative;
702
+
703
+ &--end-date {
704
+ border-top-right-radius: 8rpx;
705
+ border-bottom-right-radius: 8rpx;
706
+ }
707
+
708
+ &--start-date {
709
+ border-top-left-radius: 8rpx;
710
+ border-bottom-left-radius: 8rpx;
711
+ }
712
+
713
+ &__item {
714
+ width: 14.2857%;
715
+ @include vue-flex;
716
+ align-items: center;
717
+ justify-content: center;
718
+ padding: 6px 0;
719
+ overflow: hidden;
720
+ position: relative;
721
+ z-index: 2;
722
+
723
+ &__inner {
724
+ height: 84rpx;
725
+ @include vue-flex;
726
+ align-items: center;
727
+ justify-content: center;
728
+ flex-direction: column;
729
+ font-size: 32rpx;
730
+ position: relative;
731
+ border-radius: 50%;
732
+
733
+ &__desc {
734
+ width: 100%;
735
+ font-size: 24rpx;
736
+ line-height: 24rpx;
737
+ transform: scale(0.75);
738
+ transform-origin: center center;
739
+ position: absolute;
740
+ left: 0;
741
+ text-align: center;
742
+ bottom: 2rpx;
743
+ }
744
+ }
745
+
746
+ &__tips {
747
+ width: 100%;
748
+ font-size: 24rpx;
749
+ line-height: 24rpx;
750
+ position: absolute;
751
+ left: 0;
752
+ transform: scale(0.8);
753
+ transform-origin: center center;
754
+ text-align: center;
755
+ bottom: 8rpx;
756
+ z-index: 2;
757
+ }
758
+ }
759
+
760
+ &__bg-month {
761
+ position: absolute;
762
+ font-size: 130px;
763
+ line-height: 130px;
764
+ left: 50%;
765
+ top: 50%;
766
+ transform: translate(-50%, -50%);
767
+ color: #e4e7ed;
768
+ z-index: 1;
769
+ }
770
+ }
771
+
772
+ &__bottom {
773
+ width: 100%;
774
+ @include vue-flex;
775
+ align-items: center;
776
+ justify-content: center;
777
+ flex-direction: column;
778
+ background-color: #fff;
779
+ padding: 0 40rpx 30rpx;
780
+ box-sizing: border-box;
781
+ font-size: 24rpx;
782
+ color: $u-tips-color;
783
+
784
+ &__choose {
785
+ height: 50rpx;
786
+ }
787
+
788
+ &__btn {
789
+ width: 100%;
790
+ }
791
+ }
792
+ }
793
+ </style>