2
0
mirror of https://github.com/tenrok/vue2-datepicker.git synced 2026-06-11 09:12:27 +03:00

feat: add valueType to format binding value

This commit is contained in:
mengxiong10
2019-01-12 19:44:20 +08:00
parent 37759701fd
commit dd6f2eaa49
8 changed files with 233 additions and 71 deletions
+44 -27
View File
@@ -69,34 +69,51 @@ export default {
```
### Props
| Prop | Type | Default | Description |
|---------------------|---------------|-------------|-----------------------------------------------------|
| type | String | 'date' | select date type (date/datetime/year/month/time) |
| range | Boolean | false | if true, the type is daterange or datetimerange |
| format | String | YYYY-MM-DD | The parsing tokens are similar to the moment.js |
| lang | String/Object | zh | Translation ([custom](#lang))(en/zh/es/pt-br/fr/ru/de/it/cs) |
| clearable | Boolean | true | if false, don't show the clear icon |
| confirm | Boolean | false | if true, need click the button to change the value |
| editable | Boolean | true | if false, user cann't type it |
| disabled | Boolean | false | Disable the component |
| placeholder | String | | input placeholder text |
| width | String/Number | 210 | input size |
| append-to-body | Boolean | false | append the popup to body |
| popupStyle | Object | | popup style(override the top, left style) |
| not-before | String/Date | '' | Disable all dates before new Date(not-before) |
| not-after | String/Date | '' | Disable all dates after new Date(not-after) |
| disabled-days | Array/function| [] | Disable Days |
| shortcuts | Boolean/Array | true | the shortcuts for the range picker |
| time-picker-options | Object | {} | set timePickerOptions(start, step, end) |
| minute-step | Number | 0 | if > 0 don't show the second picker(0 - 60) |
| first-day-of-week | Number | 7 | set the first day of week (1-7) |
| input-class | String | 'mx-input' | the input class name |
| input-name | String | 'date' | the input name attr |
| input-attr | Object | | the input attr(eg: { required: true, id: 'input'}) |
| confirm-text | String | 'OK' | the default text to display on confirm button |
| range-separator | String | '~' | the range separator text |
| date-format | String | '' | format the time header and tooltip |
| Prop | Type | Accepted Values | Default | Description |
|---------------------|---------------|-----------------|-------------|-----------------------------------------------------|
| type | String | date/datetime/year/month/time | 'date' | select date type |
| range | Boolean | — | false | if true, the type is daterange or datetimerange |
| format | String | — | YYYY-MM-DD | The parsing tokens are similar to the moment.js |
| value-type | String/Object | date/format/timestamp | 'date' | type of binding value. If not specified, the binding value will be a Date object(see [detail](#value-type)) |
| lang | String/Object | en/zh/es/pt-br/fr/ru/de/it/cs | zh | Translation (set [how to custom](#lang)) |
| clearable | Boolean | | true | if false, don't show the clear icon |
| confirm | Boolean | | false | if true, need click the button to change the value |
| editable | Boolean | — | true | if false, user cann't type it |
| disabled | Boolean | — | false | Disable the component |
| placeholder | String | — | | input placeholder text |
| width | String/Number | — | 210 | input size |
| append-to-body | Boolean | | false | append the popup to body |
| popupStyle | Object | — | | popup style(override the top, left style) |
| not-before | String/Date | — | '' | Disable all dates before new Date(not-before) |
| not-after | String/Date | | '' | Disable all dates after new Date(not-after) |
| disabled-days | Array/function| — | [] | Disable Days |
| shortcuts | Boolean/Array | — | true | the shortcuts for the range picker |
| time-picker-options | Object | | {} | set timePickerOptions(start, step, end) |
| minute-step | Number | 0 - 60 | 0 | if > 0 don't show the second picker |
| first-day-of-week | Number | 1 - 7 | 7 | set the first day of week |
| input-class | String | — | 'mx-input' | the input class name |
| input-attr | Object | — | | the input attr(eg: { required: true, id: 'input'}) |
| confirm-text | String | — | 'OK' | the default text to display on confirm button |
| range-separator | String | — | '~' | the range separator text |
| date-format | String | — | '' | format the time header and tooltip |
#### value-type
set the format of binding value
| Value | Description |
|-----------------|-------------------------------------------|
| date | binding value will be a Date object |
| timestamp | binding value will be a timestamp number |
| format | binding value will be the format string |
Advanced: You can also customize objects to implement two functions.
```js
{
value2date: (value: any) => Date, // transform the binding value to calendar Date Object
date2value: (date: Date) => any // transform the calendar Date Object to binding value
}
```
#### lang
* String (en/zh/es/pt-br/fr/ru/de/it/cs)
+46 -26
View File
@@ -69,32 +69,52 @@ export default {
```
### Props
| Prop | Type | Default | Description
|---------------------|---------------|-------------|-----------------------------------------------------
| type | String | 'date' | 选择日期或日期时间(可选:date,datetime,year,month,time)
| range | Boolean | false | 如果是true, 显示日历范围选择
| format | String | YYYY-MM-DD | 格式化显示日期 api类似moment.js
| lang | String/Object | zh | 选择语言或自定义 (en/zh/es/pt-br/fr/ru/de/it/cs)(custom)
| clearable | Boolean | true | 如果设置false, 不显示清除图标
| confirm | Boolean | false | 如果是true, 显示确认按钮且需要确认才更新时间
| editable | Boolean | true | 如果是false, 用户不能手动输入更新日期
| disabled | Boolean | false | 禁用组件
| placeholder | String | | 输入框placeholder
| width | String/Number | 210 | 设置宽度
| append-to-body | Boolean | false | 弹出层放到body下面
| popup-style | Object | | 弹出层的样式(可以覆盖left,top样式)
| not-before | String/Date | '' | 禁止选择这个时间之前的时间
| not-after | String/Date | '' | 禁止选择这个时间之前=后的时间
| disabled-days | Array/function| [] | 自定义禁止的日期
| shortcuts | Boolean/Array | true | 自定义范围选择的时候快捷选项(见下表)
| time-picker-options | Object | {} | 自定义时间选择的开始,结束,步进(见下表)
| minute-step | Number | 0 | 设置分钟的步进, 设置大于0不显示秒的选择(0-60)
| first-day-of-week | Number | 7 | 设置日历星期几开头(1-7)
| input-class | String | 'mx-input' | 自定义输入框的类名
| input-name | String | 'date' | 自定义input 的 name 属性
| confirm-text | String | 'OK' | 确认按钮的名称
| range-separator | String | '~' | range 分隔符
| date-format | String | '' | 格式化时间组件头部和日历的tooltip,默认是format字段去除时间的格式化
| 属性 | 类型 | 可选值 | 默认值 | 描述
|---------------------|---------------| ---------------------- |-------------| -----------
| type | String | date,datetime,year,month,time | 'date' | 选择日期或日期时间
| range | Boolean | - | false | 如果是true, 显示日历范围选择
| format | String | - | YYYY-MM-DD | 格式化显示日期 api类似moment.js
| value-type | String/Object | date/format/timestamp | 'date' | 设置绑定值的格式([详情](#value-type)) |
| lang | String/Object | en/zh/es/pt-br/fr/ru/de/it/cs| zh | 选择语言或自定义 ([自定义](#lang))
| clearable | Boolean | - | true | 如果设置false, 不显示清除图标
| confirm | Boolean | - | false | 如果是true, 显示确认按钮且需要确认才更新时间
| editable | Boolean | - | true | 如果是false, 用户不能手动输入更新日期
| disabled | Boolean | - | false | 禁用组件
| placeholder | String | - | | 输入框placeholder
| width | String/Number | - | 210 | 设置宽度
| append-to-body | Boolean | - | false | 弹出层放到body下面
| popup-style | Object | - | | 弹出层的样式(可以覆盖left,top样式)
| not-before | String/Date | - | '' | 禁止选择这个时间之前的时间
| not-after | String/Date | - | '' | 禁止选择这个时间之前=后的时间
| disabled-days | Array/function| - | [] | 自定义禁止的日期
| shortcuts | Boolean/Array | - | true | 自定义范围选择的时候快捷选项(见下表)
| time-picker-options | Object | - | {} | 自定义时间选择的开始,结束,步进(见下表)
| minute-step | Number | 0 - 60 | 0 | 设置分钟的步进, 设置大于0不显示秒的选择(0-60)
| first-day-of-week | Number | 1 - 7 | 7 | 设置日历星期几开头
| input-class | String | - | 'mx-input' | 自定义input元素的类名
| input-attr | Object | — | | 自定义input元是的属性(eg: { required: true, id: 'input', name:'date'})
| confirm-text | String | - | 'OK' | 确认按钮的名称
| range-separator | String | - | '~' | range 分隔符
| date-format | String | - | '' | 格式化时间组件头部和日历的tooltip,默认是format字段去除时间的格式化
#### value-type
设置绑定值的格式
| 可选值 | 描述
|-----------------|---------------------------------------
| date | 返回的绑定值是Date对象
| timestamp | 返回的绑定值是时间戳数字
| format | 返回的绑定值是通过`format`属性格式化的值
高级: 也可以传入一个自定义实现包含2个函数的对象
```js
{
value2date: (value: any) => Date, // 转化绑定值到日历时间对象
date2value: (date: Date) => any // 转化日历时间对象到绑定值
}
```
#### lang
* String (en/zh/es/pt-br/fr/ru/de/it/cs)
+32 -17
View File
@@ -109,6 +109,7 @@
import fecha from 'fecha'
import clickoutside from '@/directives/clickoutside'
import { isValidDate, isValidRange, isDateObejct, isPlainObject, formatDate, parseDate, throttle } from '@/utils/index'
import { transformDate, transformDateRange } from '@/utils/transform'
import CalendarPanel from './calendar.vue'
import locale from '@/mixins/locale'
import Languages from '@/locale/languages'
@@ -123,6 +124,12 @@ export default {
},
props: {
value: null,
valueType: {
default: 'date',
validator: function (value) {
return ['timestamp', 'format', 'date'].indexOf(value) !== -1 || isPlainObject(value)
}
},
placeholder: {
type: String,
default: null
@@ -217,6 +224,14 @@ export default {
}
},
computed: {
transform () {
const obj = this.range ? transformDateRange : transformDate
const type = this.valueType
if (isPlainObject(type)) {
return { ...obj.date, ...type }
}
return obj[type] || obj.date
},
language () {
if (isPlainObject(this.lang)) {
return { ...Languages.en, ...this.lang }
@@ -233,11 +248,12 @@ export default {
if (this.userInput !== null) {
return this.userInput
}
const date = this.transform.value2date(this.value, this.format)
if (!this.range) {
return isValidDate(this.value) ? this.stringify(this.value) : ''
return date ? this.stringify(date) : ''
}
return isValidRange(this.value)
? `${this.stringify(this.value[0])} ${this.rangeSeparator} ${this.stringify(this.value[1])}`
return Array.isArray(date) && date[0] && date[1]
? `${this.stringify(date[0])} ${this.rangeSeparator} ${this.stringify(date[1])}`
: ''
},
computedWidth () {
@@ -264,28 +280,28 @@ export default {
{
text: pickers[0],
onClick (self) {
self.currentValue = [ new Date(), new Date(Date.now() + 3600 * 1000 * 24 * 7) ]
self.currentValue = [new Date(), new Date(Date.now() + 3600 * 1000 * 24 * 7)]
self.updateDate(true)
}
},
{
text: pickers[1],
onClick (self) {
self.currentValue = [ new Date(), new Date(Date.now() + 3600 * 1000 * 24 * 30) ]
self.currentValue = [new Date(), new Date(Date.now() + 3600 * 1000 * 24 * 30)]
self.updateDate(true)
}
},
{
text: pickers[2],
onClick (self) {
self.currentValue = [ new Date(Date.now() - 3600 * 1000 * 24 * 7), new Date() ]
self.currentValue = [new Date(Date.now() - 3600 * 1000 * 24 * 7), new Date()]
self.updateDate(true)
}
},
{
text: pickers[3],
onClick (self) {
self.currentValue = [ new Date(Date.now() - 3600 * 1000 * 24 * 30), new Date() ]
self.currentValue = [new Date(Date.now() - 3600 * 1000 * 24 * 30), new Date()]
self.updateDate(true)
}
}
@@ -346,7 +362,7 @@ export default {
if (typeof range.onClick === 'function') {
return range.onClick(this)
}
this.currentValue = [ new Date(range.start), new Date(range.end) ]
this.currentValue = [new Date(range.start), new Date(range.end)]
this.updateDate(true)
},
clearDate () {
@@ -360,7 +376,7 @@ export default {
if (valid) {
this.updateDate(true)
}
this.$emit('confirm', this.currentValue)
this.emitDate('confirm')
this.closePopup()
},
updateDate (confirm = false) {
@@ -371,16 +387,15 @@ export default {
if (equal) {
return false
}
this.$emit('input', this.currentValue)
this.$emit('change', this.currentValue)
this.emitDate('input')
this.emitDate('change')
return true
},
emitDate (eventName) {
this.$emit(eventName, this.transform.date2value(this.currentValue, this.format))
},
handleValueChange (value) {
if (!this.range) {
this.currentValue = isValidDate(value) ? new Date(value) : null
} else {
this.currentValue = isValidRange(value) ? [new Date(value[0]), new Date(value[1])] : [null, null]
}
this.currentValue = this.transform.value2date(value, this.format)
},
selectDate (date) {
this.currentValue = date
@@ -484,7 +499,7 @@ export default {
const start = this.parseDate(range[0], this.format)
const end = this.parseDate(range[1], this.format)
if (start && end && !checkDate(start, null, end) && !checkDate(end, start, null)) {
this.currentValue = [ start, end ]
this.currentValue = [start, end]
this.updateDate(true)
this.closePopup()
return
+1 -1
View File
@@ -66,7 +66,7 @@ export function parseDate (value, format) {
try {
return fecha.parse(value, format)
} catch (e) {
return false
return null
}
}
+40
View File
@@ -0,0 +1,40 @@
import { isValidDate, isValidRange, parseDate, formatDate } from './index'
export const transformDate = {
date: {
value2date: (value) => isValidDate(value) ? new Date(value) : null,
date2value: (date) => date
},
timestamp: {
value2date: (value) => isValidDate(value) ? new Date(value) : null,
date2value: (date) => isValidDate(date) ? new Date(date).getTime() : null
},
format: {
value2date: parseDate,
date2value: (date, format) => isValidDate(date) ? formatDate(date, format) : null
}
}
export const transformDateRange = {
date: {
value2date: (value) => isValidRange(value) ? [new Date(value[0]), new Date(value[1])] : [null, null],
date2value: (date) => date
},
timestamp: {
value2date: (value) => isValidRange(value) ? [new Date(value[0]), new Date(value[1])] : [null, null],
date2value: (date) => date.map(transformDate.timestamp.date2value)
},
format: {
value2date: (value, format) => {
if (Array.isArray(value) && value.length === 2) {
const value0 = parseDate(value[0], format)
const value1 = parseDate(value[1], format)
if (value0 && value1 && value1 >= value0) {
return [value0, value1]
}
}
return [null, null]
},
date2value: (date, format) => date.map(v => transformDate.format.date2value(v, format))
}
}
+30
View File
@@ -5,6 +5,7 @@ import CalendarPanel from '../src/calendar.vue'
import DatePanel from '../src/panel/date'
import TimePanel from '../src/panel/time'
import YearPanel from '../src/panel/year'
import { transformDate, transformDateRange } from '../src/utils/transform'
let wrapper
@@ -13,6 +14,35 @@ afterEach(() => {
})
describe('datepicker', () => {
it('prop: valueType', () => {
wrapper = mount(DatePicker, {
propsData: {
value: new Date(2018, 4, 2)
}
})
const vm = wrapper.vm
expect(vm.transform).toBe(transformDate.date)
wrapper.setProps({ valueType: 'timestamp' })
expect(vm.transform).toBe(transformDate.timestamp)
wrapper.setProps({ valueType: 'format' })
expect(vm.transform).toBe(transformDate.format)
wrapper.setProps({ valueType: 'date', range: true })
expect(vm.transform).toBe(transformDateRange.date)
wrapper.setProps({ valueType: 'timestamp' })
expect(vm.transform).toBe(transformDateRange.timestamp)
wrapper.setProps({ valueType: 'format' })
expect(vm.transform).toBe(transformDateRange.format)
const fn = (date) => date
wrapper.setProps({ valueType: {
date2value: fn,
value2date: fn
}})
expect(vm.transform).toHaveProperty('date2value', fn)
expect(vm.transform).toHaveProperty('value2date', fn)
})
it('prop: inputAttr', () => {
wrapper = mount(DatePicker, {
propsData: {
+11
View File
@@ -0,0 +1,11 @@
import lang from '../src/locale/languages'
const testLang = (key) => it(key, () => {
expect(lang[key].days).toHaveLength(7)
expect(lang[key].months).toHaveLength(12)
expect(lang[key].pickers).toHaveLength(4)
})
describe('transformDate', () => {
Object.keys(lang).forEach(key => testLang(key))
})
+29
View File
@@ -0,0 +1,29 @@
import { transformDate, transformDateRange } from '../src/utils/transform'
const time = new Date(2019, 1, 3)
const timestamp = time.getTime()
const format = 'MM-DD-YYYY'
const text = '02-03-2019'
const testfn = ({ type, value, date, err = null, range = false }) => it(`${type}}`, () => {
const obj = range ? transformDateRange : transformDate
const typeObj = obj[type]
expect(typeObj.value2date(err, format)).toEqual(err)
expect(typeObj.value2date(value, format)).toEqual(date)
expect(typeObj.date2value(err, format)).toEqual(err)
expect(typeObj.date2value(date, format)).toEqual(value)
})
describe('transformDate', () => {
testfn({ type: 'date', value: time, date: time })
testfn({ type: 'format', value: text, date: time })
testfn({ type: 'timestamp', value: timestamp, date: time })
})
describe('transformDateRange', () => {
const err = [null, null]
const date = [time, time]
testfn({ type: 'date', value: [time, time], date, err, range: true })
testfn({ type: 'format', value: [text, text], date, err, range: true })
testfn({ type: 'timestamp', value: [timestamp, timestamp], date, err, range: true })
})