574 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			574 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Vue
		
	
	
	
| <template>
 | ||
|   <div class="md-date-picker" :class="[type]">
 | ||
|     <md-picker
 | ||
|       ref="picker"
 | ||
|       v-model="isPickerShow"
 | ||
|       :data="columnData"
 | ||
|       :cols="columnData.length"
 | ||
|       :default-value="columnDataDefault"
 | ||
|       :title="title"
 | ||
|       :describe="describe"
 | ||
|       :ok-text="okText"
 | ||
|       :cancel-text="cancelText"
 | ||
|       :is-view="isView"
 | ||
|       :mask-closable="maskClosable"
 | ||
|       @initialed="$emit('initialed')"
 | ||
|       @change="$_onPickerChange"
 | ||
|       @confirm="$_onPickerConfirm"
 | ||
|       @cancel="$_onPickerCancel"
 | ||
|       @show="$_onPickerShow"
 | ||
|       @hide="$_onPickerHide"
 | ||
|     ></md-picker>
 | ||
|   </div>
 | ||
| </template>
 | ||
| 
 | ||
| <script>
 | ||
| import Picker from '../picker'
 | ||
| import pickerMixin from '../picker/mixins'
 | ||
| import {
 | ||
|   toObject,
 | ||
|   toArray,
 | ||
|   warn
 | ||
| } from '../_util'
 | ||
| 
 | ||
| // yyyy-MM-dd hh:mm:ss => Year-Month-Date Hour:Minute
 | ||
| const TYPE_FORMAT = {
 | ||
|   'yyyy': 'Year',
 | ||
|   'MM': 'Month',
 | ||
|   'dd': 'Date',
 | ||
|   'hh': 'Hour',
 | ||
|   'mm': 'Minute'
 | ||
| }
 | ||
| 
 | ||
| const TYPE_FORMAT_INVERSE = toObject(
 | ||
|   Object.keys(TYPE_FORMAT).map(item => {
 | ||
|     return {
 | ||
|       [TYPE_FORMAT[item]]: item
 | ||
|     }
 | ||
|   })
 | ||
| )
 | ||
| 
 | ||
| const TYPE_METHODS = {
 | ||
|   'Year': 'getFullYear',
 | ||
|   'Month': 'getMonth',
 | ||
|   'Date': 'getDate',
 | ||
|   'Hour': 'getHours',
 | ||
|   'Minute': 'getMinutes'
 | ||
| }
 | ||
| 
 | ||
