year.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <template>
  2. <wd-toast selector="wd-year" />
  3. <view class="wd-year year">
  4. <view class="wd-year__title" v-if="showTitle">{{ yearTitle(date) }}</view>
  5. <view class="wd-year__months">
  6. <view
  7. v-for="(item, index) in months"
  8. :key="index"
  9. :class="`wd-year__month ${item.disabled ? 'is-disabled' : ''} ${item.isLastRow ? 'is-last-row' : ''} ${
  10. item.type ? monthTypeClass(item.type) : ''
  11. }`"
  12. @click="handleDateClick(index)"
  13. >
  14. <view class="wd-year__month-top">{{ item.topInfo }}</view>
  15. <view class="wd-year__month-text">{{ getMonthLabel(item.date) }}</view>
  16. <view class="wd-year__month-bottom">{{ item.bottomInfo }}</view>
  17. </view>
  18. </view>
  19. </view>
  20. </template>
  21. <script lang="ts">
  22. export default {
  23. options: {
  24. addGlobalClass: true,
  25. virtualHost: true,
  26. styleIsolation: 'shared'
  27. }
  28. }
  29. </script>
  30. <script lang="ts" setup>
  31. import wdToast from '../../wd-toast/wd-toast.vue'
  32. import { computed, ref, watch } from 'vue'
  33. import { deepClone, isArray, isFunction } from '../../common/util'
  34. import { compareMonth, formatYearTitle, getDateByDefaultTime, getItemClass, getMonthByOffset, getMonthOffset } from '../utils'
  35. import { useToast } from '../../wd-toast'
  36. import { useTranslate } from '../../composables/useTranslate'
  37. import dayjs from '../../../dayjs'
  38. import { yearProps } from './types'
  39. import type { CalendarDayItem, CalendarDayType } from '../types'
  40. const props = defineProps(yearProps)
  41. const emit = defineEmits(['change'])
  42. const toast = useToast('wd-year')
  43. const { translate } = useTranslate('calendar-view')
  44. const months = ref<CalendarDayItem[]>([])
  45. const monthTypeClass = computed(() => {
  46. return (monthType: CalendarDayType) => {
  47. return getItemClass(monthType, props.value, props.type)
  48. }
  49. })
  50. const yearTitle = computed(() => {
  51. return (date: number) => {
  52. return formatYearTitle(date)
  53. }
  54. })
  55. watch(
  56. [() => props.type, () => props.date, () => props.value, () => props.minDate, () => props.maxDate, () => props.formatter],
  57. () => {
  58. setMonths()
  59. },
  60. {
  61. deep: true,
  62. immediate: true
  63. }
  64. )
  65. function getMonthLabel(date: number) {
  66. return dayjs(date).format(translate('month', date))
  67. }
  68. function setMonths() {
  69. const monthList: CalendarDayItem[] = []
  70. const date = new Date(props.date)
  71. const year = date.getFullYear()
  72. const value = props.value
  73. if (props.type.indexOf('range') > -1 && value && !isArray(value)) {
  74. console.error('[wot-design] value should be array when type is range')
  75. return
  76. }
  77. for (let month = 0; month < 12; month++) {
  78. const date = new Date(year, month, 1).getTime()
  79. let type: CalendarDayType = getMonthType(date)
  80. if (!type && compareMonth(date, Date.now()) === 0) {
  81. type = 'current'
  82. }
  83. const monthObj = getFormatterDate(date, month, type)
  84. monthList.push(monthObj)
  85. }
  86. months.value = deepClone(monthList)
  87. }
  88. function getMonthType(date: number) {
  89. if (props.type === 'monthrange' && isArray(props.value)) {
  90. const [startDate, endDate] = props.value || []
  91. if (startDate && compareMonth(date, startDate) === 0) {
  92. if (endDate && compareMonth(startDate, endDate) === 0) {
  93. return 'same'
  94. }
  95. return 'start'
  96. } else if (endDate && compareMonth(date, endDate) === 0) {
  97. return 'end'
  98. } else if (startDate && endDate && compareMonth(date, startDate) === 1 && compareMonth(date, endDate) === -1) {
  99. return 'middle'
  100. } else {
  101. return ''
  102. }
  103. } else {
  104. if (props.value && compareMonth(date, props.value as number) === 0) {
  105. return 'selected'
  106. } else {
  107. return ''
  108. }
  109. }
  110. }
  111. function handleDateClick(index: number) {
  112. const date = months.value[index]
  113. if (date.disabled) return
  114. switch (props.type) {
  115. case 'month':
  116. handleMonthChange(date)
  117. break
  118. case 'monthrange':
  119. handleMonthRangeChange(date)
  120. break
  121. default:
  122. handleMonthChange(date)
  123. }
  124. }
  125. function getDate(date: number) {
  126. return props.defaultTime && props.defaultTime.length > 0 ? getDateByDefaultTime(date, props.defaultTime[0]) : date
  127. }
  128. function handleMonthChange(date: CalendarDayItem) {
  129. if (date.type !== 'selected') {
  130. emit('change', {
  131. value: getDate(date.date)
  132. })
  133. }
  134. }
  135. function handleMonthRangeChange(date: CalendarDayItem) {
  136. let value: (number | null)[] = []
  137. const [startDate, endDate] = isArray(props.value) ? props.value || [] : []
  138. const compare = compareMonth(date.date, startDate!)
  139. // 禁止选择同个日期
  140. if (!props.allowSameDay && !endDate && compare === 0) return
  141. if (startDate && !endDate && compare > -1) {
  142. if (props.maxRange && getMonthOffset(date.date, startDate) > props.maxRange) {
  143. const maxEndDate = getMonthByOffset(startDate, props.maxRange - 1)
  144. value = [startDate, getDate(maxEndDate)]
  145. toast.show({
  146. msg: props.rangePrompt || translate('rangePromptMonth', props.maxRange)
  147. })
  148. } else {
  149. value = [startDate, getDate(date.date)]
  150. }
  151. } else {
  152. value = [getDate(date.date), null]
  153. }
  154. emit('change', {
  155. value
  156. })
  157. }
  158. function getFormatterDate(date: number, month: number, type?: CalendarDayType) {
  159. let monthObj: CalendarDayItem = {
  160. date: date,
  161. text: month + 1,
  162. topInfo: '',
  163. bottomInfo: '',
  164. type,
  165. disabled: compareMonth(date, props.minDate) === -1 || compareMonth(date, props.maxDate) === 1,
  166. isLastRow: month >= 8
  167. }
  168. if (props.formatter) {
  169. if (isFunction(props.formatter)) {
  170. monthObj = props.formatter(monthObj)
  171. } else {
  172. console.error('[wot-design] error(wd-calendar-view): the formatter prop of wd-calendar-view should be a function')
  173. }
  174. }
  175. return monthObj
  176. }
  177. </script>
  178. <style lang="scss" scoped>
  179. @import './index.scss';
  180. </style>