mobile.jsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { ref, computed } from 'vue'
  2. import { Field as VanField, Popup as VanPopup, Picker as VanPicker, DatetimePicker as VanDatetimePicker } from 'vant'
  3. import { formInputProps } from '../../form-input-props'
  4. import { useFormInput } from '../../../hooks/form-input'
  5. import dayjs from 'dayjs'
  6. import advancedFormat from 'dayjs/plugin/advancedFormat'
  7. import weekYear from 'dayjs/plugin/weekYear'
  8. import weekOfYear from 'dayjs/plugin/weekOfYear'
  9. dayjs.extend(advancedFormat)
  10. dayjs.extend(weekYear)
  11. dayjs.extend(weekOfYear)
  12. export default {
  13. props: formInputProps,
  14. setup (props, context) {
  15. const { width, clearable, placeholder, emitModelValue } = useFormInput(props, context)
  16. const show = ref(false)
  17. const openPicker = () => {
  18. show.value = true
  19. }
  20. const formatter = computed(() => {
  21. if (props.config?.formatter) {
  22. return props.config?.formatter
  23. } else {
  24. const typeToFormatter = {
  25. year: 'YYYY',
  26. month: 'YYYY-MM',
  27. week: 'YYYY 第 ww 周',
  28. date: 'YYYY-MM-DD',
  29. datetime: 'YYYY-MM-DD HH:mm:ss'
  30. }
  31. return typeToFormatter[type.value] ?? typeToFormatter.date
  32. }
  33. })
  34. const type = computed(() => {
  35. const viewType = props.config?.viewType ?? 'date'
  36. return viewType.includes('range') ? 'date' : viewType
  37. })
  38. // vant对dataType的特殊处理
  39. const vantType = computed(() => {
  40. if (type.value === 'month') return 'year-month'
  41. return type.value
  42. })
  43. const isTimestamp = computed(() => {
  44. return props.config?.isTimestamp ?? false
  45. })
  46. // 展示的时间转换
  47. const transValue = computed(() => {
  48. if (!props.modelValue) return null
  49. if (isTimestamp.value) {
  50. return dayjs(props.modelValue).format(formatter.value)
  51. }
  52. if (type.value === 'dates') {
  53. return props.modelValue.split(',')
  54. }
  55. return dayjs(props.modelValue).format(formatter.value)
  56. })
  57. const pickerValue = computed(() => {
  58. return props.modelValue ? new Date(props.modelValue) : new Date()
  59. })
  60. // 时间选择范围确定,最小时间和最大时间
  61. const currentYear = new Date().getFullYear()
  62. const minDate = computed(() => {
  63. return props.config?.minDate ?? new Date(currentYear - 10, 0, 1)
  64. })
  65. const maxDate = computed(() => {
  66. return props.config?.maxDate ?? new Date(currentYear + 10, 11, 31)
  67. })
  68. // 确定按钮
  69. const confirm = (value) => {
  70. show.value = false
  71. emitModelValue(typeToValue(value))
  72. }
  73. // 取消按钮
  74. const cancel = () => {
  75. show.value = false
  76. }
  77. // 不同事件类型传值不同,做转换
  78. const typeToValue = (val) => {
  79. if (!val) return ''
  80. if (isTimestamp.value) {
  81. return new Date(val).getTime()
  82. } else if (type.value === 'datetime') {
  83. return dayjs(val).format('YYYY-MM-DD HH:mm:ss')
  84. }
  85. return dayjs(val).format(formatter.value)
  86. }
  87. // 年/月单独实现
  88. const times = (n, iteratee) => {
  89. if (n < 0) return []
  90. const result = new Array(n)
  91. let index = -1
  92. while (++index < n) {
  93. result[index] = iteratee(index)
  94. }
  95. return result
  96. }
  97. // 月份前置0
  98. const padZero = (num, targetLength = 2) => {
  99. let str = num + ''
  100. while (str.length < targetLength) {
  101. str = '0' + str
  102. }
  103. return str
  104. }
  105. // 年月的选项列表
  106. const columns = computed(() => {
  107. const ranges = [
  108. { type: 'year', range: [dayjs(minDate.value).format('YYYY'), dayjs(maxDate.value).format('YYYY')] }
  109. // { type: 'month', range: [dayjs(minDate.value).format('MM'), dayjs(maxDate.value).format('MM')] }
  110. ]
  111. const { range = null } = ranges.find(v => v.type === type.value)
  112. if (range) return times(range[1] - range[0] + 1, (index) => padZero(parseInt(range[0]) + index))
  113. return []
  114. })
  115. // 年月的默认选中值设置
  116. const defaultIndex = computed(() => {
  117. if (type.value === 'year') {
  118. return columns.value.findIndex(v => v === new Date().getFullYear().toString())
  119. }
  120. if (type.value === 'month') {
  121. return columns.value.findIndex(v => v === new Date().getMonth().toString())
  122. }
  123. return 0
  124. })
  125. // 头部标题配置
  126. const titleMap = {
  127. year: '选择年',
  128. month: '选择年月',
  129. date: '选择年月日',
  130. datetime: '选择日期时间'
  131. }
  132. // 弹窗头部标题
  133. const title = computed(() => {
  134. return titleMap[type.value] ?? titleMap.datetime
  135. })
  136. return () =>
  137. <div style={{ width: width.value }}>
  138. <VanField modelValue={transValue.value}
  139. placeholder={placeholder.value}
  140. clearable={clearable.value}
  141. onclick={openPicker}
  142. readonly
  143. is-link />
  144. <VanPopup show={show.value} position="bottom" style="height: 40%">
  145. {['year'].includes(type.value)
  146. ? <VanPicker modelValue={pickerValue.value}
  147. columns={columns.value}
  148. default-index={defaultIndex.value}
  149. title={title.value}
  150. onConfirm={confirm}
  151. onCancel={cancel}/>
  152. : <VanDatetimePicker modelValue={pickerValue.value}
  153. type={vantType.value}
  154. title={title.value}
  155. minDate={new Date(minDate.value)}
  156. maxDate={new Date(maxDate.value)}
  157. onConfirm={confirm}
  158. onCancel={cancel} />}
  159. </VanPopup>
  160. </div>
  161. }
  162. }