date-picker.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <template>
  2. <cube-cascade-picker
  3. v-model="isVisible"
  4. :data="data"
  5. :selected-index="selectedIndex"
  6. :title="title"
  7. :subtitle="subtitle"
  8. :cancel-txt="_cancelTxt"
  9. :confirm-txt="_confirmTxt"
  10. :swipe-time="swipeTime"
  11. :z-index="zIndex"
  12. :mask-closable="maskClosable"
  13. @select="_select"
  14. @cancel="_cancel"
  15. @change="_change">
  16. </cube-cascade-picker>
  17. </template>
  18. <script>
  19. import visibilityMixin from '../../common/mixins/visibility'
  20. import popupMixin from '../../common/mixins/popup'
  21. import pickerMixin from '../../common/mixins/picker'
  22. import localeMixin from '../../common/mixins/locale'
  23. import { deepAssign, findIndex } from '../../common/helpers/util'
  24. import { computeNatureMaxDay, formatType } from '../../common/lang/date'
  25. const COMPONENT_NAME = 'cube-date-picker'
  26. const EVENT_SELECT = 'select'
  27. const EVENT_CANCEL = 'cancel'
  28. const EVENT_CHANGE = 'change'
  29. const TYPE_LIST = ['year', 'month', 'date', 'hour', 'minute', 'second']
  30. const NATURE_BOUNDARY_MAP = {
  31. month: {
  32. natureMin: 1,
  33. natureMax: 12
  34. },
  35. date: {
  36. natureMin: 1,
  37. natureMax: 31
  38. },
  39. hour: {
  40. natureMin: 0,
  41. natureMax: 23
  42. },
  43. minute: {
  44. natureMin: 0,
  45. natureMax: 59
  46. },
  47. second: {
  48. natureMin: 0,
  49. natureMax: 59
  50. }
  51. }
  52. const DEFAULT_FORMAT = {
  53. year: 'YYYY',
  54. month: 'M',
  55. date: 'D',
  56. hour: 'hh',
  57. minute: 'mm',
  58. second: 'ss'
  59. }
  60. export default {
  61. name: COMPONENT_NAME,
  62. mixins: [visibilityMixin, popupMixin, pickerMixin, localeMixin],
  63. props: {
  64. min: {
  65. type: [Date, Array],
  66. default() {
  67. return new Date(2010, 1, 1)
  68. }
  69. },
  70. max: {
  71. type: [Date, Array],
  72. default() {
  73. return new Date(2020, 12, 31)
  74. }
  75. },
  76. startColumn: {
  77. type: String,
  78. default() {
  79. return 'year'
  80. }
  81. },
  82. columnCount: {
  83. type: Number,
  84. default: 3
  85. },
  86. format: {
  87. type: Object,
  88. default() {
  89. return {}
  90. }
  91. },
  92. value: {
  93. type: [Date, Array],
  94. default() {
  95. return this.min
  96. }
  97. }
  98. },
  99. computed: {
  100. formatConfig() {
  101. const formatConfig = Object.assign({}, DEFAULT_FORMAT)
  102. deepAssign(formatConfig, this.format)
  103. return formatConfig
  104. },
  105. natureRangeCache() {
  106. const natureRangeCache = {
  107. hour: [],
  108. minute: [],
  109. second: []
  110. }
  111. Object.keys(natureRangeCache).forEach((key) => {
  112. natureRangeCache[key] = this._range(key, NATURE_BOUNDARY_MAP[key].natureMin, NATURE_BOUNDARY_MAP[key].natureMax)
  113. })
  114. return natureRangeCache
  115. },
  116. startIndex() {
  117. const startIndex = TYPE_LIST.indexOf(this.startColumn)
  118. return startIndex < 0 ? 0 : startIndex
  119. },
  120. minArray() {
  121. return this.min instanceof Date
  122. ? dateToArray(this.min).slice(this.startIndex, this.startIndex + this.columnCount)
  123. : this.min
  124. },
  125. maxArray() {
  126. return this.max instanceof Date
  127. ? dateToArray(this.max).slice(this.startIndex, this.startIndex + this.columnCount)
  128. : this.max
  129. },
  130. valueArray() {
  131. return this.value instanceof Date
  132. ? dateToArray(this.value).slice(this.startIndex, this.startIndex + this.columnCount)
  133. : this.value
  134. },
  135. data() {
  136. const data = []
  137. this._generateData(this.startIndex, 0, data)
  138. return data
  139. },
  140. selectedIndex() {
  141. const selectedIndex = []
  142. let data = this.data
  143. let index
  144. for (let i = 0; i < this.columnCount && i < 6 - this.startIndex; i++) {
  145. index = findIndex(data, (item) => {
  146. return this.valueArray[i] && item.value === this.valueArray[i]
  147. })
  148. selectedIndex[i] = index !== -1 ? index : 0
  149. data = data[selectedIndex[i]] && data[selectedIndex[i]].children
  150. }
  151. return selectedIndex
  152. }
  153. },
  154. methods: {
  155. _select(selectedVal, selectedIndex, selectedText) {
  156. this.$emit(EVENT_SELECT, this._arrayToDate(selectedVal), selectedVal, selectedText)
  157. },
  158. _cancel() {
  159. this.$emit(EVENT_CANCEL)
  160. },
  161. _change(i, newIndex) {
  162. this.$emit(EVENT_CHANGE, i, newIndex)
  163. },
  164. _generateData(i, count, item) {
  165. if (count === 0) {
  166. const min = i === 0 ? this.minArray[0] : Math.max(this.minArray[0], NATURE_BOUNDARY_MAP[TYPE_LIST[i]].natureMin)
  167. const max = i === 0 ? this.maxArray[0] : Math.min(this.maxArray[0], NATURE_BOUNDARY_MAP[TYPE_LIST[i]].natureMax)
  168. item.push.apply(item, this._range(TYPE_LIST[i], min, max, true, true))
  169. } else {
  170. if (i < 3 || item.isMin || item.isMax) {
  171. const natureMax = i === 2 ? computeNatureMaxDay(item.value, item.year) : NATURE_BOUNDARY_MAP[TYPE_LIST[i]].natureMax
  172. const min = item.isMin ? Math.max(this.minArray[count], NATURE_BOUNDARY_MAP[TYPE_LIST[i]].natureMin) : NATURE_BOUNDARY_MAP[TYPE_LIST[i]].natureMin
  173. const max = item.isMax ? Math.min(this.maxArray[count], natureMax) : natureMax
  174. const storageYear = i === 1 && this.startIndex === 0 && this.columnCount >= 3 && item.value
  175. item.children = this._range(TYPE_LIST[i], min, max, item.isMin, item.isMax, storageYear)
  176. } else {
  177. item.children = this.natureRangeCache[TYPE_LIST[i]]
  178. }
  179. }
  180. if (count < this.columnCount - 1 && i < 5) {
  181. (item.children || item).forEach(subItem => {
  182. (!subItem.children || subItem.isMin || subItem.isMax) && this._generateData(i + 1, count + 1, subItem)
  183. })
  184. }
  185. },
  186. _arrayToDate(selectedVal) {
  187. const args = []
  188. const defaultDateArray = dateToArray(new Date(0))
  189. for (let i = 0; i < 6; i++) {
  190. if (i < this.startIndex) {
  191. args[i] = defaultDateArray[i]
  192. } else if (i >= this.startIndex + this.columnCount) {
  193. args[i] = NATURE_BOUNDARY_MAP[TYPE_LIST[i]].natureMin
  194. } else {
  195. args[i] = selectedVal[i - this.startIndex]
  196. }
  197. }
  198. // Month need to subtract 1.
  199. args[1]--
  200. return new Date(...args)
  201. },
  202. _range(type, min, max, fatherIsMin, fatherIsMax, year = 0) {
  203. if (!this._rangeCache) {
  204. this._rangeCache = {}
  205. }
  206. const k = type + year + min + max + fatherIsMin + fatherIsMax
  207. if (this._rangeCache[k]) {
  208. return this._rangeCache[k]
  209. }
  210. const arr = []
  211. const format = this.formatConfig[type]
  212. for (let i = min; i <= max; i++) {
  213. const object = {
  214. text: formatType(type, format, i, 'i'),
  215. value: i
  216. }
  217. if (fatherIsMin && i === min) object.isMin = true
  218. if (fatherIsMax && i === max) object.isMax = true
  219. if (year) object.year = year
  220. arr.push(object)
  221. }
  222. this._rangeCache[k] = arr
  223. return arr
  224. }
  225. }
  226. }
  227. function dateToArray(date) {
  228. return [date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()]
  229. }
  230. </script>