| export default {
 | ||
|   name: 'md-date-picker',
 | ||
| 
 | ||
|   mixins: [pickerMixin],
 | ||
| 
 | ||
|   components: {
 | ||
|     [Picker.name]: Picker
 | ||
|   },
 | ||
| 
 | ||
|   props: {
 | ||
|     type: { // date, time, datetime, custom
 | ||
|       type: String,
 | ||
|       default: 'date'
 | ||
|     },
 | ||
|     customTypes: { // yyyy, MM, dd, hh, mm
 | ||
|       type: Array,
 | ||
|       default () {
 | ||
|         return []
 | ||
|       },
 | ||
|       validator (val) {
 | ||
|         let res = true
 | ||
|         val.forEach(type => {
 | ||
|           type = TYPE_FORMAT[type] || type
 | ||
|           /* istanbul ignore if */
 | ||
|           if (!(type in TYPE_METHODS)) {
 | ||
|             return (res = false)
 | ||
|           }
 | ||
|         })
 | ||
|         return res
 | ||
|       }
 | ||
|     },
 | ||
|     minDate: {
 | ||
|       type: Date
 | ||
|     },
 | ||
|     maxDate: {
 | ||
|       type: Date
 | ||
|     },
 | ||
|     defaultDate: {
 | ||
|       type: Date
 | ||
|     },
 | ||
|     minuteStep: {
 | ||
|       type: Number,
 | ||
|       default: 1
 | ||
|     },
 | ||
|     unitText: {
 | ||
|       type: Array,
 | ||
|       default () {
 | ||
|         return ['年', '月', '日', '时', '分']
 | ||
|       }
 | ||
|     },
 | ||
|     todayText: {
 | ||
|       type: String,
 | ||
|       default: ''
 | ||
|     },
 | ||
|     textRender: {
 | ||
|       type: [Function, String],
 | ||
|       default: ''
 | ||
|     },
 | ||
| 
 | ||
|     // Mixin Props
 | ||
|     // value: {
 | ||
|     //   type: Boolean,
 | ||
|     //   default: false
 | ||
|     // },
 | ||
|     // title: {
 | ||
|     //   type: String
 | ||
|     // },
 | ||
|     // describe: {
 | ||
|     //   type: String
 | ||
|     // },
 | ||
|     // okText: {
 | ||
|     //   type: String
 | ||
|     // },
 | ||
|     // cancelText: {
 | ||
|     //   type: String
 | ||
|     // },
 | ||
|     // isView: {
 | ||
|     //   type: Boolean,
 | ||
|     //   default: false
 | ||
|     // },
 | ||
|     // maskClosable: {
 | ||
|     //   type: Boolean,
 | ||
|     //   default: true,
 | ||
|     // }
 | ||
|   },
 | ||
| 
 | ||
|   data () {
 | ||
|     return {
 | ||
|       isPickerShow: false,
 | ||
|       currentDateIns: new Date(),
 | ||
|       columnData: [],
 | ||
|       oldColumnData: null,
 | ||
|       columnDataDefault: [],
 | ||
|       columnDataGenerator: [],
 | ||
|     }
 | ||
|   },
 | ||
| 
 | ||
|   computed: {
 | ||
|     picker () {
 | ||
|       return this.$refs['picker']
 | ||
|     },
 | ||
|     currentYear () {
 | ||
|       return this.currentDateIns.getFullYear()
 | ||
|     },
 | ||
|     currentMonth () {
 | ||
|       return this.currentDateIns.getMonth() + 1
 | ||
|     },
 | ||
|     currentDate () {
 | ||
|       return this.currentDateIns.getDate()
 | ||
|     },
 | ||
|     currentHours () {
 | ||
|       return this.currentDateIns.getHours()
 | ||
|     },
 | ||
|     currentMinutes () {
 | ||
|       return this.currentDateIns.getMinutes()
 | ||
|     }
 | ||
|   },
 | ||
| 
 | ||
|   watch: {
 | ||
|     value (val) {
 | ||
|       this.isPickerShow = val
 | ||
|     },
 | ||
|     isPickerShow (val) {
 | ||
|       if (!val) {
 | ||
|         this.$emit('input', val)
 | ||
|       }
 | ||
|     },
 | ||
|     defaultDate () {
 | ||
|       this.$_initPickerColumn()
 | ||
|     },
 | ||
|     minDate () {
 | ||
|       this.$_initPickerColumn()
 | ||
|     },
 | ||
|     maxDate () {
 | ||
|       this.$_initPickerColumn()
 | ||
|     }
 | ||
|   },
 | ||
| 
 | ||
|   mounted () {
 | ||
|     this.$_initPicker()
 | ||
|   },
 | ||
| 
 | ||
|   methods: {
 | ||
|     // MARK: private methods
 | ||
|     $_initPicker () {
 | ||
|       if (!this.isView && this.value) {
 | ||
|         this.isPickerShow = this.value
 | ||
|       }
 | ||
| 
 | ||
|       this.picker.inheritPickerApi(this)
 | ||
|       this.$_initPickerColumn()
 | ||
|     },
 | ||
|     $_initPickerColumn () {
 | ||
|       this.$_resetPickerColumn()
 | ||
|       this.$_initColumnDataGenerator()
 | ||
|       this.$_initColumnData(0, this.columnDataDefault)
 | ||
|     },
 | ||
|     $_resetPickerColumn() {
 | ||
|       this.oldColumnData = null
 | ||
|       this.columnData = []
 | ||
|       this.columnDataDefault = []
 | ||
|       this.columnDataGenerator = []
 | ||
|     },
 | ||
|     $_initColumnData (columnIndex, defaultDate = [], isSetColumn = true) {
 | ||
|       const columnData = this.columnData
 | ||
|       const columnDataGenerator = this.columnDataGenerator
 | ||
|       for (let i = columnIndex, len = columnDataGenerator.length; i < len; i++) {
 | ||
|         // Collect parameters for columnDataGenerator
 | ||
|         const columnDataGeneratorParams = []
 | ||
|         const generator = columnDataGenerator[i]
 | ||
|         for (let j = 0; j < i; j++) {
 | ||
|           const _generator = columnDataGenerator[j]
 | ||
|           if (defaultDate[j] && _generator) {
 | ||
|             columnDataGeneratorParams.push({
 | ||
|               type: _generator.type,
 | ||
|               value: defaultDate[j]
 | ||
|             })
 | ||
|             continue
 | ||
|           }
 | ||
| 
 | ||
|           const itemIndex = this.picker.getColumnIndex(j) || 0
 | ||
|           /* istanbul ignore else */
 | ||
|           if (columnData[j]) {
 | ||
|             columnDataGeneratorParams.push(columnData[j][itemIndex])
 | ||
|           } else {
 | ||
|             columnDataGeneratorParams.push('')
 | ||
|             warn(`DatePicker columnData of index ${j} is void`)
 | ||
|           }
 | ||
|         }
 | ||
| 
 | ||
|         // Generator colume data with columnDataGeneratorParams
 | ||
|         const curColumnData = generator ? generator.apply(this, columnDataGeneratorParams) : ''
 | ||
| 
 | ||
|         // set picker column data & refresh picker
 | ||
|         isSetColumn && this.picker.setColumnValues(i, curColumnData)
 | ||
| 
 | ||
|         // store column date
 | ||
|         this.$set(columnData, i, curColumnData)
 | ||
|       }
 | ||
| 
 | ||
|       isSetColumn && this.picker.refresh(null, columnIndex)
 | ||
|     },
 | ||
|     $_initColumnDataGenerator () {
 | ||
|       this.$_generateYearData.type = 'Year'
 | ||
|       this.$_generateMonthData.type = 'Month'
 | ||
|       this.$_generateDateData.type = 'Date'
 | ||
|       this.$_generateHourData.type = 'Hour'
 | ||
|       this.$_generateMinuteData.type = 'Minute'
 | ||
|     
 | ||
|       const defaultDate = this.$_getDefaultDate()     
 | ||
|       switch (this.type) {
 | ||
|         case 'date':
 | ||
|           this.$_initColumnDataGeneratorForDate(defaultDate)
 | ||
|           break
 | ||
|         case 'time':
 | ||
|           this.$_initColumnDataGeneratorForTime(defaultDate)
 | ||
|           break
 | ||
|         case 'datetime':
 | ||
|           this.$_initColumnDataGeneratorForDate(defaultDate)
 | ||
|           this.$_initColumnDataGeneratorForTime(defaultDate)
 | ||
|           break
 | ||
|         default:
 | ||
|           this.$_initColumnDataGeneratorForCustom(defaultDate)
 | ||
|           break
 | ||
|       }
 | ||
|     },
 | ||
|     $_initColumnDataGeneratorForDate (defaultDate) {
 | ||
|       this.columnDataGenerator = this.columnDataGenerator.concat([
 | ||
|         this.$_generateYearData,
 | ||
|         this.$_generateMonthData,
 | ||
|         this.$_generateDateData
 | ||
|       ])
 | ||
| 
 | ||
|       this.columnDataDefault = defaultDate ? this.columnDataDefault.concat([
 | ||
|         defaultDate.getFullYear(),
 | ||
|         defaultDate.getMonth() + 1,
 | ||
|         defaultDate.getDate()
 | ||
|       ]) : []
 | ||
|     },
 | ||
|     $_initColumnDataGeneratorForTime (defaultDate) {
 | ||
|       this.columnDataGenerator = this.columnDataGenerator.concat([
 | ||
|         this.$_generateHourData,
 | ||
|         this.$_generateMinuteData
 | ||
|       ])
 | ||
|       this.columnDataDefault = defaultDate ? this.columnDataDefault.concat([
 | ||
|         defaultDate.getHours(),
 | ||
|         defaultDate.getMinutes()
 | ||
|       ]) : []
 | ||
|     },
 | ||
|     $_initColumnDataGeneratorForCustom (defaultDate) {
 | ||
|       this.customTypes.forEach(type => {
 | ||
|         type = TYPE_FORMAT[type] || type
 | ||
|         this.columnDataGenerator.push(this[`$_generate${type}Data`])
 | ||
| 
 | ||
|         if (defaultDate) {
 | ||
|           let value = defaultDate[TYPE_METHODS[type]]()
 | ||
|           
 | ||
|           if (type === 'Month') {
 | ||
|             value += 1
 | ||
|           }
 | ||
| 
 | ||
|           this.columnDataDefault.push(value)
 | ||
|         }
 | ||
|       })
 | ||
|     },
 | ||
|     $_getDefaultDate () {
 | ||
|       const defaultDate = this.defaultDate
 | ||
|       const minDate = this.minDate
 | ||
|       const maxDate = this.maxDate
 | ||
| 
 | ||
|       if (!defaultDate) {
 | ||
|         return defaultDate
 | ||
|       }
 | ||
| 
 | ||
|       if (minDate && defaultDate.getTime() < minDate.getTime()) {
 | ||
|         return minDate
 | ||
|       }
 | ||
| 
 | ||
|       if (maxDate && defaultDate.getTime() > maxDate.getTime()) {
 | ||
|         return maxDate
 | ||
|       }
 | ||
| 
 | ||
|       return defaultDate
 | ||
|     },
 | ||
|     $_getGeneratorArguments (args) {
 | ||
|       const defaultArguments = {
 | ||
|         Year: this.currentYear,
 | ||
|         Month: this.currentMonth,
 | ||
|         Date: this.currentDate,
 | ||
|         Hour: this.currentHours,
 | ||
|         Minute: this.currentMinutes
 | ||
|       }
 | ||
|       args.map(item => {
 | ||
|         defaultArguments[item.type] = item.value
 | ||
|       })
 | ||
|       return defaultArguments
 | ||
|     },
 | ||
|     $_generateYearData () {
 | ||
|       const start = this.minDate ? this.minDate.getFullYear() : this.currentYear - 20
 | ||
|       const end = this.maxDate ? this.maxDate.getFullYear() : this.currentYear + 20
 | ||
|       /* istanbul ignore if */
 | ||
|       if (start > end) {
 | ||
|         warn('MinDate Year should be earlier than MaxDate')
 | ||
|         return
 | ||
|       }
 | ||
|       return this.$_generateData(start, end, 'Year', this.unitText[0], 1)
 | ||
|     },
 | ||
|     $_generateMonthData () {
 | ||
|       const args = this.$_getGeneratorArguments(toArray(arguments))
 | ||
|       let start, end
 | ||
| 
 | ||
|       if (this.$_isDateTimeEqual(this.minDate, args.Year)) {
 | ||
|         start = this.minDate.getMonth() + 1
 | ||
|       } else {
 | ||
|         start = 1
 | ||
|       }
 | ||
| 
 | ||
|       if (this.$_isDateTimeEqual(this.maxDate, args.Year)) {
 | ||
|         end = this.maxDate.getMonth() + 1
 | ||
|       } else {
 | ||
|         end = 12
 | ||
|       }
 | ||
|       return this.$_generateData(start, end, 'Month', this.unitText[1] || '', 1, arguments)
 | ||
|     },
 | ||
|     $_generateDateData () {
 | ||
|       const args = this.$_getGeneratorArguments(toArray(arguments))
 | ||
|       
 | ||
|       let start, end
 | ||
| 
 | ||
|       if (this.$_isDateTimeEqual(this.minDate, args.Year, args.Month)) {
 | ||
|         start = this.minDate.getDate()
 | ||
|       } else {
 | ||
|         start = 1
 | ||
|       }
 | ||
| 
 | ||
|       if (this.$_isDateTimeEqual(this.maxDate, args.Year, args.Month)) {
 | ||
|         end = this.maxDate.getDate()
 | ||
|       } else {
 | ||
|         end = new Date(args.Year, args.Month, 0).getDate()
 | ||
|       }
 | ||
| 
 | ||
|       const dateData = this.$_generateData(start, end, 'Date', this.unitText[2] || '', 1, arguments)
 | ||
| 
 | ||
|       if (this.$_isDateTimeEqual(this.currentDateIns, args.Year, args.Month) && 
 | ||
|           this.currentDate >= start && this.currentDate <= end &&
 | ||
|           this.todayText) {
 | ||
|         const currentDateIndex = this.currentDate - start
 | ||
|         const currentDate = dateData[currentDateIndex].text
 | ||
|         dateData[currentDateIndex].text = this.todayText.replace('&', currentDate)
 | ||
|       }
 | ||
| 
 | ||
|       return dateData
 | ||
|     },
 | ||
|     $_generateHourData () {
 | ||
|       const args = this.$_getGeneratorArguments(toArray(arguments))
 | ||
|       let start, end
 | ||
| 
 | ||
|       if (this.$_isDateTimeEqual(this.minDate, args.Year, args.Month, args.Date)) {
 | ||
|         start = this.minDate.getHours()
 | ||
|       } else {
 | ||
|         start = 0
 | ||
|       }
 | ||
| 
 | ||
|       if (this.$_isDateTimeEqual(this.maxDate, args.Year, args.Month, args.Date)) {
 | ||
|           end = this.maxDate.getHours()
 | ||
|       } else {
 | ||
|         end = 23
 | ||
|       }
 | ||
| 
 | ||
|       /* istanbul ignore if */
 | ||
|       if (end < start) {
 | ||
|         end = 23
 | ||
|       }
 | ||
|       /* istanbul ignore if */
 | ||
|       if (start > end) {
 | ||
|         warn('MinDate Hour should be earlier than MaxDate')
 | ||
|         return
 | ||
|       }
 | ||
|   
 | ||
|       return this.$_generateData(start, end, 'Hour', this.unitText[3] || '', 1, arguments)
 | ||
|     },
 | ||
|     $_generateMinuteData () {
 | ||
|       const args = this.$_getGeneratorArguments(toArray(arguments))
 | ||
|       let start, end
 | ||
| 
 | ||
|       if (this.$_isDateTimeEqual(this.minDate, args.Year, args.Month, args.Date, args.Hour)) {
 | ||
|         start = this.minDate.getMinutes()
 | ||
|       } else {
 | ||
|         start = 0
 | ||
|       }
 | ||
| 
 | ||
|       if (this.$_isDateTimeEqual(this.maxDate, args.Year, args.Month, args.Date, args.Hour)) {
 | ||
|           end = this.maxDate.getMinutes()
 | ||
|       } else {
 | ||
|         end = 59
 | ||
|       }
 | ||
| 
 | ||
|       return this.$_generateData(start, end, 'Minute', this.unitText[4] || '', this.minuteStep, arguments)
 | ||
|     },
 | ||
|     $_generateData (from, to, type, unit, step = 1, args = []) {
 | ||
|       let count = from
 | ||
|       let text
 | ||
|       const data = []
 | ||
|       const defaultArgs = toArray(args).map(item => {
 | ||
|         return typeof item === 'object' ? item.value : item
 | ||
|       })
 | ||
| 
 | ||
|       while (count <= to) {
 | ||
|         this.textRender 
 | ||
|         && (text = this.textRender.apply(this, [
 | ||
|             TYPE_FORMAT_INVERSE[type],
 | ||
|             ...defaultArgs,
 | ||
|             count
 | ||
|            ]))
 | ||
|         data.push({
 | ||
|           text: text || `${count}${unit}`,
 | ||
|           value: count,
 | ||
|           typeFormat: TYPE_FORMAT_INVERSE[type] || type,
 | ||
|           type
 | ||
|         })
 | ||
|         count += step
 | ||
|       }
 | ||
| 
 | ||
|       return data
 | ||
|     },
 | ||
|     /**
 | ||
|      * Determine whether year, month, date, etc of 
 | ||
|      * the current date are equal to the given value
 | ||
|      * @params Date
 | ||
|      * @params year, month, date ...
 | ||
|      */
 | ||
|     $_isDateTimeEqual () {
 | ||
|       const methods = Object.keys(TYPE_METHODS).map(key => {
 | ||
|         return TYPE_METHODS[key]
 | ||
|       })
 | ||
|       const args = toArray(arguments)
 | ||
|       const date = args[0]
 | ||
| 
 | ||
|       let res = true
 | ||
|       if (!date) {
 | ||
|         return res = false
 | ||
|       }
 | ||
| 
 | ||
|       for (let i = 1; i < args.length; i++) {
 | ||
|         const methodName = methods[i - 1]
 | ||
|         const curVal = date[methodName]() + Number(methodName === 'getMonth')
 | ||
|         const targetVal = +args[i]
 | ||
| 
 | ||
|         if (curVal !== targetVal) {
 | ||
|           res = false
 | ||
|           break
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       return res
 | ||
|     },
 | ||
| 
 | ||
|     // MARK: events handler
 | ||
|     $_onPickerShow () {
 | ||
|       this.oldColumnData = [...this.columnData]
 | ||
|       this.$emit('show')
 | ||
|     },
 | ||
|     $_onPickerHide () {
 | ||
|       this.$emit('hide')
 | ||
|     },
 | ||
|     $_onPickerChange (columnIndex, itemIndex, value) {
 | ||
|       this.$emit('change', columnIndex, itemIndex, value)
 | ||
| 
 | ||
|       if (columnIndex < this.columnData.length - 1) {
 | ||
|         this.$_initColumnData(columnIndex + 1)
 | ||
|       }
 | ||
|     },
 | ||
|     $_onPickerConfirm (columnsValue) {
 | ||
|       this.$emit('confirm', columnsValue)
 | ||
|     },
 | ||
|     $_onPickerCancel () {
 | ||
|       this.$emit('cancel')
 | ||
|       if (this.oldColumnData) {
 | ||
|         this.columnData = [...this.oldColumnData]
 | ||
|       }
 | ||
|     },
 | ||
| 
 | ||
|     getFormatDate (format = 'yyyy-MM-dd hh:mm') {
 | ||
|       const columnValues = this.picker.getColumnValues()
 | ||
| 
 | ||
|       columnValues.forEach(item => {
 | ||
|         /* istanbul ignore if */
 | ||
|         if (!item) {
 | ||
|           return
 | ||
|         }
 | ||
| 
 | ||
|         let value = item.value
 | ||
| 
 | ||
|         if (value < 10) {
 | ||
|           value = '0' + value
 | ||
|         }
 | ||
| 
 | ||
|         format = format.replace(item.type, value)
 | ||
|         format = format.replace(TYPE_FORMAT_INVERSE[item.type], value)
 | ||
|       })
 | ||
| 
 | ||
|       return format
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| </script>
 | ||
| 
 | ||
| <style lang="stylus">
 | ||
| .md-date-picker
 | ||
|   .column-item
 | ||
|     font-size date-picker-font-size !important
 | ||
|     overflow visible !important
 | ||
|   // &.datetime .column-item
 | ||
|   //   font-size date-time-picker-font-size !important
 | ||
| </style>
 |