mirror of
https://github.com/tenrok/vue2-datepicker.git
synced 2026-05-17 04:09:40 +03:00
refactor: 3.0
This commit is contained in:
@@ -1,26 +1,29 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"modules": false
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"transform-vue-jsx",
|
||||
"jsx-v-model"
|
||||
"transform-vue-jsx"
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": [
|
||||
["env", { "target": { "node": "current" }}],
|
||||
"stage-3"
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"transform-vue-jsx",
|
||||
"jsx-v-model"
|
||||
"transform-vue-jsx"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+6
-7
@@ -1,8 +1,7 @@
|
||||
/build/
|
||||
/config/
|
||||
/dist/
|
||||
/lib/
|
||||
/coverage/
|
||||
/*.js
|
||||
|
||||
dist
|
||||
lib
|
||||
es
|
||||
node_modules
|
||||
/src/index.scss
|
||||
/locale
|
||||
/index.*
|
||||
|
||||
+25
-35
@@ -1,47 +1,37 @@
|
||||
// https://eslint.org/docs/user-guide/configuring
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint'
|
||||
parser: 'babel-eslint',
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
jest: true
|
||||
jest: true,
|
||||
es6: true,
|
||||
},
|
||||
extends: [
|
||||
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
|
||||
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
|
||||
'plugin:vue/essential',
|
||||
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
|
||||
'standard'
|
||||
],
|
||||
// required to lint *.vue files
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
node: {
|
||||
extensions: ['.js', '.jsx', '.vue'],
|
||||
},
|
||||
},
|
||||
'import/extensions': ['.js', '.jsx', '.vue'],
|
||||
},
|
||||
extends: ['airbnb-base', 'plugin:vue/recommended', 'prettier', 'prettier/vue'],
|
||||
|
||||
plugins: ['vue'],
|
||||
// add your custom rules here
|
||||
|
||||
rules: {
|
||||
// allow async-await
|
||||
'generator-star-spacing': 'off',
|
||||
// allow debugger during development
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'no-console': ['error', { allow: ['warn', 'error'] }],
|
||||
camelcase: ['off', { properties: 'never' }],
|
||||
// "vue/max-attributes-per-line": [2, {
|
||||
// "singleline": 1,
|
||||
// "multiline": {
|
||||
// "max": 1,
|
||||
// "allowFirstLine": true
|
||||
// }
|
||||
// }],
|
||||
'vue/html-indent': [
|
||||
'error',
|
||||
2,
|
||||
{
|
||||
attribute: 1,
|
||||
closeBracket: 0,
|
||||
alignAttributesVertically: false,
|
||||
ignores: []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
'no-plusplus': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-restricted-globals': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'import/no-unresolved': [2, { ignore: ['vue2-datepicker'] }],
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/require-prop-types': 'off',
|
||||
'vue/no-v-html': 'off',
|
||||
},
|
||||
};
|
||||
|
||||
+11
-6
@@ -1,7 +1,12 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
lib
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
coverage
|
||||
.vscode
|
||||
.cache
|
||||
|
||||
_site
|
||||
|
||||
node_modules
|
||||
|
||||
/dist
|
||||
|
||||
/locale
|
||||
|
||||
/index.*
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||
|
||||
module.exports = {
|
||||
"plugins": {
|
||||
"autoprefixer": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100
|
||||
}
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
language: node_js
|
||||
node_js: 10.16.3
|
||||
script: "npm run test:push"
|
||||
script: 'npm test'
|
||||
after_script: npm run cov
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
</a>
|
||||
|
||||
## Demo
|
||||
|
||||
<https://mengxiong10.github.io/vue2-datepicker/demo/index.html>
|
||||
|
||||

|
||||
@@ -32,178 +33,185 @@ $ npm install vue2-datepicker --save
|
||||
|
||||
```html
|
||||
<script>
|
||||
import DatePicker from 'vue2-datepicker'
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import 'vue2-datepicker/index.css';
|
||||
|
||||
export default {
|
||||
components: { DatePicker },
|
||||
data() {
|
||||
return {
|
||||
time1: '',
|
||||
time2: '',
|
||||
time3: '',
|
||||
// custom lang
|
||||
lang: {
|
||||
days: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||
pickers: ['next 7 days', 'next 30 days', 'previous 7 days', 'previous 30 days'],
|
||||
placeholder: {
|
||||
date: 'Select Date',
|
||||
dateRange: 'Select Date Range'
|
||||
}
|
||||
time1: null,
|
||||
time2: null,
|
||||
time3: null,
|
||||
};
|
||||
},
|
||||
// custom range shortcuts
|
||||
shortcuts: [
|
||||
{
|
||||
text: 'Today',
|
||||
onClick: () => {
|
||||
this.time3 = [ new Date(), new Date() ]
|
||||
}
|
||||
}
|
||||
],
|
||||
timePickerOptions:{
|
||||
start: '00:00',
|
||||
step: '00:30',
|
||||
end: '23:30'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<date-picker v-model="time1" valueType="format" :first-day-of-week="1"></date-picker>
|
||||
<date-picker v-model="time2" type="datetime" :time-picker-options="timePickerOptions"></date-picker>
|
||||
<date-picker v-model="time3" range :shortcuts="shortcuts"></date-picker>
|
||||
<date-picker v-model="value" :lang="lang"></date-picker>
|
||||
<date-picker v-model="time1" valueType="format"></date-picker>
|
||||
<date-picker v-model="time2" type="datetime"></date-picker>
|
||||
<date-picker v-model="time3" range></date-picker>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Props
|
||||
|
||||
| Prop | Description | Type | Default |
|
||||
|------|--------------|-------|---------|
|
||||
| type | select date type | 'date' \| 'datetime' \| 'year' \| 'month' \| 'time' | 'date' |
|
||||
| range | if true, the type is daterange or datetimerange | `boolean` | false |
|
||||
| format | format the Date. The parsing tokens are similar to the moment.js | [token](https://github.com/taylorhakes/fecha#formatting-tokens) \| [`object`](https://github.com/mengxiong10/vue2-datepicker/issues/232#issuecomment-458558141) | 'YYYY-MM-DD' |
|
||||
| value-type | type of binding value. If not specified, the binding value will be a Date object | [value-type](#value-type) | 'date' |
|
||||
| lang | Translation | [lang](#lang) | 'zh' |
|
||||
| clearable | if false, don't show the clear icon | `boolean` | true |
|
||||
| confirm | if true, need click the button to change the value | `boolean` | false |
|
||||
| editable | if false, user cann't type it | `boolean` | true |
|
||||
| disabled | Disable the component | `boolean` | false |
|
||||
| placeholder | input placeholder text | `string` | — |
|
||||
| width | input size | `string`\|`number` | 210 |
|
||||
| append-to-body | append the popup to body | `boolean` | false |
|
||||
| ------------------- | ---------------------------------------------- | ------------------------------------------- | -------------- |
|
||||
| type | select the type of picker | date \|datetime\|year\|month\|time\|week | 'date' |
|
||||
| range | if true, pick the range date | `boolean` | false |
|
||||
| format | to set the date format. similar to moment.js | [token](#token) | 'YYYY-MM-DD' |
|
||||
| value-type | data type of the binding value | [value-type](#value-type) | 'date' |
|
||||
| default-value | default date of the calendar | `Date` | new Date() |
|
||||
| popupStyle | popup style(override the top, left style) | `object` | — |
|
||||
| not-before | Disable all dates before new Date(not-before) | `string`\|`Date` | ''|
|
||||
| not-after | Disable all dates after new Date(not-after) | `string`\|`Date`| '' |
|
||||
| disabled-days | Disable Days | `(date) => boolean` | - |
|
||||
| shortcuts | the shortcuts for the range picker | [shortcuts](#shortcuts) | true |
|
||||
| time-picker-options | custom time-picker | [time-picker-options](#time-picker-options) | null |
|
||||
| time-select-options | custom time-select | [time-select-options](#time-select-options) | null |
|
||||
| minute-step | if > 0 don't show the second picker | 0 - 60 | 0 |
|
||||
| first-day-of-week | set the first day of week | 1 - 7 | 7 |
|
||||
| input-class | the input class name | `string` | 'mx-input' |
|
||||
| input-attr | the input attr(eg: { required: true, id: 'input'}) | `object` | — |
|
||||
| confirm-text | the default text to display on confirm button | `string` | 'OK' |
|
||||
| range-separator | the range separator text | `string` | '~' |
|
||||
| date-format | format the time header and tooltip | `string` | '' |
|
||||
| icon-day | set the number of calendar icon | `string`\|`number` | '' |
|
||||
| lang | override the default locale | `object` | |
|
||||
| placeholder | input placeholder text | `string` | '' |
|
||||
| editable | whether the input is editable | `boolean` | true |
|
||||
| clearable | if false, don't show the clear icon | `boolean` | true |
|
||||
| confirm | if true, need click the button to change value | `boolean` | false |
|
||||
| confirm-text | the text of confirm button | `string` | 'OK' |
|
||||
| disabled | disable the component | `boolean` | false |
|
||||
| disabled-date | specify the date that cannot be selected | `(date) => boolean` | - |
|
||||
| disabled-time | specify the time that cannot be selected | `(date) => boolean` | - |
|
||||
| append-to-body | append the popup to body | `boolean` | true |
|
||||
| inline | without input | `boolean` | false |
|
||||
| input-class | input classname | `string` | 'mx-input' |
|
||||
| input-attr | input attrs(eg: { name: 'date', id: 'foo'}) | `object` | — |
|
||||
| open | open state of picker | `boolean` | - |
|
||||
| popupStyle | popup style | `object` | — |
|
||||
| popupClass | popup classes | | — |
|
||||
| shortcuts | set shortcuts to select | `Array<{text, onClick}>` | - |
|
||||
| title-format | format of the tooltip in calendar cell | [token](#token) | 'YYYY-MM-DD' |
|
||||
| range-separator | text of range separator | `string` | ' ~ ' |
|
||||
| show-week-number | determine whether show week number | `boolean` | false |
|
||||
| hour-step | interval between hours in time picker | 1 - 60 | 1 |
|
||||
| minute-step | interval between minutes in time picker | 1 - 60 | 1 |
|
||||
| second-step | interval between seconds in time picker | 1 - 60 | 1 |
|
||||
| hour-options | custom hour column | `Array<number>` | - |
|
||||
| minute-options | custom minute column | `Array<number>` | - |
|
||||
| second-options | custom second column | `Array<number>` | - |
|
||||
| show-hour | whether show hour column | `boolean` | base on format |
|
||||
| show-minute | whether show minute column | `boolean` | base on format |
|
||||
| show-second | whether show second column | `boolean` | base on format |
|
||||
| use12h | whether show ampm column | `boolean` | base on format |
|
||||
| show-time-header | whether show header of time picker | `boolean` | false |
|
||||
| time-title-format | format of the time header | [token](#token) | 'YYYY-MM-DD' |
|
||||
| time-picker-options | set fixed time list to select | [time-picker-options](#time-picker-options) | null |
|
||||
|
||||
#### Token
|
||||
|
||||
| Uint | Token | output |
|
||||
| -------------------------- | ----- | -------------------------------------- |
|
||||
| Year | YY | 70 71 ... 10 11 |
|
||||
| | YYYY | 1970 1971 ... 2010 2011 |
|
||||
| Month | M | 1 2 ... 11 12 |
|
||||
| | MM | 01 02 ... 11 12 |
|
||||
| | MMM | Jan Feb ... Nov Dec |
|
||||
| | MMMM | January February ... November December |
|
||||
| Day of Month | D | 1 2 ... 30 31 |
|
||||
| | DD | 01 02 ... 30 31 |
|
||||
| Day of Week | d | 0 1 ... 5 6 |
|
||||
| | dd | Su Mo ... Fr Sa |
|
||||
| | ddd | Sun Mon ... Fri Sat |
|
||||
| | dddd | Sunday Monday ... Friday Saturday |
|
||||
| AM/PM | A | AM PM |
|
||||
| | a | am pm |
|
||||
| Hour | H | 0 1 ... 22 23 |
|
||||
| | HH | 00 01 ... 22 23 |
|
||||
| | h | 1 2 ... 12 |
|
||||
| | hh | 01 02 ... 12 |
|
||||
| Minute | m | 0 1 ... 58 59 |
|
||||
| | mm | 00 01 ... 58 59 |
|
||||
| Second | s | 0 1 ... 58 59 |
|
||||
| | ss | 00 01 ... 58 59 |
|
||||
| Fractional Second | S | 0 1 ... 8 9 |
|
||||
| | SS | 00 01 ... 98 99 |
|
||||
| | SSS | 000 001 ... 998 999 |
|
||||
| Time Zone | Z | -07:00 -06:00 ... +06:00 +07:00 |
|
||||
| | ZZ | -0700 -0600 ... +0600 +0700 |
|
||||
| Week of Year | w | 1 2 ... 52 53 |
|
||||
| | ww | 01 02 ... 52 53 |
|
||||
| Unix Timestamp | X | 1360013296 |
|
||||
| Unix Millisecond Timestamp | x | 1360013296123 |
|
||||
|
||||
#### 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
|
||||
|
||||
| Type |
|
||||
|------|
|
||||
| 'en'\|'zh'\|'es'\|'pt-br'\|'fr'\|'ru'\|'de'\|'it'\|'cs' |
|
||||
| { days: string[]; months: string[]; picker: string[]; placeholder: { date: string; dateRange: string } } |
|
||||
| ----------------- | ---------------------------------------------------- |
|
||||
| 'date' | return a Date object |
|
||||
| 'timestamp' | return a timestamp number |
|
||||
| 'format' | returns a string formatted using pattern of `format` |
|
||||
| token(MM/DD/YYYY) | returns a string formatted using this pattern |
|
||||
|
||||
#### shortcuts
|
||||
|
||||
the shortcuts for the range picker
|
||||
|
||||
| Value | Description |
|
||||
|-----------------|-------------|
|
||||
| true | show the default shortcuts |
|
||||
| false | hide the defaualt shortcuts |
|
||||
| [{text: string, onClick: () => any }] | custom shortcuts |
|
||||
```js
|
||||
[
|
||||
{ text: 'today', onClick: () => new Date() },
|
||||
{
|
||||
text: 'Yesterday',
|
||||
onClick: () => {
|
||||
const date = new Date();
|
||||
date.setTime(date.getTime() - 3600 * 1000 * 24);
|
||||
return date;
|
||||
},
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
| Attribute | Description |
|
||||
| --------- | ----------------------------------------- |
|
||||
| text | title of the shortcut |
|
||||
| onClick | callback function , need to return a Date |
|
||||
|
||||
#### time-picker-options
|
||||
custom time-picker
|
||||
|
||||
| Type |
|
||||
|------|
|
||||
| {start: '00:00', step:'00:30' , end: '23:30'} |
|
||||
| () => Array<{ label: string; values: { hours: number; minutes: number } }> |
|
||||
set fixed time list to select;
|
||||
|
||||
#### time-select-options
|
||||
custom time-select for columns
|
||||
```js
|
||||
{start: '00:00', step:'00:30' , end: '23:30'}
|
||||
```
|
||||
|
||||
| Type |
|
||||
|------|
|
||||
| {hours: [9, 10, 11], minutes: [10, 20], seconds: [10, 20] } |
|
||||
| Attribute | Description |
|
||||
| --------- | ----------- |
|
||||
| start | start time |
|
||||
| step | step time |
|
||||
| end | end time |
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Description | Callback Arguments |
|
||||
|-----------------|--------------------------------------------------------|------------------------|
|
||||
| ----------- | ------------------------------------ | ---------------------- |
|
||||
| input | When the value change(v-model event) | the currentValue |
|
||||
| change | When the value change(same as input) | the currentValue |
|
||||
| change | When the value change(same as input) | the currentValue, type |
|
||||
| open | When panel opening | |
|
||||
| close | When panel closing | |
|
||||
| confirm | When click 'confirm' button | the currentValue |
|
||||
| clear | When click 'clear' button | |
|
||||
| input-error | When user type a invalid Date | the input text |
|
||||
| panel-change | When change the panel view(eg: from year to month view)| [panel](#panel), [oldPanel](#panel) |
|
||||
| calendar-change | When calendar view year or month change | now(Date), oldNow(Date)|
|
||||
| focus | When input focus | |
|
||||
| blur | When input blur | |
|
||||
|
||||
#### panel
|
||||
|
||||
| Value | Description |
|
||||
|-------|----------------------|
|
||||
| NONE | when panel is closed |
|
||||
| DATE | when panel is date |
|
||||
| YEAR | when panel is year |
|
||||
| MONTH | when panel is month |
|
||||
| TIME | when panel is time |
|
||||
|
||||
### Slots
|
||||
|
||||
| Name | Description |
|
||||
|-----------------|--------------------------|
|
||||
| calendar-icon | custom the calender icon |
|
||||
| ------------- | ------------------------ |
|
||||
| icon-calendar | custom the calender icon |
|
||||
| icon-clear | custom the clear icon |
|
||||
| header | popup header |
|
||||
| footer | popup footer |
|
||||
| sidebar | popup sidebar |
|
||||
|
||||
## ChangeLog
|
||||
|
||||
[CHANGELOG](CHANGELOG.md)
|
||||
|
||||
|
||||
## Donate
|
||||
|
||||
If you find this project useful, you can buy author a glass of juice
|
||||
|
||||
[PayPal](https://www.paypal.me/mengxiong10) |
|
||||
[AliPay](https://user-images.githubusercontent.com/14135808/57742967-be1ac000-76f5-11e9-9607-c0854e0fdd11.png) |
|
||||
[WeChat](https://user-images.githubusercontent.com/14135808/57743255-e2c36780-76f6-11e9-8bb8-7720a2607dc1.png)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/mengxiong10/vue2-datepicker/blob/master/LICENSE)
|
||||
|
||||
+143
-134
@@ -18,6 +18,7 @@
|
||||
</a>
|
||||
|
||||
## 线上 Demo
|
||||
|
||||
<https://mengxiong10.github.io/vue2-datepicker/demo/index.html>
|
||||
|
||||

|
||||
@@ -28,182 +29,190 @@
|
||||
$ npm install vue2-datepicker --save
|
||||
```
|
||||
|
||||
## 用法
|
||||
## Usage
|
||||
|
||||
```html
|
||||
<script>
|
||||
import DatePicker from 'vue2-datepicker'
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import 'vue2-datepicker/index.css';
|
||||
|
||||
export default {
|
||||
components: { DatePicker },
|
||||
data() {
|
||||
return {
|
||||
time1: '',
|
||||
time2: '',
|
||||
time3: '',
|
||||
// custom lang
|
||||
lang: {
|
||||
days: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||
pickers: ['next 7 days', 'next 30 days', 'previous 7 days', 'previous 30 days'],
|
||||
placeholder: {
|
||||
date: 'Select Date',
|
||||
dateRange: 'Select Date Range'
|
||||
}
|
||||
time1: null,
|
||||
time2: null,
|
||||
time3: null,
|
||||
};
|
||||
},
|
||||
// custom range shortcuts
|
||||
shortcuts: [
|
||||
{
|
||||
text: 'Today',
|
||||
onClick: () => {
|
||||
this.time3 = [ new Date(), new Date() ]
|
||||
}
|
||||
}
|
||||
],
|
||||
timePickerOptions:{
|
||||
start: '00:00',
|
||||
step: '00:30',
|
||||
end: '23:30'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<date-picker v-model="time1" valueType="format" :first-day-of-week="1"></date-picker>
|
||||
<date-picker v-model="time2" type="datetime" :time-picker-options="timePickerOptions"></date-picker>
|
||||
<date-picker v-model="time3" range :shortcuts="shortcuts"></date-picker>
|
||||
<date-picker v-model="value" :lang="lang"></date-picker>
|
||||
<date-picker v-model="time1" valueType="format"></date-picker>
|
||||
<date-picker v-model="time2" type="datetime"></date-picker>
|
||||
<date-picker v-model="time3" range></date-picker>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
### 属性
|
||||
|
||||
### Props
|
||||
|
||||
| 属性 | 描述 | 类型 | 默认值 |
|
||||
|------|--------------|-------|---------|
|
||||
| type | 选择日期类型 | 'date' \| 'datetime' \| 'year' \| 'month' \| 'time' | 'date' |
|
||||
| range | 如果是true, 显示日历范围选择 | `boolean` | false |
|
||||
| format | 格式化显示日期, 值类似moment.js | [token](https://github.com/taylorhakes/fecha#formatting-tokens) \| [`object`](https://github.com/mengxiong10/vue2-datepicker/issues/232#issuecomment-458558141) | 'YYYY-MM-DD' |
|
||||
| value-type | 设置绑定值的格式, 默认返回日期对象 | [value-type](#value-type) | 'date' |
|
||||
| lang | 选择语言或自定义 | [lang](#lang) | 'zh' |
|
||||
| clearable | 如果设置false, 不显示清除图标 | `boolean` | true |
|
||||
| confirm | 如果是true, 显示确认按钮且需要确认才更新时间 | `boolean` | false |
|
||||
| editable | 如果是false, 用户不能手动输入更新日期 | `boolean` | true |
|
||||
| ------------------- | ------------------------------------------------ | ------------------------------------------- | -------------- |
|
||||
| type | 日期选择的类型 | date \|datetime\|year\|month\|time\|week | 'date' |
|
||||
| range | 如果是 true, 变成日期范围选择 | `boolean` | false |
|
||||
| format | 设置格式化的 token, 类似 moment.js | [token](#token) | 'YYYY-MM-DD' |
|
||||
| value-type | 设置绑定值的类型 | [value-type](#value-type) | 'date' |
|
||||
| default-value | 设置日历默认的时间 | `Date` | new Date() |
|
||||
| lang | 覆盖默认的语音设置 | `object` | |
|
||||
| placeholder | 输入框的 placeholder | `string` | '' |
|
||||
| editable | 输入框是否可以输入 | `boolean` | true |
|
||||
| clearable | 是否显示清除按钮 | `boolean` | true |
|
||||
| confirm | 是否需要确认 | `boolean` | false |
|
||||
| confirm-text | 确认按钮的文字 | `string` | 'OK' |
|
||||
| disabled | 禁用组件 | `boolean` | false |
|
||||
| placeholder | 输入框placeholder | `string` | — |
|
||||
| width | 设置宽度 | `string`\|`number` | 210 |
|
||||
| append-to-body | 弹出层元素插入body下面 | `boolean` | false |
|
||||
| default-value | 日历的默认值 | `Date` | new Date() |
|
||||
| popupStyle | 弹出层的样式(可以覆盖left,top样式) | `object` | — |
|
||||
| not-before | 禁止选择这个时间之前的时间 | `string`\|`Date` | ''|
|
||||
| not-after | 禁止选择这个时间之前=后的时间 | `string`\|`Date`| '' |
|
||||
| disabled-days | 自定义禁止的日期 | `(date) => boolean` | - |
|
||||
| shortcuts | 自定义范围选择的时候快捷选项 | [shortcuts](#shortcuts) | true |
|
||||
| time-picker-options | 自定义时间选择的开始,结束,步进 | [time-picker-options](#time-picker-options) | null |
|
||||
| time-select-options | 自定义时间列的选择 | [time-select-options](#time-select-options) | null |
|
||||
| minute-step | 设置分钟的步进, 设置大于0不显示秒的选择(0-60) | 0 - 60 | 0 |
|
||||
| first-day-of-week | 设置日历星期几开头 | 1 - 7 | 7 |
|
||||
| input-class | 自定义input元素的类名 | `string` | 'mx-input' |
|
||||
| input-attr | 自定义input元素的属性(eg: { required: true, id: 'input'}) | `object` | — |
|
||||
| confirm-text | 确认按钮的名称 | `string` | 'OK' |
|
||||
| range-separator | range 分隔符 | `string` | '~' |
|
||||
| date-format | 格式化时间组件头部和日历的tooltip,默认是format字段去除时间的格式化 | `string` | '' |
|
||||
| icon-day | 设置日历图标的数字 | `string`\|`number` | '' |
|
||||
| disabled-date | 禁止选择的日期 | `(date) => boolean` | - |
|
||||
| disabled-time | 禁止选择的时间 | `(date) => boolean` | - |
|
||||
| append-to-body | 弹出层插入到 body 元素下 | `boolean` | true |
|
||||
| inline | 不显示输入框 | `boolean` | false |
|
||||
| input-class | 输入框的类 | `string` | 'mx-input' |
|
||||
| input-attr | 输入框的其他属性(eg: { name: 'date', id: 'foo'}) | `object` | — |
|
||||
| open | 控制弹出层的显示 | `boolean` | - |
|
||||
| popupStyle | 弹出层的样式 | `object` | — |
|
||||
| popupClass | 弹出层的类 | | — |
|
||||
| shortcuts | 设置快捷选择 | `Array<{text, onClick}>` | - |
|
||||
| title-format | 日历单元格的 tooltip | [token](#token) | 'YYYY-MM-DD' |
|
||||
| range-separator | 范围分隔符 | `string` | ' ~ ' |
|
||||
| show-week-number | 是否显示星期数字 | `boolean` | false |
|
||||
| hour-step | 小时列的间隔 | 1 - 60 | 1 |
|
||||
| minute-step | 分钟列的间隔 | 1 - 60 | 1 |
|
||||
| second-step | 秒列的间隔 | 1 - 60 | 1 |
|
||||
| hour-options | 自定义小时列 | `Array<number>` | - |
|
||||
| minute-options | 自定义分钟列 | `Array<number>` | - |
|
||||
| second-options | 自定义秒列 | `Array<number>` | - |
|
||||
| show-hour | 是否显示小时列 | `boolean` | base on format |
|
||||
| show-minute | 是否显示分钟列 | `boolean` | base on format |
|
||||
| show-second | 是否显示秒列 | `boolean` | base on format |
|
||||
| use12h | 是否显示 ampm 列 | `boolean` | base on format |
|
||||
| show-time-header | 是否显示时间选择面板的头部 | `boolean` | false |
|
||||
| time-title-format | 时间面板头部的格式化 | [token](#token) | 'YYYY-MM-DD' |
|
||||
| time-picker-options | 设置固定时间去选择 | [time-picker-options](#time-picker-options) | null |
|
||||
|
||||
#### Token
|
||||
|
||||
| 单元 | 符号 | 输入 |
|
||||
| -------------------------- | ---- | -------------------------------------- |
|
||||
| Year | YY | 70 71 ... 10 11 |
|
||||
| | YYYY | 1970 1971 ... 2010 2011 |
|
||||
| Month | M | 1 2 ... 11 12 |
|
||||
| | MM | 01 02 ... 11 12 |
|
||||
| | MMM | Jan Feb ... Nov Dec |
|
||||
| | MMMM | January February ... November December |
|
||||
| Day of Month | D | 1 2 ... 30 31 |
|
||||
| | DD | 01 02 ... 30 31 |
|
||||
| Day of Week | d | 0 1 ... 5 6 |
|
||||
| | dd | Su Mo ... Fr Sa |
|
||||
| | ddd | Sun Mon ... Fri Sat |
|
||||
| | dddd | Sunday Monday ... Friday Saturday |
|
||||
| AM/PM | A | AM PM |
|
||||
| | a | am pm |
|
||||
| Hour | H | 0 1 ... 22 23 |
|
||||
| | HH | 00 01 ... 22 23 |
|
||||
| | h | 1 2 ... 12 |
|
||||
| | hh | 01 02 ... 12 |
|
||||
| Minute | m | 0 1 ... 58 59 |
|
||||
| | mm | 00 01 ... 58 59 |
|
||||
| Second | s | 0 1 ... 58 59 |
|
||||
| | ss | 00 01 ... 58 59 |
|
||||
| Fractional Second | S | 0 1 ... 8 9 |
|
||||
| | SS | 00 01 ... 98 99 |
|
||||
| | SSS | 000 001 ... 998 999 |
|
||||
| Time Zone | Z | -07:00 -06:00 ... +06:00 +07:00 |
|
||||
| | ZZ | -0700 -0600 ... +0600 +0700 |
|
||||
| Week of Year | w | 1 2 ... 52 53 |
|
||||
| | ww | 01 02 ... 52 53 |
|
||||
| Unix Timestamp | X | 1360013296 |
|
||||
| Unix Millisecond Timestamp | x | 1360013296123 |
|
||||
|
||||
#### value-type
|
||||
设置绑定值的格式
|
||||
|
||||
设置绑定值的类型
|
||||
|
||||
| 可选值 | 描述 |
|
||||
|-----------------|-------------------------------------------|
|
||||
| date | 返回的绑定值是Date对象 |
|
||||
| timestamp | 返回的绑定值是时间戳数字 |
|
||||
| format | 返回的绑定值是通过`format`属性格式化的值 |
|
||||
|
||||
高级: 也可以传入一个自定义实现包含2个函数的对象
|
||||
```js
|
||||
{
|
||||
value2date: (value: any) => Date, // 转化绑定值到日历时间对象
|
||||
date2value: (date: Date) => any // 转化日历时间对象到绑定值
|
||||
}
|
||||
|
||||
```
|
||||
#### lang
|
||||
|
||||
| 可选值 |
|
||||
|------|
|
||||
| 'en'\|'zh'\|'es'\|'pt-br'\|'fr'\|'ru'\|'de'\|'it'\|'cs' |
|
||||
| { days: string[]; months: string[]; picker: string[]; placeholder: { date: string; dateRange: string } } |
|
||||
| ----------------- | ------------------------------------ |
|
||||
| 'date' | 返回一个日期对象 |
|
||||
| 'timestamp' | 返回一个时间戳 |
|
||||
| 'format' | 返回一个用 format 字段格式化的字符串 |
|
||||
| token(MM/DD/YYYY) | 返回一个用这个字段格式化的字符串 |
|
||||
|
||||
#### shortcuts
|
||||
|
||||
| 可选值 | 描述 |
|
||||
|-----------------|-------------|
|
||||
| true | 显示默认快捷选择 |
|
||||
| false | 隐藏默认快捷选择 |
|
||||
| [{text: string, onClick: () => any }] | 自定义快捷选择 |
|
||||
设置快捷选择方式
|
||||
|
||||
```js
|
||||
[
|
||||
{ text: 'today', onClick: () => new Date() },
|
||||
{
|
||||
text: 'Yesterday',
|
||||
onClick: () => {
|
||||
const date = new Date();
|
||||
date.setTime(date.getTime() - 3600 * 1000 * 24);
|
||||
return date;
|
||||
},
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
| 属性 | 描述 |
|
||||
| ------- | --------------------------------- |
|
||||
| text | 显示的名称 |
|
||||
| onClick | 回调函数, 需要返回一个 Date 对象 |
|
||||
|
||||
#### time-picker-options
|
||||
自定义时间选择
|
||||
|
||||
| 可选值 |
|
||||
|------|
|
||||
| {start: '00:00', step:'00:30' , end: '23:30'} |
|
||||
| () => Array<{ label: string; values: { hours: number; minutes: number } }> |
|
||||
设置固定时间用于选择
|
||||
|
||||
#### time-select-options
|
||||
自定义时间选择列
|
||||
```js
|
||||
{start: '00:00', step:'00:30' , end: '23:30'}
|
||||
```
|
||||
|
||||
| 可选值 |
|
||||
|------|
|
||||
| {hours: [9, 10, 11], minutes: [10, 20], seconds: [10, 20] } |
|
||||
| 属性 | 描述 |
|
||||
| ----- | -------- |
|
||||
| start | 开始时间 |
|
||||
| step | 间隔时间 |
|
||||
| end | 结束时间 |
|
||||
|
||||
### 事件
|
||||
| Name | 说明 | 回调参数
|
||||
|-----------------|----------------------------- |----------------
|
||||
| change | 日期改变的时候触发 | 选择的日期
|
||||
| input | 日期改变的时候触发 | 选择的日期
|
||||
| confirm | 点击确认按钮触发的事件 | 选择的日期
|
||||
| clear | 清空时候触发 |
|
||||
| input-error | 当用户输入的值无效时候触发 | 用户输入的字符串
|
||||
| panel-change | 切换面板时触发 | [panel](#panel), [oldPanel](#panel)
|
||||
| calendar-change | 日历的年或月改变时触发 | 当前日历时间,过去日历时间
|
||||
| focus | 当输入框获得焦点 |
|
||||
| blur | 当输入框失去焦点 |
|
||||
|
||||
#### panel
|
||||
|
||||
| 值 | 描述 |
|
||||
|-------|----------------------|
|
||||
| NONE | 当面板关闭时的值 |
|
||||
| DATE | 当面板是日期选择时的值 |
|
||||
| YEAR | 当面板是年选择时的值 |
|
||||
| MONTH | 当面板是月选择时的值 |
|
||||
| TIME | 当面板是时间选择时的值 |
|
||||
| 名称 | 描述 | 回调函数的参数 |
|
||||
| ----------- | -------------------- | ---------------------- |
|
||||
| input | 当选择日期的事件触发 | 选择的日期 |
|
||||
| change | 当选择日期的事件触发 | 选择的日期, 选择的类型 |
|
||||
| open | 当弹出层打开时候 | |
|
||||
| close | 当弹出层关闭时候 | |
|
||||
| confirm | 当点击确认按钮 | 选择的日期 |
|
||||
| clear | 当点击清除按钮 | |
|
||||
| input-error | 当输入一个无效的时间 | 输入的值 |
|
||||
| focus | 当输入框有焦点 | |
|
||||
| blur | 当输入框失焦 | |
|
||||
|
||||
### Slots
|
||||
|
||||
| 名称 | 描述 |
|
||||
|-----------------|--------------------------|
|
||||
| calendar-icon | 图标自定义 |
|
||||
| header | 日历头部自定义 |
|
||||
| footer | 日历尾部自定义 |
|
||||
| ------------- | -------------- |
|
||||
| icon-calendar | 自定义日历图标 |
|
||||
| icon-clear | 自定义清除图标 |
|
||||
| header | 弹出层的头部 |
|
||||
| footer | 弹出层的底部 |
|
||||
| sidebar | 弹出层的侧边 |
|
||||
|
||||
## 日志
|
||||
## ChangeLog
|
||||
|
||||
[CHANGELOG](CHANGELOG.md)
|
||||
|
||||
## 捐赠
|
||||
|
||||
如果这个项目帮到了你, 你可以请作者喝杯果汁
|
||||
|
||||
[PayPal](https://www.paypal.me/mengxiong10) |
|
||||
[支付宝](https://user-images.githubusercontent.com/14135808/57742967-be1ac000-76f5-11e9-9607-c0854e0fdd11.png) |
|
||||
[微信](https://user-images.githubusercontent.com/14135808/57743255-e2c36780-76f6-11e9-8bb8-7720a2607dc1.png)
|
||||
|
||||
|
||||
## 许可证
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/mengxiong10/vue2-datepicker/blob/master/LICENSE)
|
||||
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CalendarPanel prop: type=date 1`] = `
|
||||
<div
|
||||
class="mx-calendar mx-calendar-panel-date"
|
||||
>
|
||||
<div
|
||||
class="mx-calendar-header"
|
||||
>
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-double-left"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-double-left"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-left"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-left"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-double-right"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-double-right"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-right"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-right"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<span
|
||||
class="mx-calendar-header-label"
|
||||
>
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-current-month"
|
||||
>
|
||||
|
||||
Oct
|
||||
|
||||
</button>
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-current-year"
|
||||
>
|
||||
|
||||
2019
|
||||
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mx-calendar-content"
|
||||
>
|
||||
<table-year-stub
|
||||
decade="2010"
|
||||
getcellclasses="function () { [native code] }"
|
||||
style="display: none;"
|
||||
/>
|
||||
|
||||
<table-month-stub
|
||||
getcellclasses="function () { [native code] }"
|
||||
style="display: none;"
|
||||
/>
|
||||
|
||||
<table-date-stub
|
||||
calendarmonth="9"
|
||||
calendaryear="2019"
|
||||
getcellclasses="function () { [native code] }"
|
||||
getrowclasses="function () { [native code] }"
|
||||
titleformat="YYYY-MM-DD"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CalendarPanel prop: type=month 1`] = `
|
||||
<div
|
||||
class="mx-calendar mx-calendar-panel-month"
|
||||
>
|
||||
<div
|
||||
class="mx-calendar-header"
|
||||
>
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-double-left"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-double-left"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-left"
|
||||
style="display: none;"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-left"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-double-right"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-double-right"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-right"
|
||||
style="display: none;"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-right"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<span
|
||||
class="mx-calendar-header-label"
|
||||
>
|
||||
<button
|
||||
class="mx-btn mx-btn-text"
|
||||
>
|
||||
|
||||
2019
|
||||
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mx-calendar-content"
|
||||
>
|
||||
<table-year-stub
|
||||
decade="2010"
|
||||
getcellclasses="function () { [native code] }"
|
||||
style="display: none;"
|
||||
/>
|
||||
|
||||
<table-month-stub
|
||||
getcellclasses="function () { [native code] }"
|
||||
/>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CalendarPanel prop: type=year 1`] = `
|
||||
<div
|
||||
class="mx-calendar mx-calendar-panel-year"
|
||||
>
|
||||
<div
|
||||
class="mx-calendar-header"
|
||||
>
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-double-left"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-double-left"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-left"
|
||||
style="display: none;"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-left"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-double-right"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-double-right"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mx-btn mx-btn-text mx-btn-icon-right"
|
||||
style="display: none;"
|
||||
>
|
||||
<i
|
||||
class="mx-icon-right"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<span
|
||||
class="mx-calendar-header-label"
|
||||
>
|
||||
<span>
|
||||
2010 ~ 2019
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mx-calendar-content"
|
||||
>
|
||||
<table-year-stub
|
||||
decade="2010"
|
||||
getcellclasses="function () { [native code] }"
|
||||
/>
|
||||
|
||||
<!---->
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,144 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`DatePicker prop: attrs of input 1`] = `
|
||||
<div
|
||||
class="mx-datepicker"
|
||||
>
|
||||
<div
|
||||
class="mx-input-wrapper"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="test"
|
||||
name="test"
|
||||
placeholder="test placeholder"
|
||||
type="number"
|
||||
/>
|
||||
|
||||
<!---->
|
||||
|
||||
<i
|
||||
class="mx-icon-calendar"
|
||||
>
|
||||
<icon-calendar-stub />
|
||||
</i>
|
||||
</div>
|
||||
|
||||
<popup-stub
|
||||
appendtobody="true"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="mx-datepicker-content-wrapper"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="mx-datepicker-content"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!---->
|
||||
</popup-stub>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`DatePicker prop: clearable 1`] = `
|
||||
<div
|
||||
class="mx-datepicker"
|
||||
>
|
||||
<div
|
||||
class="mx-input-wrapper"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="mx-input"
|
||||
name="date"
|
||||
placeholder=""
|
||||
type="text"
|
||||
/>
|
||||
|
||||
<!---->
|
||||
|
||||
<i
|
||||
class="mx-icon-calendar"
|
||||
>
|
||||
<icon-calendar-stub />
|
||||
</i>
|
||||
</div>
|
||||
|
||||
<popup-stub
|
||||
appendtobody="true"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="mx-datepicker-content-wrapper"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="mx-datepicker-content"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!---->
|
||||
</popup-stub>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`DatePicker prop: editable 1`] = `
|
||||
<div
|
||||
class="mx-datepicker"
|
||||
>
|
||||
<div
|
||||
class="mx-input-wrapper"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="mx-input"
|
||||
name="date"
|
||||
placeholder=""
|
||||
readonly="readonly"
|
||||
type="text"
|
||||
/>
|
||||
|
||||
<i
|
||||
class="mx-icon-clear"
|
||||
>
|
||||
<icon-close-stub />
|
||||
</i>
|
||||
|
||||
<i
|
||||
class="mx-icon-calendar"
|
||||
>
|
||||
<icon-calendar-stub />
|
||||
</i>
|
||||
</div>
|
||||
|
||||
<popup-stub
|
||||
appendtobody="true"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="mx-datepicker-content-wrapper"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="mx-datepicker-content"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!---->
|
||||
</popup-stub>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,453 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TableDate corrent render 1`] = `
|
||||
<table
|
||||
class="mx-table mx-table-date"
|
||||
firstdayofweek="1"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<!---->
|
||||
|
||||
<th>
|
||||
Su
|
||||
</th>
|
||||
<th>
|
||||
Mo
|
||||
</th>
|
||||
<th>
|
||||
Tu
|
||||
</th>
|
||||
<th>
|
||||
We
|
||||
</th>
|
||||
<th>
|
||||
Th
|
||||
</th>
|
||||
<th>
|
||||
Fr
|
||||
</th>
|
||||
<th>
|
||||
Sa
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr
|
||||
class="mx-date-row"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<td
|
||||
class="cell"
|
||||
data-day="-1"
|
||||
title="29/09/2019"
|
||||
>
|
||||
<div>
|
||||
29
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="0"
|
||||
title="30/09/2019"
|
||||
>
|
||||
<div>
|
||||
30
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="1"
|
||||
title="01/10/2019"
|
||||
>
|
||||
<div>
|
||||
1
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="2"
|
||||
title="02/10/2019"
|
||||
>
|
||||
<div>
|
||||
2
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="3"
|
||||
title="03/10/2019"
|
||||
>
|
||||
<div>
|
||||
3
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="4"
|
||||
title="04/10/2019"
|
||||
>
|
||||
<div>
|
||||
4
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="5"
|
||||
title="05/10/2019"
|
||||
>
|
||||
<div>
|
||||
5
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="mx-date-row"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<td
|
||||
class="cell"
|
||||
data-day="6"
|
||||
title="06/10/2019"
|
||||
>
|
||||
<div>
|
||||
6
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="7"
|
||||
title="07/10/2019"
|
||||
>
|
||||
<div>
|
||||
7
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="8"
|
||||
title="08/10/2019"
|
||||
>
|
||||
<div>
|
||||
8
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="9"
|
||||
title="09/10/2019"
|
||||
>
|
||||
<div>
|
||||
9
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="10"
|
||||
title="10/10/2019"
|
||||
>
|
||||
<div>
|
||||
10
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="11"
|
||||
title="11/10/2019"
|
||||
>
|
||||
<div>
|
||||
11
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="12"
|
||||
title="12/10/2019"
|
||||
>
|
||||
<div>
|
||||
12
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="mx-date-row"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<td
|
||||
class="cell"
|
||||
data-day="13"
|
||||
title="13/10/2019"
|
||||
>
|
||||
<div>
|
||||
13
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="14"
|
||||
title="14/10/2019"
|
||||
>
|
||||
<div>
|
||||
14
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="15"
|
||||
title="15/10/2019"
|
||||
>
|
||||
<div>
|
||||
15
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="16"
|
||||
title="16/10/2019"
|
||||
>
|
||||
<div>
|
||||
16
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="17"
|
||||
title="17/10/2019"
|
||||
>
|
||||
<div>
|
||||
17
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="18"
|
||||
title="18/10/2019"
|
||||
>
|
||||
<div>
|
||||
18
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="19"
|
||||
title="19/10/2019"
|
||||
>
|
||||
<div>
|
||||
19
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="mx-date-row"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<td
|
||||
class="cell"
|
||||
data-day="20"
|
||||
title="20/10/2019"
|
||||
>
|
||||
<div>
|
||||
20
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="21"
|
||||
title="21/10/2019"
|
||||
>
|
||||
<div>
|
||||
21
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="22"
|
||||
title="22/10/2019"
|
||||
>
|
||||
<div>
|
||||
22
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="23"
|
||||
title="23/10/2019"
|
||||
>
|
||||
<div>
|
||||
23
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="24"
|
||||
title="24/10/2019"
|
||||
>
|
||||
<div>
|
||||
24
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="25"
|
||||
title="25/10/2019"
|
||||
>
|
||||
<div>
|
||||
25
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="26"
|
||||
title="26/10/2019"
|
||||
>
|
||||
<div>
|
||||
26
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="mx-date-row"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<td
|
||||
class="cell"
|
||||
data-day="27"
|
||||
title="27/10/2019"
|
||||
>
|
||||
<div>
|
||||
27
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="28"
|
||||
title="28/10/2019"
|
||||
>
|
||||
<div>
|
||||
28
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="29"
|
||||
title="29/10/2019"
|
||||
>
|
||||
<div>
|
||||
29
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="30"
|
||||
title="30/10/2019"
|
||||
>
|
||||
<div>
|
||||
30
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="31"
|
||||
title="31/10/2019"
|
||||
>
|
||||
<div>
|
||||
31
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="32"
|
||||
title="01/11/2019"
|
||||
>
|
||||
<div>
|
||||
1
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="33"
|
||||
title="02/11/2019"
|
||||
>
|
||||
<div>
|
||||
2
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="mx-date-row"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<td
|
||||
class="cell"
|
||||
data-day="34"
|
||||
title="03/11/2019"
|
||||
>
|
||||
<div>
|
||||
3
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="35"
|
||||
title="04/11/2019"
|
||||
>
|
||||
<div>
|
||||
4
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="36"
|
||||
title="05/11/2019"
|
||||
>
|
||||
<div>
|
||||
5
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="37"
|
||||
title="06/11/2019"
|
||||
>
|
||||
<div>
|
||||
6
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="38"
|
||||
title="07/11/2019"
|
||||
>
|
||||
<div>
|
||||
7
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="39"
|
||||
title="08/11/2019"
|
||||
>
|
||||
<div>
|
||||
8
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-day="40"
|
||||
title="09/11/2019"
|
||||
>
|
||||
<div>
|
||||
9
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
@@ -0,0 +1,119 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TableMonth correct render 1`] = `
|
||||
<table
|
||||
class="mx-table mx-table-month"
|
||||
getclasses="month => {
|
||||
if (month === 9) {
|
||||
return 'active';
|
||||
}
|
||||
|
||||
return '';
|
||||
}"
|
||||
>
|
||||
<tr>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="0"
|
||||
>
|
||||
<div>
|
||||
Jan
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="1"
|
||||
>
|
||||
<div>
|
||||
Feb
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="2"
|
||||
>
|
||||
<div>
|
||||
Mar
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="3"
|
||||
>
|
||||
<div>
|
||||
Apr
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="4"
|
||||
>
|
||||
<div>
|
||||
May
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="5"
|
||||
>
|
||||
<div>
|
||||
Jun
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="6"
|
||||
>
|
||||
<div>
|
||||
Jul
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="7"
|
||||
>
|
||||
<div>
|
||||
Aug
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="8"
|
||||
>
|
||||
<div>
|
||||
Sep
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="9"
|
||||
>
|
||||
<div>
|
||||
Oct
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="10"
|
||||
>
|
||||
<div>
|
||||
Nov
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-month="11"
|
||||
>
|
||||
<div>
|
||||
Dec
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
`;
|
||||
@@ -0,0 +1,98 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TableYear decade=2010 1`] = `
|
||||
<table
|
||||
class="mx-table mx-table-year"
|
||||
>
|
||||
<tr>
|
||||
<td
|
||||
class="cell"
|
||||
data-year="2010"
|
||||
>
|
||||
<div>
|
||||
2010
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-year="2011"
|
||||
>
|
||||
<div>
|
||||
2011
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="cell"
|
||||
data-year="2012"
|
||||
>
|
||||
<div>
|
||||
2012
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-year="2013"
|
||||
>
|
||||
<div>
|
||||
2013
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="cell"
|
||||
data-year="2014"
|
||||
>
|
||||
<div>
|
||||
2014
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-year="2015"
|
||||
>
|
||||
<div>
|
||||
2015
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="cell"
|
||||
data-year="2016"
|
||||
>
|
||||
<div>
|
||||
2016
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-year="2017"
|
||||
>
|
||||
<div>
|
||||
2017
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="cell"
|
||||
data-year="2018"
|
||||
>
|
||||
<div>
|
||||
2018
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="cell"
|
||||
data-year="2019"
|
||||
>
|
||||
<div>
|
||||
2019
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
`;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,328 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TimeRange render: correct classes of the columns 1`] = `
|
||||
<div
|
||||
class="mx-range-wrapper"
|
||||
>
|
||||
<div
|
||||
class="mx-time"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="mx-time-content"
|
||||
>
|
||||
<div
|
||||
class="mx-time-columns"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar mx-time-column"
|
||||
style="position: relative; overflow: hidden;"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-wrap"
|
||||
style="overflow: hidden scroll; height: 100%; margin-right: -0px;"
|
||||
>
|
||||
<ul
|
||||
data-type="hour"
|
||||
>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570120200000"
|
||||
>
|
||||
|
||||
12
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570127400000"
|
||||
>
|
||||
|
||||
02
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570134600000"
|
||||
>
|
||||
|
||||
04
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570141800000"
|
||||
>
|
||||
|
||||
06
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell active"
|
||||
data-value="1570149000000"
|
||||
>
|
||||
|
||||
08
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570156200000"
|
||||
>
|
||||
|
||||
10
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mx-scrollbar-track"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-thumb"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx-scrollbar mx-time-column"
|
||||
style="position: relative; overflow: hidden;"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-wrap"
|
||||
style="overflow: hidden scroll; height: 100%; margin-right: -0px;"
|
||||
>
|
||||
<ul
|
||||
data-type="minute"
|
||||
>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570147200000"
|
||||
>
|
||||
|
||||
00
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell active"
|
||||
data-value="1570149000000"
|
||||
>
|
||||
|
||||
30
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mx-scrollbar-track"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-thumb"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx-scrollbar mx-time-column"
|
||||
style="position: relative; overflow: hidden;"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-wrap"
|
||||
style="overflow: hidden scroll; height: 100%; margin-right: -0px;"
|
||||
>
|
||||
<ul
|
||||
data-type="ampm"
|
||||
>
|
||||
<li
|
||||
class="cell active"
|
||||
data-value="1570149000000"
|
||||
>
|
||||
|
||||
AM
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570192200000"
|
||||
>
|
||||
|
||||
PM
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mx-scrollbar-track"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-thumb"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx-time"
|
||||
>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="mx-time-content"
|
||||
>
|
||||
<div
|
||||
class="mx-time-columns"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar mx-time-column"
|
||||
style="position: relative; overflow: hidden;"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-wrap"
|
||||
style="overflow: hidden scroll; height: 100%; margin-right: -0px;"
|
||||
>
|
||||
<ul
|
||||
data-type="hour"
|
||||
>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570163400000"
|
||||
>
|
||||
|
||||
12
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570170600000"
|
||||
>
|
||||
|
||||
02
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570177800000"
|
||||
>
|
||||
|
||||
04
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell active"
|
||||
data-value="1570185000000"
|
||||
>
|
||||
|
||||
06
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570192200000"
|
||||
>
|
||||
|
||||
08
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570199400000"
|
||||
>
|
||||
|
||||
10
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mx-scrollbar-track"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-thumb"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx-scrollbar mx-time-column"
|
||||
style="position: relative; overflow: hidden;"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-wrap"
|
||||
style="overflow: hidden scroll; height: 100%; margin-right: -0px;"
|
||||
>
|
||||
<ul
|
||||
data-type="minute"
|
||||
>
|
||||
<li
|
||||
class="cell"
|
||||
data-value="1570183200000"
|
||||
>
|
||||
|
||||
00
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell active"
|
||||
data-value="1570185000000"
|
||||
>
|
||||
|
||||
30
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mx-scrollbar-track"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-thumb"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx-scrollbar mx-time-column"
|
||||
style="position: relative; overflow: hidden;"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-wrap"
|
||||
style="overflow: hidden scroll; height: 100%; margin-right: -0px;"
|
||||
>
|
||||
<ul
|
||||
data-type="ampm"
|
||||
>
|
||||
<li
|
||||
class="cell disabled"
|
||||
data-value="1570141800000"
|
||||
>
|
||||
|
||||
AM
|
||||
|
||||
</li>
|
||||
<li
|
||||
class="cell active"
|
||||
data-value="1570185000000"
|
||||
>
|
||||
|
||||
PM
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mx-scrollbar-track"
|
||||
>
|
||||
<div
|
||||
class="mx-scrollbar-thumb"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,157 @@
|
||||
import { mount, shallowMount } from '@vue/test-utils';
|
||||
import CalendarPanel from '../src/calendar/calendar-panel';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('CalendarPanel', () => {
|
||||
it('feat: type = date, should emit select when click date', () => {
|
||||
wrapper = mount(CalendarPanel, {
|
||||
propsData: {
|
||||
value: new Date(2019, 9, 4),
|
||||
},
|
||||
});
|
||||
const tds = wrapper.findAll('.mx-table-date td');
|
||||
tds.at(0).trigger('click');
|
||||
expect(wrapper.emitted().select[0][0]).toEqual(new Date(2019, 8, 29));
|
||||
});
|
||||
|
||||
it('feat: type = month, should emit event when click month', () => {
|
||||
wrapper = mount(CalendarPanel, {
|
||||
propsData: {
|
||||
type: 'month',
|
||||
defaultValue: new Date(2019, 9, 1),
|
||||
},
|
||||
});
|
||||
const tds = wrapper.findAll('.mx-table-month td > div');
|
||||
tds.at(0).trigger('click');
|
||||
expect(wrapper.emitted().select[0][0]).toEqual(new Date(2019, 0, 1));
|
||||
});
|
||||
|
||||
it('feat: type = year, should emit event when click year', () => {
|
||||
wrapper = mount(CalendarPanel, {
|
||||
propsData: {
|
||||
type: 'year',
|
||||
defaultValue: new Date(2019, 9, 1),
|
||||
},
|
||||
});
|
||||
const tds = wrapper.findAll('.mx-table-year td > div');
|
||||
tds.at(0).trigger('click');
|
||||
expect(wrapper.emitted().select[0][0]).toEqual(new Date(2010, 0));
|
||||
});
|
||||
|
||||
it('feat: active class', () => {
|
||||
wrapper = mount(CalendarPanel);
|
||||
const td = wrapper.find('.mx-table-date td:nth-child(6)');
|
||||
expect(td.classes()).not.toContain('active');
|
||||
wrapper.setProps({ value: new Date(2019, 9, 4) });
|
||||
expect(td.classes()).toContain('active');
|
||||
});
|
||||
|
||||
const renderType = type => {
|
||||
it(`prop: type=${type}`, () => {
|
||||
wrapper = shallowMount(CalendarPanel, {
|
||||
propsData: {
|
||||
type,
|
||||
value: new Date(2019, 9, 1, 10),
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
};
|
||||
['date', 'month', 'year'].forEach(renderType);
|
||||
|
||||
it('prop: disabledDate', () => {
|
||||
const disabledDate = date => {
|
||||
return date < new Date(2019, 9, 1) || date > new Date(2019, 9, 20);
|
||||
};
|
||||
wrapper = mount(CalendarPanel, {
|
||||
propsData: {
|
||||
value: new Date(2019, 9, 4),
|
||||
disabledDate,
|
||||
},
|
||||
});
|
||||
const tds = wrapper.findAll('.mx-table-date td');
|
||||
for (let i = 0; i < 42; i++) {
|
||||
const td = tds.at(i);
|
||||
const classes = td.classes();
|
||||
if (i < 2 || i > 21) {
|
||||
expect(classes).toContain('disabled');
|
||||
} else {
|
||||
expect(classes).not.toContain('disabled');
|
||||
}
|
||||
}
|
||||
tds.at(1).trigger('click');
|
||||
expect(wrapper.emitted().select).toBeUndefined();
|
||||
});
|
||||
|
||||
it('feat: click prev/next month', () => {
|
||||
wrapper = shallowMount(CalendarPanel);
|
||||
|
||||
const nextBtn = wrapper.find('.mx-btn-icon-right');
|
||||
const lastBtn = wrapper.find('.mx-btn-icon-left');
|
||||
const { vm } = wrapper;
|
||||
let count = 12;
|
||||
while (count--) {
|
||||
const oldYear = vm.calendarYear;
|
||||
const oldMonth = vm.calendarMonth;
|
||||
nextBtn.trigger('click');
|
||||
const newYear = vm.calendarYear;
|
||||
const newMonth = vm.calendarMonth;
|
||||
if (oldMonth === 11) {
|
||||
expect(newMonth).toBe(0);
|
||||
expect(newYear).toBe(oldYear + 1);
|
||||
} else {
|
||||
expect(newMonth).toBe(oldMonth + 1);
|
||||
expect(newYear).toBe(oldYear);
|
||||
}
|
||||
}
|
||||
count = 12;
|
||||
while (count--) {
|
||||
const oldYear = vm.calendarYear;
|
||||
const oldMonth = vm.calendarMonth;
|
||||
lastBtn.trigger('click');
|
||||
const newYear = vm.calendarYear;
|
||||
const newMonth = vm.calendarMonth;
|
||||
if (oldMonth === 0) {
|
||||
expect(newMonth).toBe(11);
|
||||
expect(newYear).toBe(oldYear - 1);
|
||||
} else {
|
||||
expect(newMonth).toBe(oldMonth - 1);
|
||||
expect(newYear).toBe(oldYear);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('feat: click prev/next year', () => {
|
||||
wrapper = shallowMount(CalendarPanel, {
|
||||
propsData: {
|
||||
value: new Date(2018, 4, 5),
|
||||
},
|
||||
});
|
||||
const nextBtn = wrapper.find('.mx-btn-icon-double-right');
|
||||
const lastBtn = wrapper.find('.mx-btn-icon-double-left');
|
||||
const yearBtn = wrapper.find('.mx-btn-current-year');
|
||||
const { vm } = wrapper;
|
||||
const oldYear = vm.calendarYear;
|
||||
expect(oldYear).toBe(2018);
|
||||
nextBtn.trigger('click');
|
||||
let newYear = vm.calendarYear;
|
||||
expect(newYear).toBe(2019);
|
||||
lastBtn.trigger('click');
|
||||
newYear = vm.calendarYear;
|
||||
expect(newYear).toBe(oldYear);
|
||||
// 年视图测试
|
||||
yearBtn.trigger('click');
|
||||
expect(vm.panel).toBe('year');
|
||||
expect(vm.calendarDecade).toBe(2010);
|
||||
nextBtn.trigger('click');
|
||||
expect(vm.calendarDecade).toBe(2020);
|
||||
lastBtn.trigger('click');
|
||||
lastBtn.trigger('click');
|
||||
expect(vm.calendarDecade).toBe(2000);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import flushPromises from 'flush-promises';
|
||||
import CalendarRange from '../src/calendar/calendar-range';
|
||||
import CalendarPanel from '../src/calendar/calendar-panel';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('CalendarRange', () => {
|
||||
it('feat: correct classes', () => {
|
||||
wrapper = mount(CalendarRange, {
|
||||
sync: false,
|
||||
propsData: {
|
||||
value: [new Date(2019, 9, 30), new Date(2019, 10, 2)],
|
||||
},
|
||||
});
|
||||
const activeTds = wrapper.findAll('.mx-table-date .active');
|
||||
const rangeTds = wrapper.findAll('.mx-table-date .in-range');
|
||||
expect(activeTds.length).toBe(2);
|
||||
expect(activeTds.at(0).text()).toBe('30');
|
||||
expect(activeTds.at(1).text()).toBe('2');
|
||||
expect(rangeTds.length).toBe(2);
|
||||
expect(rangeTds.at(0).text()).toBe('31');
|
||||
expect(rangeTds.at(1).text()).toBe('1');
|
||||
});
|
||||
|
||||
it('feat: click range', async () => {
|
||||
wrapper = mount(CalendarRange, {
|
||||
sync: false,
|
||||
propsData: {
|
||||
defaultValue: new Date(2019, 9, 1),
|
||||
},
|
||||
});
|
||||
expect(wrapper.vm.calendars).toEqual([new Date(2019, 9, 1), new Date(2019, 10, 1)]);
|
||||
const tds = wrapper.findAll('.mx-table-date td');
|
||||
tds.at(2).trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.emitted().select).toBeUndefined();
|
||||
tds.at(8).trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.emitted().select[0][0]).toEqual([new Date(2019, 9, 1), new Date(2019, 9, 7)]);
|
||||
});
|
||||
|
||||
it('feat: calendars min gap', async () => {
|
||||
wrapper = mount(CalendarRange, {
|
||||
sync: false,
|
||||
propsData: {
|
||||
defaultValue: new Date(2019, 6, 1),
|
||||
},
|
||||
});
|
||||
const panels = wrapper.findAll(CalendarPanel);
|
||||
const startPanel = panels.at(0);
|
||||
const endPanel = panels.at(1);
|
||||
|
||||
startPanel.find('.mx-btn-icon-right').trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.vm.calendars).toEqual([new Date(2019, 7, 1), new Date(2019, 8, 1)]);
|
||||
|
||||
endPanel.find('.mx-btn-icon-left').trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.vm.calendars).toEqual([new Date(2019, 6, 1), new Date(2019, 7, 1)]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,277 @@
|
||||
import { shallowMount, createWrapper, mount } from '@vue/test-utils';
|
||||
import { format, parse } from 'date-fns';
|
||||
import DatePicker from '../src/date-picker.vue';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('DatePicker', () => {
|
||||
it('feat: open and close the popup', () => {
|
||||
wrapper = mount(DatePicker, {
|
||||
attachToDocument: true,
|
||||
});
|
||||
expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(false);
|
||||
// expect click input should show the popup
|
||||
const input = wrapper.find('input');
|
||||
input.trigger('click');
|
||||
expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(true);
|
||||
// expect click out side should hide the popup
|
||||
const bodyWrapper = createWrapper(document.body);
|
||||
bodyWrapper.trigger('mousedown');
|
||||
expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(false);
|
||||
// expect focus input should show the popop
|
||||
input.trigger('focus');
|
||||
expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(true);
|
||||
// expoce keydown tab should hide the popup
|
||||
input.trigger('keydown.tab');
|
||||
expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('feat: should init panel and calendar when reopen', () => {
|
||||
wrapper = mount(DatePicker, {
|
||||
propsData: {
|
||||
defaultValue: new Date(2019, 9, 1),
|
||||
open: true,
|
||||
},
|
||||
});
|
||||
const yearBtn = wrapper.find('.mx-btn-current-year');
|
||||
yearBtn.trigger('click');
|
||||
// change to year panel
|
||||
expect(wrapper.find('.mx-calendar-panel-year').exists()).toBe(true);
|
||||
wrapper.setProps({ open: false });
|
||||
wrapper.setProps({ open: true });
|
||||
expect(wrapper.find('.mx-calendar-panel-year').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('prop: open', () => {
|
||||
wrapper = mount(DatePicker, {
|
||||
propsData: {
|
||||
open: false,
|
||||
},
|
||||
});
|
||||
const { vm } = wrapper;
|
||||
vm.openPopup();
|
||||
expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(false);
|
||||
wrapper.setProps({ open: true });
|
||||
expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(true);
|
||||
vm.closePopup();
|
||||
expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(true);
|
||||
const emitted = wrapper.emitted();
|
||||
expect(emitted['update:open'][0][0]).toBe(true);
|
||||
expect(emitted['update:open'][1][0]).toBe(false);
|
||||
});
|
||||
|
||||
it('prop: disabled(should not show the popup)', () => {
|
||||
wrapper = mount(DatePicker, {
|
||||
propsData: {
|
||||
disabled: true,
|
||||
},
|
||||
});
|
||||
const input = wrapper.find('input');
|
||||
expect(input.attributes('disabled')).toBe('disabled');
|
||||
input.trigger('click');
|
||||
expect(wrapper.find('.mx-datepicker-popup').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('prop: clearable', () => {
|
||||
wrapper = shallowMount(DatePicker, {
|
||||
propsData: {
|
||||
value: new Date(2019, 4, 10),
|
||||
defaultValue: new Date(2019, 4, 10),
|
||||
clearable: false,
|
||||
},
|
||||
// calendar-panel: default-value="Fri May 10 2019 00:00:00 GMT+0800 (CST)"
|
||||
// Exclude the impact of timezone
|
||||
scopedSlots: {
|
||||
content: '<div></div>',
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('prop: editable', () => {
|
||||
wrapper = shallowMount(DatePicker, {
|
||||
propsData: {
|
||||
value: new Date(2019, 4, 10),
|
||||
defaultValue: new Date(2019, 4, 10),
|
||||
editable: false,
|
||||
},
|
||||
scopedSlots: {
|
||||
content: '<div></div>',
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('prop: attrs of input', () => {
|
||||
wrapper = shallowMount(DatePicker, {
|
||||
propsData: {
|
||||
defaultValue: new Date(2019, 4, 10),
|
||||
placeholder: 'test placeholder',
|
||||
inputClass: 'test',
|
||||
inputAttr: {
|
||||
type: 'number',
|
||||
name: 'test',
|
||||
},
|
||||
},
|
||||
scopedSlots: {
|
||||
content: '<div></div>',
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('prop: format', () => {
|
||||
wrapper = shallowMount(DatePicker, {
|
||||
propsData: {
|
||||
format: 'YYYY/MM/DD',
|
||||
value: new Date(2019, 9, 10),
|
||||
},
|
||||
});
|
||||
const input = wrapper.find('input').element;
|
||||
expect(input.value).toBe('2019/10/10');
|
||||
});
|
||||
|
||||
it('prop: custom format', () => {
|
||||
wrapper = shallowMount(DatePicker, {
|
||||
propsData: {
|
||||
valueType: 'format',
|
||||
value: '13/10/2019',
|
||||
format: {
|
||||
stringify(date) {
|
||||
return format(date, 'dd/MM/yyyy');
|
||||
},
|
||||
parse(value) {
|
||||
return parse(value, 'dd/MM/yyyy', new Date());
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const input = wrapper.find('input').element;
|
||||
expect(input.value).toBe('13/10/2019');
|
||||
});
|
||||
|
||||
it('prop: valueType', () => {
|
||||
const value = new Date(2019, 9, 20);
|
||||
const emitValue = new Date(2019, 9, 22);
|
||||
wrapper = shallowMount(DatePicker, {
|
||||
propsData: {
|
||||
value,
|
||||
format: 'YYYY/MM/DD',
|
||||
},
|
||||
});
|
||||
const { vm } = wrapper;
|
||||
expect(vm.text).toBe('2019/10/20');
|
||||
vm.emitValue(emitValue);
|
||||
wrapper.setProps({ valueType: 'format', value: '2019/10/20' });
|
||||
expect(vm.text).toBe('2019/10/20');
|
||||
vm.emitValue(emitValue);
|
||||
wrapper.setProps({ valueType: 'timestamp', value: value.getTime() });
|
||||
expect(vm.text).toBe('2019/10/20');
|
||||
vm.emitValue(emitValue);
|
||||
wrapper.setProps({ valueType: 'DD/MM/YYYY', value: '20/10/2019' });
|
||||
expect(vm.text).toBe('2019/10/20');
|
||||
vm.emitValue(emitValue);
|
||||
const emitted = wrapper.emitted();
|
||||
expect(emitted.input).toEqual([
|
||||
[emitValue],
|
||||
['2019/10/22'],
|
||||
[emitValue.getTime()],
|
||||
['22/10/2019'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('prop: shortcut', () => {
|
||||
const date = new Date(2019, 4, 10);
|
||||
wrapper = shallowMount(DatePicker, {
|
||||
propsData: {
|
||||
open: true,
|
||||
valueType: 'YYYY/MM/DD',
|
||||
shortcuts: [
|
||||
{
|
||||
text: 'Today',
|
||||
onClick() {
|
||||
return date;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const btn = wrapper.find('.mx-btn-shortcut');
|
||||
btn.trigger('click');
|
||||
const emitted = wrapper.emitted();
|
||||
expect(emitted.input).toEqual([['2019/05/10']]);
|
||||
});
|
||||
|
||||
it('prop: popupClass', () => {
|
||||
wrapper = mount(DatePicker, {
|
||||
propsData: {
|
||||
open: true,
|
||||
popupClass: 'test',
|
||||
},
|
||||
});
|
||||
const popup = wrapper.find('.mx-datepicker-popup');
|
||||
expect(popup.classes()).toContain('test');
|
||||
});
|
||||
|
||||
it('prop: popupStyle', () => {
|
||||
wrapper = mount(DatePicker, {
|
||||
propsData: {
|
||||
open: true,
|
||||
popupStyle: {
|
||||
color: 'red',
|
||||
},
|
||||
},
|
||||
});
|
||||
const popup = wrapper.find('.mx-datepicker-popup');
|
||||
expect(popup.element.style.color).toBe('red');
|
||||
});
|
||||
|
||||
it('prop: confirm', () => {
|
||||
wrapper = shallowMount(DatePicker, {
|
||||
propsData: {
|
||||
confirm: true,
|
||||
},
|
||||
});
|
||||
const { vm } = wrapper;
|
||||
vm.openPopup();
|
||||
const btn = wrapper.find('.mx-datepicker-btn-confirm');
|
||||
expect(btn.exists()).toBe(true);
|
||||
// click the date expect popup don't close
|
||||
vm.handleSelectDate(new Date(2018, 5, 5));
|
||||
expect(wrapper.emitted().input).toBeUndefined();
|
||||
expect(vm.popupVisible).toBe(true);
|
||||
btn.trigger('click');
|
||||
expect(wrapper.emitted().input[0][0]).toEqual(new Date(2018, 5, 5));
|
||||
expect(vm.popupVisible).toBe(false);
|
||||
});
|
||||
|
||||
it('prop: confirmText', () => {
|
||||
wrapper = shallowMount(DatePicker, {
|
||||
propsData: {
|
||||
open: true,
|
||||
confirm: true,
|
||||
confirmText: 'test',
|
||||
},
|
||||
});
|
||||
|
||||
const btn = wrapper.find('.mx-datepicker-btn-confirm');
|
||||
expect(btn.text()).toBe('test');
|
||||
});
|
||||
|
||||
it('prop: appendToBody', () => {
|
||||
wrapper = mount(DatePicker, {
|
||||
propsData: {
|
||||
open: true,
|
||||
appendToBody: true,
|
||||
},
|
||||
});
|
||||
|
||||
const popup = wrapper.find('.mx-datepicker-popup');
|
||||
expect(popup.element.parentNode).toBe(document.body);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import DatetimePanel from '../src/datetime/datetime-panel';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('DatetimePanel', () => {
|
||||
it('feat: click date', () => {
|
||||
wrapper = mount(DatetimePanel, {
|
||||
propsData: {
|
||||
defaultValue: new Date(2019, 9, 1, 12),
|
||||
},
|
||||
});
|
||||
const td = wrapper.find('.mx-table-date td:nth-child(4)');
|
||||
td.trigger('click');
|
||||
expect(wrapper.emitted().select[0][0]).toEqual(new Date(2019, 9, 2, 12));
|
||||
let timeTitle = wrapper.find('.mx-time-header-title');
|
||||
expect(timeTitle.exists()).toBe(true);
|
||||
timeTitle.trigger('click');
|
||||
timeTitle = wrapper.find('.mx-time-header-title');
|
||||
expect(timeTitle.exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('feat: disabled time', () => {
|
||||
const disabledDate = date => date < new Date(2019, 9, 2);
|
||||
const disabledTime = date => date < new Date(2019, 9, 2, 12);
|
||||
wrapper = mount(DatetimePanel, {
|
||||
propsData: {
|
||||
defaultValue: new Date(2019, 9, 1),
|
||||
disabledDate,
|
||||
disabledTime,
|
||||
},
|
||||
});
|
||||
const td = wrapper.find('.mx-table-date td:nth-child(4)');
|
||||
td.trigger('click');
|
||||
expect(wrapper.emitted().select).toBeUndefined();
|
||||
const timeTitle = wrapper.find('.mx-time-header-title');
|
||||
expect(timeTitle.text()).toBe('2019-10-02');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import flushPromises from 'flush-promises';
|
||||
import DatetimeRange from '../src/datetime/datetime-range';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('DatetimeRange', () => {
|
||||
it('feat: click dates', async () => {
|
||||
wrapper = mount(DatetimeRange, {
|
||||
sync: false,
|
||||
propsData: {
|
||||
value: [new Date(2019, 9, 4, 18), new Date(2019, 9, 6, 12)],
|
||||
},
|
||||
});
|
||||
const td = wrapper.find('.mx-table-date td:nth-child(4)');
|
||||
const td2 = wrapper.find('.mx-table-date td:nth-child(5)');
|
||||
td.trigger('click');
|
||||
td2.trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.emitted().select[0][0]).toEqual([
|
||||
new Date(2019, 9, 2, 18),
|
||||
new Date(2019, 9, 3, 12),
|
||||
]);
|
||||
let timeTitle = wrapper.find('.mx-time-header-title');
|
||||
expect(timeTitle.exists()).toBe(true);
|
||||
timeTitle.trigger('click');
|
||||
await flushPromises();
|
||||
timeTitle = wrapper.find('.mx-time-header-title');
|
||||
expect(timeTitle.exists()).toBe(false);
|
||||
td.trigger('click');
|
||||
td.trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.emitted().select[1][0]).toEqual([
|
||||
new Date(2019, 9, 2, 18),
|
||||
new Date(2019, 9, 2, 18),
|
||||
]);
|
||||
});
|
||||
|
||||
it('feat: disabled time', async () => {
|
||||
const disabledDate = date => date < new Date(2019, 9, 2);
|
||||
const disabledTime = date => date < new Date(2019, 9, 2, 12);
|
||||
wrapper = mount(DatetimeRange, {
|
||||
sync: false,
|
||||
propsData: {
|
||||
defaultValue: new Date(2019, 9, 1),
|
||||
disabledDate,
|
||||
disabledTime,
|
||||
},
|
||||
});
|
||||
const td = wrapper.find('.mx-table-date td:nth-child(4)');
|
||||
td.trigger('click');
|
||||
td.trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.emitted().select).toBeUndefined();
|
||||
const timeTitle = wrapper.find('.mx-time-header-title');
|
||||
expect(timeTitle.text()).toBe('2019-10-02');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import DatePicker from '../src/date-picker.vue';
|
||||
import '../src/locale/zh-cn';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('Locale', () => {
|
||||
it('render the correct default locale', () => {
|
||||
wrapper = mount(DatePicker, {
|
||||
propsData: {
|
||||
value: new Date(2019, 9, 10),
|
||||
open: true,
|
||||
},
|
||||
});
|
||||
expect(wrapper.find('.mx-table-date th').text()).toBe('一');
|
||||
expect(wrapper.find('.mx-table-date td').element.title).toBe('2019-09-30');
|
||||
});
|
||||
|
||||
it('prop: lang - string', () => {
|
||||
wrapper = mount(DatePicker, {
|
||||
propsData: {
|
||||
value: new Date(2019, 9, 10),
|
||||
open: true,
|
||||
lang: 'en',
|
||||
titleFormat: 'MMM DD, YYYY',
|
||||
},
|
||||
});
|
||||
expect(wrapper.find('.mx-table-date th').text()).toBe('Su');
|
||||
expect(wrapper.find('.mx-table-date td').element.title).toBe('Sep 29, 2019');
|
||||
});
|
||||
|
||||
it('prop: lang - object', () => {
|
||||
wrapper = mount(DatePicker, {
|
||||
propsData: {
|
||||
open: true,
|
||||
lang: {
|
||||
formatLocale: {
|
||||
firstDayOfWeek: 2,
|
||||
},
|
||||
days: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(wrapper.find('.mx-table-date th').text()).toBe('周二');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import TableDate from '../src/calendar/table-date.vue';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('TableDate', () => {
|
||||
it('corrent render', () => {
|
||||
wrapper = mount(TableDate, {
|
||||
propsData: {
|
||||
calendarYear: 2019,
|
||||
calendarMonth: 9,
|
||||
firstDayOfWeek: 1,
|
||||
titleFormat: 'DD/MM/YYYY',
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import TableMonth from '../src/calendar/table-month.vue';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('TableMonth', () => {
|
||||
it('correct render', () => {
|
||||
wrapper = mount(TableMonth, {
|
||||
propsData: {
|
||||
getClasses: month => {
|
||||
if (month === 9) {
|
||||
return 'active';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import TableYear from '../src/calendar/table-year.vue';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('TableYear', () => {
|
||||
it('decade=2010', () => {
|
||||
wrapper = mount(TableYear, {
|
||||
propsData: {
|
||||
decade: 2010,
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import TimePanel from '../src/time/time-panel';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('TimePanel', () => {
|
||||
it('render: correct classes of the columns', () => {
|
||||
wrapper = mount(TimePanel, {
|
||||
propsData: {
|
||||
value: new Date(2019, 9, 4, 12, 30, 30),
|
||||
disabledTime: date => date.getHours() < 10,
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('render: correct columns by format', () => {
|
||||
wrapper = mount(TimePanel, {
|
||||
propsData: {
|
||||
value: new Date(2019, 9, 4),
|
||||
format: 'hh:mm a',
|
||||
minuteStep: 30,
|
||||
hourOptions: Array.from({ length: 10 }).map((_, i) => i + 8),
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('render: correct classes of the fixed time list', () => {
|
||||
wrapper = mount(TimePanel, {
|
||||
propsData: {
|
||||
value: new Date(2019, 10, 9, 12, 30),
|
||||
disabledTime: date => date.getHours() < 10,
|
||||
timePickerOptions: {
|
||||
start: '08:30',
|
||||
step: '00:30',
|
||||
end: '18:30',
|
||||
},
|
||||
format: 'HH:mm',
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('render: correct 12hours in the fixed time list', () => {
|
||||
wrapper = mount(TimePanel, {
|
||||
propsData: {
|
||||
value: new Date(2019, 10, 9, 12, 30),
|
||||
timePickerOptions: {
|
||||
start: '08:30',
|
||||
step: '00:30',
|
||||
end: '18:30',
|
||||
},
|
||||
format: 'hh:mm A',
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('feat: emit select event when click', () => {
|
||||
wrapper = mount(TimePanel, {
|
||||
propsData: {
|
||||
format: 'hh:mm:ss a',
|
||||
defaultValue: new Date(2019, 9, 10, 2),
|
||||
},
|
||||
});
|
||||
const hour = wrapper.find('[data-type=hour] li:nth-child(2)');
|
||||
hour.trigger('click');
|
||||
expect(wrapper.emitted().select[0][0]).toEqual(new Date(2019, 9, 10, 1));
|
||||
wrapper.setProps({ value: new Date(2019, 9, 10, 1) });
|
||||
const minute = wrapper.find('[data-type=minute] li:nth-child(2)');
|
||||
minute.trigger('click');
|
||||
expect(wrapper.emitted().select[1][0]).toEqual(new Date(2019, 9, 10, 1, 1));
|
||||
wrapper.setProps({ value: new Date(2019, 9, 10, 1, 1) });
|
||||
const second = wrapper.find('[data-type=second] li:nth-child(2)');
|
||||
second.trigger('click');
|
||||
expect(wrapper.emitted().select[2][0]).toEqual(new Date(2019, 9, 10, 1, 1, 1));
|
||||
wrapper.setProps({ value: new Date(2019, 9, 10, 1, 1, 1) });
|
||||
const pm = wrapper.find('[data-type=ampm] li:nth-child(2)');
|
||||
pm.trigger('click');
|
||||
expect(wrapper.emitted().select[3][0]).toEqual(new Date(2019, 9, 10, 13, 1, 1));
|
||||
});
|
||||
|
||||
it('feat: disabledTime should not emit event', () => {
|
||||
wrapper = mount(TimePanel, {
|
||||
propsData: {
|
||||
value: new Date(2019, 9, 4, 12, 30, 30),
|
||||
disabledTime: date => date.getHours() < 10,
|
||||
},
|
||||
});
|
||||
const hour = wrapper.find('[data-type=hour] li:nth-child(2)');
|
||||
hour.trigger('click');
|
||||
expect(wrapper.emitted().select).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import TimeRange from '../src/time/time-range';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('TimeRange', () => {
|
||||
it('render: correct classes of the columns', () => {
|
||||
wrapper = mount(TimeRange, {
|
||||
propsData: {
|
||||
format: 'hh:mm a',
|
||||
minuteStep: 30,
|
||||
hourStep: 2,
|
||||
value: [new Date(2019, 9, 4, 8, 30, 0), new Date(2019, 9, 4, 18, 30, 0)],
|
||||
},
|
||||
});
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('feat: change the end time when start > end', () => {
|
||||
wrapper = mount(TimeRange, {
|
||||
propsData: {
|
||||
value: [new Date(2019, 9, 4, 8, 30, 0), new Date(2019, 9, 4, 18, 30, 0)],
|
||||
},
|
||||
});
|
||||
const hour = wrapper.find('[data-type=hour] li:nth-child(20)');
|
||||
hour.trigger('click');
|
||||
expect(wrapper.emitted().select[0][0]).toEqual([
|
||||
new Date(2019, 9, 4, 19, 30, 0),
|
||||
new Date(2019, 9, 4, 19, 30, 0),
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const libLocales = path.resolve(__dirname, '../../node_modules/date-format-parse/lib/locale');
|
||||
|
||||
const localLocalePath = path.resolve(__dirname, '../../src/locale');
|
||||
|
||||
const yearBeforeMonth = ['hu', 'ja', 'ko', 'lv', 'vi', 'zh-cn', 'zh-tw'];
|
||||
|
||||
const yearFormatMap = {
|
||||
'zh-cn': 'YYYY年',
|
||||
'zh-tw': 'YYYY年',
|
||||
};
|
||||
|
||||
const getTemplate = locale => {
|
||||
const formatLocale = locale.replace(/-(\w+)/, (m, p1) => p1.toLocaleUpperCase());
|
||||
const yearFormat = yearFormatMap[locale] || 'YYYY';
|
||||
|
||||
const template = `
|
||||
${locale === 'en' ? '' : "import DatePicker from 'vue2-datepicker';"}
|
||||
import ${formatLocale} from 'date-format-parse/lib/locale/${locale}';
|
||||
|
||||
const lang = {
|
||||
formatLocale: ${formatLocale},
|
||||
yearFormat: '${yearFormat}',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: ${yearBeforeMonth.indexOf(locale) === -1},
|
||||
};
|
||||
|
||||
${locale === 'en' ? '' : `DatePicker.locale('${locale}', lang);`}
|
||||
|
||||
export default lang;
|
||||
`;
|
||||
return template.trim();
|
||||
};
|
||||
|
||||
function generateLocales() {
|
||||
fs.readdirSync(libLocales).forEach(filename => {
|
||||
if (!/\.js$/.test(filename)) {
|
||||
return;
|
||||
}
|
||||
const name = filename.replace(/\.js$/, '');
|
||||
const data = getTemplate(name);
|
||||
fs.writeFile(path.join(localLocalePath, filename), data, err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
generateLocales();
|
||||
@@ -1,48 +0,0 @@
|
||||
const path = require('path')
|
||||
const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||
|
||||
module.exports = {
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
alias: {
|
||||
'@': path.join(__dirname, '..', 'src')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'vue-loader',
|
||||
options: {
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test:/\.scss$/,
|
||||
use: ['vue-style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: ['babel-loader'],
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|gif|svg)$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[name].[ext]?[hash]'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new VueLoaderPlugin()
|
||||
]
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
const merge = require('webpack-merge')
|
||||
const devWebpackConfig = require('./webpack.dev.config.js')
|
||||
|
||||
const webpackConfig = merge(devWebpackConfig, {
|
||||
devtool: 'source-map',
|
||||
mode: 'production',
|
||||
externals: {
|
||||
'vue': 'Vue',
|
||||
'@/index': 'DatePicker'
|
||||
},
|
||||
})
|
||||
|
||||
module.exports = webpackConfig
|
||||
@@ -1,50 +0,0 @@
|
||||
const path = require('path')
|
||||
const merge = require('webpack-merge')
|
||||
const baseWebpackConfig = require('./webpack.base.config.js')
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
|
||||
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
|
||||
const webpackConfig = merge(baseWebpackConfig, {
|
||||
mode: 'production',
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../lib'),
|
||||
filename: 'index.js',
|
||||
library: "DatePicker",
|
||||
libraryTarget: "umd"
|
||||
},
|
||||
plugins: [
|
||||
new OptimizeCSSPlugin({
|
||||
cssProcessorOptions: { safe: true }
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
const webpackConfigExtractCss = merge(baseWebpackConfig, {
|
||||
mode: 'production',
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../lib'),
|
||||
filename: 'datepicker.js',
|
||||
library: "DatePicker",
|
||||
libraryTarget: "umd"
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test:/\.scss$/,
|
||||
use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader']
|
||||
},
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: 'datepicker.css'
|
||||
}),
|
||||
new OptimizeCSSPlugin({
|
||||
cssProcessorOptions: { safe: true }
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
module.exports = [ webpackConfig, webpackConfigExtractCss]
|
||||
@@ -1,31 +0,0 @@
|
||||
const path = require('path')
|
||||
const merge = require('webpack-merge')
|
||||
const baseWebpackConfig = require('./webpack.base.config.js')
|
||||
|
||||
const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||
|
||||
const webpackConfig = merge(baseWebpackConfig, {
|
||||
mode: 'development',
|
||||
entry: './demo/index.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../demo'),
|
||||
filename: 'build.js'
|
||||
},
|
||||
externals: {
|
||||
'vue': 'Vue'
|
||||
},
|
||||
devServer: {
|
||||
historyApiFallback: {
|
||||
index: './demo/index.html'
|
||||
},
|
||||
noInfo: true,
|
||||
port: 9000,
|
||||
host: '0.0.0.0'
|
||||
},
|
||||
performance: {
|
||||
hints: false
|
||||
},
|
||||
devtool: 'source-map'
|
||||
})
|
||||
|
||||
module.exports = webpackConfig
|
||||
@@ -1,2 +0,0 @@
|
||||
!function(e){var t={};function n(a){if(t[a])return t[a].exports;var r=t[a]={i:a,l:!1,exports:{}};return e[a].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,a){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:a})},n.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=2)}([function(e,t){e.exports=Vue},function(e,t){e.exports=DatePicker},function(e,t,n){"use strict";n.r(t);var a=n(0),r=n.n(a),i=n(1),o=n.n(i);r.a.use(o.a),new r.a({el:"#app",data:function(){return{value1:new Date,value2:"",value3:new Date,value4:"",value5:"",value6:"",value7:"",value8:"",value9:"",value10:new Date,value11:new Date,value12:""}},methods:{getSource:function(e){var t=this,n=this.$createElement;return Object.keys(e).map(function(a){var i=e[a];return n("section",{class:"source"},[n("label",{class:"label"},[a," : "]),r.a.compile(i).render.call(t)])})},getPre:function(e){var t=this.$createElement;return Object.keys(e).map(function(n){var a=e[n].replace(/\n/g,"").replace(/\s+/g," ");return t("pre",{class:"pre"},[t("code",{class:"language-html"},[a])])})}},render:function(e){var t=this;return e("div",{attrs:{id:"app"}},[[{exam:{base:'<date-picker v-model="value1" valueType="format" lang="en" ></date-picker>',range:'<date-picker v-model="value2" range appendToBody></date-picker>',month:'<date-picker v-model="value10" lang="en" type="month" format="YYYY-MM"></date-picker>',year:'<date-picker v-model="value11" lang="en" type="year" format="YYYY"></date-picker>',time:'<date-picker v-model="value12" lang="en" type="time" format="HH:mm:ss" placeholder="Select Time"></date-picker>'}},{exam:{datetime:'\n <date-picker\n v-model="value3"\n lang="en"\n type="datetime"\n format="[on] MM-DD-YYYY [at] HH:mm"></date-picker>',"datetime with time-picker-options":'\n <date-picker\n v-model="value4"\n lang="en"\n type="datetime"\n appendToBody\n format="YYYY-MM-DD hh:mm:ss a"\n :time-picker-options="{\n start: \'00:00\',\n step: \'00:30\',\n end: \'23:30\'\n }"></date-picker>',"datetime with minute-step":'\n <date-picker\n v-model="value9"\n lang="en"\n type="datetime"\n format="YYYY-MM-DD hh:mm:ss a"\n :minute-step="10"\n ></date-picker>',"datetime range":'\n <date-picker\n v-model="value5"\n range\n type="datetime"\n lang="en"\n format="YYYY-MM-DD HH:mm:ss"></date-picker>'},tips:'if you use the datetime, you should set the format to "YYYY-MM-DD HH:mm:ss" which default is "YYY-MM-DD'},{exam:{"with confirm":'\n <date-picker\n v-model="value6"\n format="YYYY-MM-DD"\n lang="en"\n confirm></date-picker>',"datetime with confirm":'\n <date-picker\n v-model="value7"\n type="datetime"\n lang="en"\n format="YYYY-MM-DD hh:mm:ss"\n confirm></date-picker>',"range width confirm":'\n <date-picker\n v-model="value8"\n range\n lang="en"\n format="YYYY-MM-DD"\n confirm></date-picker>'},tips:'Recommend to use the confirm option when the type is "datetime" or "range" is true'}].map(function(n){return e("div",{class:"example"},[t.getSource(n.exam),n.tips?e("blockquote",{class:"tips"},[n.tips]):"",t.getPre(n.exam)])})])}})}]);
|
||||
//# sourceMappingURL=build.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,48 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>vue2-datepicker</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet" />
|
||||
<style>
|
||||
.example {
|
||||
padding: 20px;
|
||||
word-wrap: break-word;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
.source {
|
||||
display: inline-block;
|
||||
margin:20px;
|
||||
}
|
||||
.label{
|
||||
display: inline-block;
|
||||
margin-right: 1em;
|
||||
}
|
||||
.pre {
|
||||
padding: 8px;
|
||||
overflow: auto;
|
||||
font-size: 85%;
|
||||
line-height: 1.4;
|
||||
background-color: #f6f8fa;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.tips {
|
||||
margin: 0;
|
||||
padding: 0 1em;
|
||||
color: #6a737d;
|
||||
border-left: 0.25em solid #dfe2e5;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
|
||||
<script src="https://unpkg.com/vue2-datepicker/lib/index.js"></script>
|
||||
<script src="./build.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
|
||||
<script>window.hljs.initHighlightingOnLoad();</script>
|
||||
</body>
|
||||
</html>
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
/* eslint-disable no-new */
|
||||
import Vue from 'vue'
|
||||
import DatePicker from '@/index'
|
||||
|
||||
Vue.use(DatePicker)
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data () {
|
||||
return {
|
||||
value1: new Date(),
|
||||
value2: '',
|
||||
value3: new Date(),
|
||||
value4: '',
|
||||
value5: '',
|
||||
value6: '',
|
||||
value7: '',
|
||||
value8: '',
|
||||
value9: '',
|
||||
value10: new Date(),
|
||||
value11: new Date(),
|
||||
value12: '',
|
||||
value13: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getSource (obj) {
|
||||
return Object.keys(obj).map(key => {
|
||||
const value = obj[key]
|
||||
return (
|
||||
<section class="source">
|
||||
<label class="label">{key} : </label>
|
||||
{Vue.compile(value).render.call(this)}
|
||||
</section>
|
||||
)
|
||||
})
|
||||
},
|
||||
getPre (obj) {
|
||||
return Object.keys(obj).map(key => {
|
||||
const value = obj[key].replace(/\n/g, '').replace(/\s+/g, ' ')
|
||||
return (
|
||||
<pre class="pre">
|
||||
<code class="language-html">{value}</code>
|
||||
</pre>
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
render (h) {
|
||||
const example1 = {
|
||||
'base': '<date-picker v-model="value1" valueType="format" lang="en" ></date-picker>',
|
||||
'range': '<date-picker v-model="value2" range appendToBody></date-picker>',
|
||||
'month': '<date-picker v-model="value10" lang="en" type="month" format="YYYY-MM"></date-picker>',
|
||||
'year': '<date-picker v-model="value11" lang="en" type="year" format="YYYY"></date-picker>',
|
||||
'time': '<date-picker v-model="value12" lang="en" type="time" format="HH:mm:ss" placeholder="Select Time"></date-picker>'
|
||||
}
|
||||
const example2 = {
|
||||
'datetime': `
|
||||
<date-picker
|
||||
v-model="value3"
|
||||
lang="en"
|
||||
type="datetime"
|
||||
format="[on] MM-DD-YYYY [at] HH:mm"></date-picker>`,
|
||||
'datetime with time-picker-options': `
|
||||
<date-picker
|
||||
v-model="value4"
|
||||
lang="en"
|
||||
type="datetime"
|
||||
appendToBody
|
||||
format="YYYY-MM-DD hh:mm:ss a"
|
||||
:time-picker-options="{
|
||||
start: '00:00',
|
||||
step: '00:30',
|
||||
end: '23:30'
|
||||
}"></date-picker>`,
|
||||
'datetime with minute-step': `
|
||||
<date-picker
|
||||
v-model="value9"
|
||||
lang="en"
|
||||
type="datetime"
|
||||
format="YYYY-MM-DD hh:mm:ss a"
|
||||
:minute-step="10"
|
||||
></date-picker>`,
|
||||
'datetime with time-select-options': `
|
||||
<date-picker
|
||||
v-model="value13"
|
||||
lang="en"
|
||||
type="datetime"
|
||||
format="YYYY-MM-DD hh:mm:ss a"
|
||||
:time-select-options="{
|
||||
hours: [9, 10, 11, 12, 13, 14, 15, 16],
|
||||
minutes: [0, 10,20,30,40,50],
|
||||
seconds: []
|
||||
}"
|
||||
></date-picker>`,
|
||||
'datetime range': `
|
||||
<date-picker
|
||||
v-model="value5"
|
||||
range
|
||||
type="datetime"
|
||||
lang="en"
|
||||
format="YYYY-MM-DD HH:mm:ss"></date-picker>`
|
||||
}
|
||||
const example3 = {
|
||||
'with confirm': `
|
||||
<date-picker
|
||||
v-model="value6"
|
||||
format="YYYY-MM-DD"
|
||||
lang="en"
|
||||
confirm></date-picker>`,
|
||||
'datetime with confirm': `
|
||||
<date-picker
|
||||
v-model="value7"
|
||||
type="datetime"
|
||||
lang="en"
|
||||
format="YYYY-MM-DD hh:mm:ss"
|
||||
confirm></date-picker>`,
|
||||
'range width confirm': `
|
||||
<date-picker
|
||||
v-model="value8"
|
||||
range
|
||||
lang="en"
|
||||
format="YYYY-MM-DD"
|
||||
confirm></date-picker>`
|
||||
}
|
||||
const arr = [
|
||||
{
|
||||
exam: example1
|
||||
},
|
||||
{
|
||||
exam: example2,
|
||||
tips: 'if you use the datetime, you should set the format to "YYYY-MM-DD HH:mm:ss" which default is "YYY-MM-DD'
|
||||
},
|
||||
{
|
||||
exam: example3,
|
||||
tips: 'Recommend to use the confirm option when the type is "datetime" or "range" is true'
|
||||
}
|
||||
]
|
||||
return (
|
||||
<div id="app">
|
||||
{arr.map(obj => (
|
||||
<div class="example">
|
||||
{this.getSource(obj.exam)}
|
||||
{
|
||||
obj.tips
|
||||
? <blockquote class="tips">{obj.tips}</blockquote>
|
||||
: ''
|
||||
}
|
||||
{this.getPre(obj.exam)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/* eslint-disable global-require */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import fs from 'fs';
|
||||
import hljs from 'highlight.js/lib/highlight';
|
||||
import javascript from 'highlight.js/lib/languages/javascript';
|
||||
import xml from 'highlight.js/lib/languages/xml';
|
||||
import 'highlight.js/styles/atom-one-light.css';
|
||||
|
||||
import Container from './helper/container.vue';
|
||||
import Card from './helper/card.vue';
|
||||
import Basic from './demo/Basic.vue';
|
||||
import ValueType from './demo/ValueType.vue';
|
||||
import Range from './demo/Range.vue';
|
||||
import DisabledDateTime from './demo/DisabledDateTime.vue';
|
||||
import Shortcut from './demo/Shortcut.vue';
|
||||
import ControlTimePanel from './demo/ControlTimePanel.vue';
|
||||
import ControlOpen from './demo/ControlOpen.vue';
|
||||
import HideSeconds from './demo/HideSeconds.vue';
|
||||
import MinuteStep from './demo/MinuteStep.vue';
|
||||
import FixedTimeList from './demo/FixedTimeList.vue';
|
||||
import Disabled from './demo/Disabled.vue';
|
||||
|
||||
import en from './en.md';
|
||||
|
||||
hljs.registerLanguage('javascript', javascript);
|
||||
hljs.registerLanguage('xml', xml);
|
||||
|
||||
const components = [
|
||||
{
|
||||
id: 'Basic',
|
||||
component: Basic,
|
||||
code: fs.readFileSync(`${__dirname}/demo/Basic.vue`, 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 'ValueType',
|
||||
component: ValueType,
|
||||
code: fs.readFileSync(`${__dirname}/demo/ValueType.vue`, 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 'Range',
|
||||
component: Range,
|
||||
code: fs.readFileSync(`${__dirname}/demo/Range.vue`, 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 'HideSeconds',
|
||||
component: HideSeconds,
|
||||
code: fs.readFileSync(`${__dirname}/demo/HideSeconds.vue`, 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 'MinuteStep',
|
||||
component: MinuteStep,
|
||||
code: fs.readFileSync(`${__dirname}/demo/MinuteStep.vue`, 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 'FixedTimeList',
|
||||
component: FixedTimeList,
|
||||
code: fs.readFileSync(`${__dirname}/demo/FixedTimeList.vue`, 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 'DisabledDateTime',
|
||||
component: DisabledDateTime,
|
||||
code: fs.readFileSync(`${__dirname}/demo/DisabledDateTime.vue`, 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 'Disabled',
|
||||
component: Disabled,
|
||||
code: fs.readFileSync(`${__dirname}/demo/Disabled.vue`, 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 'Shortcut',
|
||||
component: Shortcut,
|
||||
code: fs.readFileSync(`${__dirname}/demo/Shortcut.vue`, 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 'ControlTimePanel',
|
||||
component: ControlTimePanel,
|
||||
code: fs.readFileSync(`${__dirname}/demo/ControlTimePanel.vue`, 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 'ControlOpen',
|
||||
component: ControlOpen,
|
||||
code: fs.readFileSync(`${__dirname}/demo/ControlOpen.vue`, 'utf8'),
|
||||
},
|
||||
];
|
||||
|
||||
function transformMd(text) {
|
||||
const array = text.split(/\n(?=<!-)/);
|
||||
const getId = s => {
|
||||
const result = s.match(/<!--\s*(\w+)\s*-->/);
|
||||
return result && result[1].trim();
|
||||
};
|
||||
const getTitleAndDescription = s => {
|
||||
const result = s.match(/<h.*?>(.*?)<\/h\d>/);
|
||||
if (!result) return null;
|
||||
const title = result[1];
|
||||
const description = s.slice(result[0].length + result.index);
|
||||
return {
|
||||
title: title.trim().replace(/&/g, '&'),
|
||||
description: description.trim(),
|
||||
};
|
||||
};
|
||||
const result = {};
|
||||
array.forEach(str => {
|
||||
const id = getId(str);
|
||||
if (id) {
|
||||
result[id] = getTitleAndDescription(str);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
const titleAndDescMap = transformMd(en);
|
||||
|
||||
const App = {
|
||||
name: 'App',
|
||||
mounted() {
|
||||
hljs.initHighlightingOnLoad();
|
||||
},
|
||||
render(h) {
|
||||
const menus = components.map(item => {
|
||||
return {
|
||||
id: item.id,
|
||||
...titleAndDescMap[item.id],
|
||||
};
|
||||
});
|
||||
return (
|
||||
<Container menus={menus}>
|
||||
{components.map(item => {
|
||||
const { component, id, code } = item;
|
||||
const props = {
|
||||
id,
|
||||
code,
|
||||
...titleAndDescMap[id],
|
||||
};
|
||||
return <Card {...{ props }}>{h(component)}</Card>;
|
||||
})}
|
||||
</Container>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1572655426603" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1257" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M336 784c-12.286 0-24.57-4.688-33.942-14.058l-224-224c-18.746-18.746-18.746-49.138 0-67.882l224-224c18.746-18.744 49.136-18.744 67.882 0 18.742 18.744 18.742 49.136 0 67.882L179.882 512l190.058 190.058c18.742 18.746 18.742 49.136 0 67.882C360.566 779.312 348.286 784 336 784z" p-id="1258"></path><path d="M688 784c12.286 0 24.57-4.688 33.942-14.058l224-224c18.746-18.746 18.746-49.138 0-67.882l-224-224c-18.746-18.744-49.136-18.744-67.882 0-18.742 18.744-18.742 49.136 0 67.882L844.118 512l-190.058 190.058c-18.742 18.746-18.742 49.136 0 67.882C663.434 779.312 675.714 784 688 784z" p-id="1259"></path></svg>
|
||||
|
After Width: | Height: | Size: 985 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1572655405733" class="icon" viewBox="0 0 1098 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1139" xmlns:xlink="http://www.w3.org/1999/xlink" width="214.453125" height="200"><defs><style type="text/css"></style></defs><path d="M352.548571 799.451429l-28.598857 28.598857q-5.705143 5.705143-13.165714 5.705143t-13.165714-5.705143l-266.313143-266.313143q-5.705143-5.705143-5.705143-13.165714t5.705143-13.165714l266.313143-266.313143q5.705143-5.705143 13.165714-5.705143t13.165714 5.705143l28.598857 28.598857q5.705143 5.705143 5.705143 13.165714t-5.705143 13.165714l-224.548571 224.548571 224.548571 224.548571q5.705143 5.705143 5.705143 13.165714t-5.705143 13.165714zM690.322286 189.732571l-213.138286 737.718857q-2.267429 7.460571-8.850286 11.117714t-13.458286 1.462857l-35.401143-9.728q-7.460571-2.267429-11.117714-8.850286t-1.462857-13.970286l213.138286-737.718857q2.267429-7.460571 8.850286-11.117714t13.458286-1.462857l35.401143 9.728q7.460571 2.267429 11.117714 8.850286t1.462857 13.970286zM1065.691429 561.737143l-266.313143 266.313143q-5.705143 5.705143-13.165714 5.705143t-13.165714-5.705143l-28.598857-28.598857q-5.705143-5.705143-5.705143-13.165714t5.705143-13.165714l224.548571-224.548571-224.548571-224.548571q-5.705143-5.705143-5.705143-13.165714t5.705143-13.165714l28.598857-28.598857q5.705143-5.705143 13.165714-5.705143t13.165714 5.705143l266.313143 266.313143q5.705143 5.705143 5.705143 13.165714t-5.705143 13.165714z" p-id="1140"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="box">
|
||||
<section>
|
||||
<p>date (default)</p>
|
||||
<date-picker
|
||||
v-model="value1"
|
||||
format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="Select date"
|
||||
></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>month</p>
|
||||
<date-picker v-model="value2" type="month" placeholder="Select month"></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>year</p>
|
||||
<date-picker v-model="value3" type="year" placeholder="Select year"></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>datetime</p>
|
||||
<date-picker v-model="value4" type="datetime" placeholder="Select datetime"></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>time</p>
|
||||
<date-picker v-model="value5" type="time" placeholder="Select time"></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>week</p>
|
||||
<date-picker v-model="value6" type="week" placeholder="Select week"></date-picker>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Basic',
|
||||
data() {
|
||||
return {
|
||||
value1: null,
|
||||
value2: null,
|
||||
value3: null,
|
||||
value4: null,
|
||||
value5: null,
|
||||
value6: null,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<date-picker
|
||||
v-model="value"
|
||||
value-type="format"
|
||||
type="time"
|
||||
:open.sync="open"
|
||||
placeholder="Select time"
|
||||
@change="handleChange"
|
||||
></date-picker>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ControlOpen',
|
||||
data() {
|
||||
return {
|
||||
value: null,
|
||||
open: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleChange(value, type) {
|
||||
if (type === 'second') {
|
||||
this.open = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="box">
|
||||
<section>
|
||||
<date-picker
|
||||
v-model="value1"
|
||||
type="datetime"
|
||||
placeholder="Select datetime"
|
||||
:show-time-panel="showTimePanel"
|
||||
@close="handleOpenChange"
|
||||
>
|
||||
<template v-slot:footer>
|
||||
<button class="mx-btn mx-btn-text" @click="toggleTimePanel">
|
||||
{{ showTimePanel ? 'select date' : 'select time' }}
|
||||
</button>
|
||||
</template>
|
||||
</date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<date-picker
|
||||
v-model="value2"
|
||||
type="datetime"
|
||||
placeholder="Select datetime range"
|
||||
range
|
||||
:show-time-panel="showTimeRangePanel"
|
||||
@close="handleRangeClose"
|
||||
>
|
||||
<template v-slot:footer>
|
||||
<button class="mx-btn mx-btn-text" @click="toggleTimeRangePanel">
|
||||
{{ showTimeRangePanel ? 'select date' : 'select time' }}
|
||||
</button>
|
||||
</template>
|
||||
</date-picker>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
value1: null,
|
||||
value2: [],
|
||||
showTimePanel: false,
|
||||
showTimeRangePanel: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleTimePanel() {
|
||||
this.showTimePanel = !this.showTimePanel;
|
||||
},
|
||||
toggleTimeRangePanel() {
|
||||
this.showTimeRangePanel = !this.showTimeRangePanel;
|
||||
},
|
||||
handleOpenChange() {
|
||||
this.showTimePanel = false;
|
||||
},
|
||||
handleRangeClose() {
|
||||
this.showTimeRangePanel = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="box">
|
||||
<section>
|
||||
<p>disabled = "true"</p>
|
||||
<date-picker v-model="value1" disabled></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>editable = "false"</p>
|
||||
<date-picker v-model="value2" :editable="false"></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>clearable = "false"</p>
|
||||
<date-picker v-model="value3" :clearable="false"></date-picker>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
value1: new Date(),
|
||||
value2: new Date(),
|
||||
value3: new Date(),
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="box">
|
||||
<section>
|
||||
<p>date not before today</p>
|
||||
<date-picker v-model="value1" :disabled-date="notBeforeToday"></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>date not after today</p>
|
||||
<date-picker v-model="value2" :disabled-date="notAfterToday"></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>time not before 09:00</p>
|
||||
<date-picker
|
||||
v-model="value3"
|
||||
value-type="format"
|
||||
type="time"
|
||||
placeholder="HH:mm:ss"
|
||||
:default-value="new Date().setHours(9, 0, 0)"
|
||||
:disabled-time="notBeforeNine"
|
||||
></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>datetime not before 2019-10-09 12:00</p>
|
||||
<date-picker
|
||||
v-model="value4"
|
||||
type="datetime"
|
||||
:disabled-date="notBeforeDate"
|
||||
:disabled-time="notBeforeTime"
|
||||
value-type="format"
|
||||
></date-picker>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
value1: new Date(),
|
||||
value2: new Date(),
|
||||
value3: '',
|
||||
value4: '',
|
||||
value5: '',
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
notBeforeToday(date) {
|
||||
return date < today;
|
||||
},
|
||||
notAfterToday(date) {
|
||||
return date > today;
|
||||
},
|
||||
notBeforeNine(date) {
|
||||
return date.getHours() < 9;
|
||||
},
|
||||
notBeforeDate(date) {
|
||||
return date < new Date(2019, 9, 9);
|
||||
},
|
||||
notBeforeTime(date) {
|
||||
return date < new Date(2019, 9, 9, 12);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div>
|
||||
<date-picker
|
||||
v-model="value"
|
||||
:time-picker-options="{
|
||||
start: '08:30',
|
||||
step: '00:30',
|
||||
end: '18:30',
|
||||
}"
|
||||
format="hh:mm a"
|
||||
type="time"
|
||||
placeholder="hh:mm a"
|
||||
></date-picker>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FixedTimeList',
|
||||
data() {
|
||||
return {
|
||||
value: null,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div>
|
||||
<date-picker
|
||||
v-model="value"
|
||||
format="hh:mm a"
|
||||
value-type="format"
|
||||
type="time"
|
||||
placeholder="hh:mm a"
|
||||
></date-picker>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ControlOpen',
|
||||
data() {
|
||||
return {
|
||||
value: '',
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div>
|
||||
<date-picker
|
||||
v-model="value"
|
||||
:minute-step="30"
|
||||
:hour-options="hours"
|
||||
format="HH:mm"
|
||||
value-type="format"
|
||||
type="time"
|
||||
placeholder="HH:mm"
|
||||
></date-picker>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ControlOpen',
|
||||
data() {
|
||||
return {
|
||||
value: '',
|
||||
hours: Array.from({ length: 10 }).map((_, i) => i + 8),
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="box">
|
||||
<section>
|
||||
<p>date range</p>
|
||||
<date-picker v-model="value1" type="date" range placeholder="Select date range"></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>datetime range</p>
|
||||
<date-picker
|
||||
v-model="value2"
|
||||
type="datetime"
|
||||
range
|
||||
placeholder="Select datetime range"
|
||||
></date-picker>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Range',
|
||||
data() {
|
||||
return {
|
||||
value1: [new Date(2019, 9, 8), new Date(2019, 9, 19)],
|
||||
value2: [],
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="box">
|
||||
<section>
|
||||
<p>shortcuts</p>
|
||||
<date-picker v-model="value1" :shortcuts="shortcuts" placeholder="Select date"></date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>header slot</p>
|
||||
<date-picker v-model="value2" placeholder="Select date">
|
||||
<template v-slot:header="{ emit }">
|
||||
<button class="mx-btn mx-btn-text" @click="emit(new Date())">Today</button>
|
||||
</template>
|
||||
</date-picker>
|
||||
</section>
|
||||
<section>
|
||||
<p>footer slot</p>
|
||||
<date-picker v-model="value3" range placeholder="Select date range">
|
||||
<template v-slot:footer="{ emit }">
|
||||
<div style="text-align: left">
|
||||
<button class="mx-btn mx-btn-text" @click="selectNextThreeDay(emit)">
|
||||
NextThreeDay
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</date-picker>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Basic',
|
||||
data() {
|
||||
return {
|
||||
value1: null,
|
||||
value2: null,
|
||||
value3: null,
|
||||
shortcuts: [
|
||||
{
|
||||
text: 'Today',
|
||||
onClick() {
|
||||
const date = new Date();
|
||||
// return a Date
|
||||
return date;
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Yesterday',
|
||||
onClick() {
|
||||
const date = new Date();
|
||||
date.setTime(date.getTime() - 3600 * 1000 * 24);
|
||||
return date;
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
selectNextThreeDay(emit) {
|
||||
const start = new Date();
|
||||
const end = new Date();
|
||||
end.setTime(end.getTime() + 3 * 24 * 3600 * 1000);
|
||||
const date = [start, end];
|
||||
emit(date);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="box">
|
||||
<section>
|
||||
<p>format</p>
|
||||
<date-picker v-model="value2" value-type="format" format="YYYY-MM-DD"></date-picker>
|
||||
<p>
|
||||
<code>v-model = {{ value2 }}</code>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<p>date (Date Object)</p>
|
||||
<date-picker v-model="value1" value-type="date"></date-picker>
|
||||
<p>
|
||||
<code>v-model = {{ value1 }}</code>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<p>timestamp</p>
|
||||
<date-picker v-model="value3" value-type="timestamp"></date-picker>
|
||||
<p>
|
||||
<code>v-model = {{ value3 }}</code>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<p>DD/MM/YYYY</p>
|
||||
<date-picker v-model="value4" value-type="DD/MM/YYYY" format="YYYY-MM-DD"></date-picker>
|
||||
<p>
|
||||
<code>v-model = {{ value4 }}</code>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ValueType',
|
||||
data() {
|
||||
return {
|
||||
value1: new Date(2019, 9, 9),
|
||||
value2: '2019-10-09',
|
||||
value3: new Date(2019, 9, 9).getTime(),
|
||||
value4: '09/10/2019',
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,76 @@
|
||||
<!-- Basic -->
|
||||
|
||||
### Basic
|
||||
|
||||
You can select or input a date, month, year, time or datetime
|
||||
|
||||
<!-- ValueType -->
|
||||
|
||||
### ValueType
|
||||
|
||||
You can set the type of the v-model value by `valueType`.
|
||||
|
||||
- "format": return a string same as the input value.
|
||||
- "date"(default): return a Date Object.
|
||||
- "timestamp": return a Number.
|
||||
- token: a accepted format string pattern.
|
||||
|
||||
<!-- Range -->
|
||||
|
||||
### Range
|
||||
|
||||
Support to select a date range.
|
||||
|
||||
<!-- DisabledDateTime -->
|
||||
|
||||
### DisabledDate & DisabledTime
|
||||
|
||||
Disabled part of dates and time by `disabledDate` and `disabledTime` respectively
|
||||
|
||||
<!-- Disabled -->
|
||||
|
||||
### Disabled & editable
|
||||
|
||||
- disabled: A disabled state of the DatePicker
|
||||
- editable: Whether to allow manual input
|
||||
|
||||
<!-- Shortcut -->
|
||||
|
||||
### Shortcut
|
||||
|
||||
You can set `shortcuts` to improve user experience.
|
||||
You can also use the header slot or the footer slot
|
||||
|
||||
<!-- ControlTimePanel -->
|
||||
|
||||
### Control TimePanel visible
|
||||
|
||||
Determing whether the time panel is displayed by `showTimePanel`.
|
||||
The time panel is displayed after the date is selected by default.
|
||||
|
||||
<!-- ControlOpen -->
|
||||
|
||||
### Control Open
|
||||
|
||||
You can use the prop `open` to control the visible of popup.
|
||||
The time picker doesn't automatically close by default.
|
||||
This example automatically close the popup when the seconds is selected.
|
||||
|
||||
<!-- HideSeconds -->
|
||||
|
||||
### Hide Seconds & Show AM/PM
|
||||
|
||||
The columns of the time Picker is displayed according to the value of format by default.
|
||||
You can also set `showHour` `showMinute` `showSecond` to override the default value
|
||||
|
||||
<!-- MinuteStep -->
|
||||
|
||||
### Interval and custom time options
|
||||
|
||||
Show stepped options by `hourStep` `minuteStep` `secondStep` or show custom options by `hourOptions` `minuteOptions` `secondOptions`
|
||||
|
||||
<!-- FixedTimeList -->
|
||||
|
||||
### Select fixed time list
|
||||
|
||||
You can provide a list of fixed time for users to choose by `timePickerOptions`
|
||||
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<div class="card">
|
||||
<section :id="id" class="card-title" v-html="title"></section>
|
||||
<section class="card-description markdown-body" v-html="description"></section>
|
||||
<section class="card-demo markdown-body">
|
||||
<slot></slot>
|
||||
</section>
|
||||
<section class="card-actions" @click="handleExpand">
|
||||
<img v-if="codeVisible" alt="show code" class="icon-expand" src="../assets/expand.svg" />
|
||||
<img v-else alt="hide code" class="icon-expand" src="../assets/collapse.svg" />
|
||||
</section>
|
||||
<section v-show="codeVisible" class="card-code">
|
||||
<pre>
|
||||
<code class="vue">{{code}}</code>
|
||||
</pre>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// TODO: 替换img 图标, 本地
|
||||
|
||||
export default {
|
||||
name: 'DemoCard',
|
||||
props: {
|
||||
id: String,
|
||||
title: String,
|
||||
description: String,
|
||||
code: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
codeVisible: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleExpand() {
|
||||
this.codeVisible = !this.codeVisible;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
$border-color: #ebedf0;
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
color: #314659;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 4px;
|
||||
& + & {
|
||||
margin-top: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-demo {
|
||||
padding: 30px 24px;
|
||||
color: #213649;
|
||||
border-top: 1px solid $border-color;
|
||||
.box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
> section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-title {
|
||||
position: absolute;
|
||||
margin-top: -10px;
|
||||
margin-left: 14px;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
font-weight: 700;
|
||||
padding: 0 10px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.card-description {
|
||||
padding: 12px 24px;
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
margin: 10px 0;
|
||||
}
|
||||
ul,
|
||||
ol {
|
||||
padding-left: 30px;
|
||||
}
|
||||
code {
|
||||
margin: 0 1px;
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
background-color: rgba(27, 31, 35, 0.05);
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
position: relative;
|
||||
border-top: 1px solid $border-color;
|
||||
height: 36px;
|
||||
text-align: center;
|
||||
color: #d3dce6;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
user-select: none;
|
||||
&:hover {
|
||||
box-shadow: 0 0 8px 0 rgba(232, 237, 250, 0.6), 0 2px 4px 0 rgba(232, 237, 250, 0.5);
|
||||
}
|
||||
}
|
||||
.icon-expand {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -8px;
|
||||
margin-top: -8px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.card-code {
|
||||
border-top: 1px solid $border-color;
|
||||
pre {
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
&::after,
|
||||
&::before {
|
||||
width: 0;
|
||||
}
|
||||
// 添加优先级, 覆盖 引入的样式
|
||||
code {
|
||||
display: block;
|
||||
background: #fff;
|
||||
color: #314659;
|
||||
line-height: 2;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
padding: 16px 32px;
|
||||
border-radius: 2px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="sidebar">
|
||||
<a
|
||||
v-for="(menu, i) in menus"
|
||||
:key="menu.id"
|
||||
:class="{ active: activeIndex === i }"
|
||||
:href="`#${menu.id}`"
|
||||
>{{ menu.title }}</a
|
||||
>
|
||||
</div>
|
||||
<div ref="main" class="main" @scroll="handleScroll">
|
||||
<div class="content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
menus: Array,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
offset: [],
|
||||
activeIndex: 0,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
const scrollEl = this.$refs.main;
|
||||
const els = document.querySelectorAll('.card-title');
|
||||
const { top } = scrollEl.getBoundingClientRect();
|
||||
const offset = [];
|
||||
for (let i = 0; i < els.length; i++) {
|
||||
const el = els[i];
|
||||
offset.push(el.getBoundingClientRect().top - top);
|
||||
}
|
||||
this.offset = offset;
|
||||
},
|
||||
methods: {
|
||||
handleScroll(evt) {
|
||||
const value = evt.currentTarget.scrollTop - 10;
|
||||
const index = this.offset.findIndex(v => v > value);
|
||||
this.activeIndex = index;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
.sidebar {
|
||||
border-right: 1px solid #ebedf0;
|
||||
width: 280px;
|
||||
overflow: auto;
|
||||
a {
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
display: block;
|
||||
padding-left: 16px;
|
||||
overflow: hidden;
|
||||
color: #314659;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
border-left: 1px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: mix(#fff, #1284e7, 0.8);
|
||||
}
|
||||
&.active {
|
||||
color: #1284e7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 20px;
|
||||
p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>Document</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/normalize.css@8.0.1/normalize.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="./index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import Vue from 'vue';
|
||||
|
||||
import DatePicker from '../src/index';
|
||||
|
||||
// import '../src/locale/zh-cn';
|
||||
|
||||
import '../src/style/index.scss';
|
||||
|
||||
import App from './app';
|
||||
|
||||
Vue.use(DatePicker);
|
||||
|
||||
new Vue({
|
||||
render: h => h(App),
|
||||
}).$mount('#app');
|
||||
Generated
+8239
-12155
File diff suppressed because it is too large
Load Diff
+81
-70
@@ -1,20 +1,29 @@
|
||||
{
|
||||
"name": "vue2-datepicker",
|
||||
"description": "A Datepicker Component For Vue2",
|
||||
"main": "lib/index.js",
|
||||
"main": "index.js",
|
||||
"module": "index.esm.js",
|
||||
"alias": {
|
||||
"vue2-datepicker": "./src/index.js"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"src"
|
||||
"/locale",
|
||||
"/index.*"
|
||||
],
|
||||
"version": "2.13.3",
|
||||
"version": "2.12.0",
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development webpack-dev-server --hot --open --config build/webpack.dev.config.js",
|
||||
"demo": "cross-env NODE_ENV=production webpack --progress --hide-modules --config build/webpack.demo.config.js",
|
||||
"build": "npm run lint && npm test && cross-env NODE_ENV=production webpack --progress --hide-modules --config build/webpack.deploy.config.js",
|
||||
"lint": "eslint src/**/* test/**/*",
|
||||
"version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",
|
||||
"test:push": "jest --coverage --coverageReporters=text-lcov | coveralls",
|
||||
"dev": "parcel ./example/index.html --open --port 2235",
|
||||
"build:index": "rollup -c",
|
||||
"build:locale": "rollup -c rollup.locale.config.js",
|
||||
"build:css": "sass --style=compressed --no-source-map src/style/index.scss index.css",
|
||||
"build": "npm run lint && npm run test && npm run clean && npm run build:index && npm run build:css && npm run build:locale",
|
||||
"clean": "rimraf locale index.*",
|
||||
"lint": "eslint src/**/*.{js,vue}",
|
||||
"deploy:build": "parcel build ./example/index.html --public-url ./ --out-dir _site --no-source-maps --no-minify",
|
||||
"format": "prettier --write src/**/*",
|
||||
"test": "jest",
|
||||
"cov": "jest --coverage --coverageReporters=text-lcov | coveralls",
|
||||
"version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",
|
||||
"release": "bash build/git.sh && bash build/release.sh"
|
||||
},
|
||||
"repository": {
|
||||
@@ -23,7 +32,9 @@
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
"datepicker"
|
||||
"calendar",
|
||||
"datepicker",
|
||||
"datetimepicker"
|
||||
],
|
||||
"author": "xiemengxiong",
|
||||
"license": "MIT",
|
||||
@@ -31,58 +42,54 @@
|
||||
"url": "https://github.com/mengxiong10/vue2-datepicker/issues"
|
||||
},
|
||||
"homepage": "https://github.com/mengxiong10/vue2-datepicker#readme",
|
||||
"peerDependencies": {
|
||||
"vue": ">=2.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"fecha": "^2.3.3"
|
||||
"date-fns": "^2.0.1",
|
||||
"date-format-parse": "^0.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^7.2.1",
|
||||
"@commitlint/config-conventional": "^7.1.2",
|
||||
"@vue/test-utils": "^1.0.0-beta.20",
|
||||
"autoprefixer": "^7.2.6",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^8.2.3",
|
||||
"@babel/core": "^7.5.5",
|
||||
"@babel/preset-env": "^7.5.5",
|
||||
"@commitlint/cli": "^8.2.0",
|
||||
"@commitlint/config-conventional": "^8.2.0",
|
||||
"@vue/babel-helper-vue-jsx-merge-props": "^1.0.0",
|
||||
"@vue/babel-plugin-transform-vue-jsx": "^1.0.0",
|
||||
"@vue/component-compiler-utils": "^3.0.0",
|
||||
"@vue/test-utils": "^1.0.0-beta.29",
|
||||
"acorn": "^7.0.0",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-eslint": "^10.0.2",
|
||||
"babel-helper-vue-jsx-merge-props": "^2.0.3",
|
||||
"babel-jest": "^23.0.1",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-plugin-jsx-v-model": "^2.0.3",
|
||||
"babel-jest": "^24.9.0",
|
||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||
"babel-plugin-transform-vue-jsx": "^3.7.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"coveralls": "^3.0.1",
|
||||
"cross-env": "^5.1.6",
|
||||
"css-loader": "^0.25.0",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-standard": "^11.0.0",
|
||||
"eslint-friendly-formatter": "^3.0.0",
|
||||
"eslint-loader": "^2.0.0",
|
||||
"eslint-plugin-import": "^2.12.0",
|
||||
"eslint-plugin-node": "^6.0.1",
|
||||
"eslint-plugin-promise": "^3.8.0",
|
||||
"eslint-plugin-standard": "^3.1.0",
|
||||
"eslint-plugin-vue": "^4.5.0",
|
||||
"file-loader": "^1.1.11",
|
||||
"highlight.js": "^9.12.0",
|
||||
"husky": "^1.2.0",
|
||||
"jest": "^23.0.1",
|
||||
"lint-staged": "^8.1.0",
|
||||
"mini-css-extract-plugin": "^0.4.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"optimize-css-assets-webpack-plugin": "^4.0.2",
|
||||
"postcss-loader": "^2.1.5",
|
||||
"sass-loader": "^6.0.7",
|
||||
"vue": "^2.5.16",
|
||||
"vue-jest": "^2.6.0",
|
||||
"vue-loader": "^15.2.1",
|
||||
"vue-template-compiler": "^2.5.16",
|
||||
"webpack": "^4.9.1",
|
||||
"webpack-cli": "^2.1.4",
|
||||
"webpack-dev-server": "^3.1.4",
|
||||
"webpack-merge": "^4.1.2"
|
||||
"babel-plugin-transform-vue-jsx": "^4.0.1",
|
||||
"conventional-changelog-cli": "^2.0.25",
|
||||
"core-js": "^3.3.5",
|
||||
"eslint": "^6.2.2",
|
||||
"eslint-config-airbnb-base": "^14.0.0",
|
||||
"eslint-config-prettier": "^6.1.0",
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-vue": "^5.2.3",
|
||||
"flush-promises": "^1.0.2",
|
||||
"highlight.js": "^9.15.10",
|
||||
"husky": "^3.0.9",
|
||||
"jest": "^24.9.0",
|
||||
"jest-serializer-vue": "^2.0.2",
|
||||
"lint-staged": "^9.4.2",
|
||||
"marked": "^0.7.0",
|
||||
"parcel-bundler": "^1.12.3",
|
||||
"rimraf": "^3.0.0",
|
||||
"rollup": "^1.26.0",
|
||||
"rollup-plugin-auto-external": "^2.0.0",
|
||||
"rollup-plugin-babel": "^4.3.3",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-terser": "^5.1.2",
|
||||
"rollup-plugin-vue": "^5.1.2",
|
||||
"sass": "^1.22.10",
|
||||
"vue": "^2.6.10",
|
||||
"vue-hot-reload-api": "^2.3.3",
|
||||
"vue-jest": "^3.0.5",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
@@ -93,9 +100,16 @@
|
||||
"^.+\\.js$": "<rootDir>/node_modules/babel-jest",
|
||||
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest"
|
||||
},
|
||||
"snapshotSerializers": [
|
||||
"jest-serializer-vue"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"^@/(.*)$": "<rootDir>/src/$1"
|
||||
}
|
||||
"vue2-datepicker": "<rootDir>/src"
|
||||
},
|
||||
"coverageReporters": [
|
||||
"text",
|
||||
"text-summary"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
@@ -104,18 +118,15 @@
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.{js,jsx}": [
|
||||
"**/*.{js,vue}": [
|
||||
"eslint --fix",
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie < 10"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
import vue from 'rollup-plugin-vue';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import pkg from './package.json';
|
||||
|
||||
const external = id => /date-fns/.test(id);
|
||||
|
||||
const input = 'src/index.js';
|
||||
|
||||
const plugins = [
|
||||
resolve({
|
||||
extensions: ['.js', '.vue', '.jsx', '.json'],
|
||||
}),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
}),
|
||||
commonjs(),
|
||||
vue(),
|
||||
];
|
||||
|
||||
const esm = {
|
||||
external,
|
||||
input,
|
||||
output: [
|
||||
{
|
||||
file: pkg.module,
|
||||
format: 'esm',
|
||||
exports: 'named',
|
||||
},
|
||||
],
|
||||
plugins,
|
||||
};
|
||||
|
||||
const umd = {
|
||||
input,
|
||||
output: [
|
||||
{
|
||||
file: pkg.main,
|
||||
format: 'umd',
|
||||
name: 'DatePicker',
|
||||
sourcemap: true,
|
||||
},
|
||||
{
|
||||
file: pkg.main.replace(/\.js$/, '.min.js'),
|
||||
format: 'umd',
|
||||
name: 'DatePicker',
|
||||
sourcemap: true,
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
...plugins,
|
||||
terser({
|
||||
include: [/^.+\.min\.js$/],
|
||||
sourcemap: true,
|
||||
output: { comments: false },
|
||||
compress: {
|
||||
keep_infinity: true,
|
||||
pure_getters: true,
|
||||
},
|
||||
warnings: true,
|
||||
ecma: 5,
|
||||
toplevel: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
export default [esm, umd];
|
||||
@@ -0,0 +1,56 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const localePath = path.resolve(__dirname, 'src/locale');
|
||||
const fileList = fs.readdirSync(localePath);
|
||||
|
||||
const plugins = [
|
||||
resolve({
|
||||
extensions: ['.js', '.json'],
|
||||
}),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
}),
|
||||
commonjs(),
|
||||
];
|
||||
|
||||
const umd = fileList.map(file => {
|
||||
const input = path.join(localePath, file);
|
||||
const external = ['vue2-datepicker'];
|
||||
const name = path.basename(file, '.js').replace(/-/g, '');
|
||||
return {
|
||||
input,
|
||||
plugins,
|
||||
external,
|
||||
output: {
|
||||
file: `locale/${file}`,
|
||||
format: 'umd',
|
||||
name: `DatePicker.lang.${name}`,
|
||||
globals: {
|
||||
'vue2-datepicker': 'DatePicker',
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const esm = fileList.map(file => {
|
||||
const input = path.join(localePath, file);
|
||||
const external = id => !id.startsWith('.') && !id.startsWith('/');
|
||||
return {
|
||||
input,
|
||||
plugins,
|
||||
external,
|
||||
output: {
|
||||
file: `locale/es/${file}`,
|
||||
format: 'esm',
|
||||
exports: 'named',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default [...esm, ...umd];
|
||||
@@ -1,458 +0,0 @@
|
||||
<template>
|
||||
<div class="mx-calendar" :class="'mx-calendar-panel-' + panel.toLowerCase()">
|
||||
<div class="mx-calendar-header">
|
||||
<a v-show="panel !== 'TIME'" class="mx-icon-last-year" @click="handleIconYear(-1)">«</a>
|
||||
<a v-show="panel === 'DATE'" class="mx-icon-last-month" @click="handleIconMonth(-1)">‹</a>
|
||||
<a v-show="panel !== 'TIME'" class="mx-icon-next-year" @click="handleIconYear(1)">»</a>
|
||||
<a v-show="panel === 'DATE'" class="mx-icon-next-month" @click="handleIconMonth(1)">›</a>
|
||||
<a
|
||||
v-show="panel === 'DATE'"
|
||||
class="mx-current-month"
|
||||
@click="handleBtnMonth"
|
||||
>{{months[calendarMonth]}}</a>
|
||||
<a
|
||||
v-show="panel === 'DATE' || panel === 'MONTH'"
|
||||
class="mx-current-year"
|
||||
@click="handleBtnYear"
|
||||
>{{calendarYear}}</a>
|
||||
<a v-show="panel === 'YEAR'" class="mx-current-year">{{yearHeader}}</a>
|
||||
<a v-show="panel === 'TIME'" class="mx-time-header" @click="handleTimeHeader">{{timeHeader}}</a>
|
||||
</div>
|
||||
<div class="mx-calendar-content">
|
||||
<panel-date
|
||||
v-show="panel === 'DATE'"
|
||||
:value="value"
|
||||
:date-format="dateFormat"
|
||||
:calendar-month="calendarMonth"
|
||||
:calendar-year="calendarYear"
|
||||
:start-at="startAt"
|
||||
:end-at="endAt"
|
||||
:first-day-of-week="firstDayOfWeek"
|
||||
:disabled-date="isDisabledDate"
|
||||
@select="selectDate"
|
||||
/>
|
||||
<panel-year
|
||||
v-show="panel === 'YEAR'"
|
||||
:value="value"
|
||||
:disabled-year="isDisabledYear"
|
||||
:first-year="firstYear"
|
||||
@select="selectYear"
|
||||
/>
|
||||
<panel-month
|
||||
v-show="panel === 'MONTH'"
|
||||
:value="value"
|
||||
:disabled-month="isDisabledMonth"
|
||||
:calendar-year="calendarYear"
|
||||
@select="selectMonth"
|
||||
/>
|
||||
<panel-time
|
||||
v-show="panel === 'TIME'"
|
||||
:minute-step="minuteStep"
|
||||
:time-picker-options="timePickerOptions"
|
||||
:time-select-options="timeSelectOptions"
|
||||
:value="value"
|
||||
:disabled-time="isDisabledTime"
|
||||
:time-type="timeType"
|
||||
@select="selectTime"
|
||||
@pick="pickTime"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isValidDate, isDateObejct, formatDate } from '@/utils/index'
|
||||
import locale from '@/mixins/locale'
|
||||
import emitter from '@/mixins/emitter'
|
||||
import scrollIntoView from '@/utils/scroll-into-view'
|
||||
import PanelDate from '@/panel/date'
|
||||
import PanelYear from '@/panel/year'
|
||||
import PanelMonth from '@/panel/month'
|
||||
import PanelTime from '@/panel/time'
|
||||
|
||||
export default {
|
||||
name: 'CalendarPanel',
|
||||
components: { PanelDate, PanelYear, PanelMonth, PanelTime },
|
||||
mixins: [locale, emitter],
|
||||
props: {
|
||||
value: {
|
||||
default: null,
|
||||
validator: function (val) {
|
||||
return val === null || isValidDate(val)
|
||||
}
|
||||
},
|
||||
startAt: null,
|
||||
endAt: null,
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'date' // ['date', 'datetime'] zendy added 'month', 'year', mxie added "time"
|
||||
},
|
||||
dateFormat: {
|
||||
type: String,
|
||||
default: 'YYYY-MM-DD'
|
||||
},
|
||||
index: Number,
|
||||
// below user set
|
||||
defaultValue: {
|
||||
validator: function (val) {
|
||||
return isValidDate(val)
|
||||
}
|
||||
},
|
||||
firstDayOfWeek: {
|
||||
default: 7,
|
||||
type: Number,
|
||||
validator: val => val >= 1 && val <= 7
|
||||
},
|
||||
notBefore: {
|
||||
default: null,
|
||||
validator: function (val) {
|
||||
return !val || isValidDate(val)
|
||||
}
|
||||
},
|
||||
notAfter: {
|
||||
default: null,
|
||||
validator: function (val) {
|
||||
return !val || isValidDate(val)
|
||||
}
|
||||
},
|
||||
disabledDays: {
|
||||
type: [Array, Function],
|
||||
default: function () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
minuteStep: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
validator: val => val >= 0 && val <= 60
|
||||
},
|
||||
timeSelectOptions: {
|
||||
type: Object,
|
||||
default () {
|
||||
return null
|
||||
}
|
||||
},
|
||||
timePickerOptions: {
|
||||
type: [Object, Function],
|
||||
default () {
|
||||
return null
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
const now = this.getNow(this.value)
|
||||
const calendarYear = now.getFullYear()
|
||||
const calendarMonth = now.getMonth()
|
||||
const firstYear = Math.floor(calendarYear / 10) * 10
|
||||
return {
|
||||
panel: 'NONE',
|
||||
dates: [],
|
||||
calendarMonth,
|
||||
calendarYear,
|
||||
firstYear
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
now: {
|
||||
get () {
|
||||
return new Date(this.calendarYear, this.calendarMonth).getTime()
|
||||
},
|
||||
set (val) {
|
||||
const now = new Date(val)
|
||||
this.calendarYear = now.getFullYear()
|
||||
this.calendarMonth = now.getMonth()
|
||||
}
|
||||
},
|
||||
timeType () {
|
||||
const h = /h+/.test(this.$parent.format) ? '12' : '24'
|
||||
const a = /A/.test(this.$parent.format) ? 'A' : 'a'
|
||||
return [h, a]
|
||||
},
|
||||
timeHeader () {
|
||||
if (this.type === 'time') {
|
||||
return this.$parent.format
|
||||
}
|
||||
return this.value && formatDate(this.value, this.dateFormat)
|
||||
},
|
||||
yearHeader () {
|
||||
return this.firstYear + ' ~ ' + (this.firstYear + 9)
|
||||
},
|
||||
months () {
|
||||
return this.t('months')
|
||||
},
|
||||
notBeforeTime () {
|
||||
return this.getCriticalTime(this.notBefore)
|
||||
},
|
||||
notAfterTime () {
|
||||
return this.getCriticalTime(this.notAfter)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler: 'updateNow'
|
||||
},
|
||||
defaultValue: {
|
||||
handler: 'updateNow'
|
||||
},
|
||||
visible: {
|
||||
immediate: true,
|
||||
handler: 'init'
|
||||
},
|
||||
panel: {
|
||||
handler: 'handelPanelChange'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handelPanelChange (panel, oldPanel) {
|
||||
this.dispatch('DatePicker', 'panel-change', [panel, oldPanel])
|
||||
if (panel === 'YEAR') {
|
||||
this.firstYear = Math.floor(this.calendarYear / 10) * 10
|
||||
} else if (panel === 'TIME') {
|
||||
this.$nextTick(() => {
|
||||
const list = this.$el.querySelectorAll('.mx-panel-time .mx-time-list')
|
||||
for (let i = 0, len = list.length; i < len; i++) {
|
||||
const el = list[i]
|
||||
scrollIntoView(el, el.querySelector('.actived'))
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
init (val) {
|
||||
if (val) {
|
||||
const type = this.type
|
||||
if (type === 'month') {
|
||||
this.showPanelMonth()
|
||||
} else if (type === 'year') {
|
||||
this.showPanelYear()
|
||||
} else if (type === 'time') {
|
||||
this.showPanelTime()
|
||||
} else {
|
||||
this.showPanelDate()
|
||||
}
|
||||
} else {
|
||||
this.showPanelNone()
|
||||
this.updateNow(this.value)
|
||||
}
|
||||
},
|
||||
getNow (value) {
|
||||
return value
|
||||
? new Date(value)
|
||||
: this.defaultValue && isValidDate(this.defaultValue)
|
||||
? new Date(this.defaultValue)
|
||||
: new Date()
|
||||
},
|
||||
// 根据value更新日历
|
||||
updateNow (value) {
|
||||
const oldNow = this.now
|
||||
this.now = this.getNow(value)
|
||||
if (this.visible && this.now !== oldNow) {
|
||||
this.dispatch('DatePicker', 'calendar-change', [
|
||||
new Date(this.now),
|
||||
new Date(oldNow)
|
||||
])
|
||||
}
|
||||
},
|
||||
getCriticalTime (value) {
|
||||
if (!value) {
|
||||
return null
|
||||
}
|
||||
const date = new Date(value)
|
||||
if (this.type === 'year') {
|
||||
return new Date(date.getFullYear(), 0).getTime()
|
||||
} else if (this.type === 'month') {
|
||||
return new Date(date.getFullYear(), date.getMonth()).getTime()
|
||||
} else if (this.type === 'date') {
|
||||
return date.setHours(0, 0, 0, 0)
|
||||
}
|
||||
return date.getTime()
|
||||
},
|
||||
inBefore (time, startAt) {
|
||||
if (startAt === undefined) {
|
||||
startAt = this.startAt
|
||||
}
|
||||
return (
|
||||
(this.notBeforeTime && time < this.notBeforeTime) ||
|
||||
(startAt && time < this.getCriticalTime(startAt))
|
||||
)
|
||||
},
|
||||
inAfter (time, endAt) {
|
||||
if (endAt === undefined) {
|
||||
endAt = this.endAt
|
||||
}
|
||||
return (
|
||||
(this.notAfterTime && time > this.notAfterTime) ||
|
||||
(endAt && time > this.getCriticalTime(endAt))
|
||||
)
|
||||
},
|
||||
inDisabledDays (time) {
|
||||
if (Array.isArray(this.disabledDays)) {
|
||||
return this.disabledDays.some(v => this.getCriticalTime(v) === time)
|
||||
} else if (typeof this.disabledDays === 'function') {
|
||||
return this.disabledDays(new Date(time))
|
||||
}
|
||||
return false
|
||||
},
|
||||
isDisabledYear (year) {
|
||||
const time = new Date(year, 0).getTime()
|
||||
const maxTime = new Date(year + 1, 0).getTime() - 1
|
||||
return (
|
||||
this.inBefore(maxTime) ||
|
||||
this.inAfter(time) ||
|
||||
(this.type === 'year' && this.inDisabledDays(time))
|
||||
)
|
||||
},
|
||||
isDisabledMonth (month) {
|
||||
const time = new Date(this.calendarYear, month).getTime()
|
||||
const maxTime = new Date(this.calendarYear, month + 1).getTime() - 1
|
||||
return (
|
||||
this.inBefore(maxTime) ||
|
||||
this.inAfter(time) ||
|
||||
(this.type === 'month' && this.inDisabledDays(time))
|
||||
)
|
||||
},
|
||||
isDisabledDate (date) {
|
||||
const time = new Date(date).getTime()
|
||||
const maxTime = new Date(date).setHours(23, 59, 59, 999)
|
||||
return (
|
||||
this.inBefore(maxTime) ||
|
||||
this.inAfter(time) ||
|
||||
this.inDisabledDays(time)
|
||||
)
|
||||
},
|
||||
isDisabledTime (date, startAt, endAt) {
|
||||
const time = new Date(date).getTime()
|
||||
return (
|
||||
this.inBefore(time, startAt) ||
|
||||
this.inAfter(time, endAt) ||
|
||||
this.inDisabledDays(time)
|
||||
)
|
||||
},
|
||||
selectDate (date) {
|
||||
if (this.type === 'datetime') {
|
||||
let time = new Date(date)
|
||||
if (isDateObejct(this.value)) {
|
||||
time.setHours(
|
||||
this.value.getHours(),
|
||||
this.value.getMinutes(),
|
||||
this.value.getSeconds()
|
||||
)
|
||||
}
|
||||
if (this.isDisabledTime(time)) {
|
||||
time.setHours(0, 0, 0, 0)
|
||||
if (
|
||||
this.notBefore &&
|
||||
time.getTime() < new Date(this.notBefore).getTime()
|
||||
) {
|
||||
time = new Date(this.notBefore)
|
||||
}
|
||||
if (
|
||||
this.startAt &&
|
||||
time.getTime() < new Date(this.startAt).getTime()
|
||||
) {
|
||||
time = new Date(this.startAt)
|
||||
}
|
||||
}
|
||||
this.selectTime(time)
|
||||
this.showPanelTime()
|
||||
return
|
||||
}
|
||||
this.$emit('select-date', date)
|
||||
},
|
||||
selectYear (year) {
|
||||
this.changeCalendarYear(year)
|
||||
if (this.type.toLowerCase() === 'year') {
|
||||
return this.selectDate(new Date(this.now))
|
||||
}
|
||||
this.dispatch('DatePicker', 'select-year', [year, this.index])
|
||||
this.showPanelMonth()
|
||||
},
|
||||
selectMonth (month) {
|
||||
this.changeCalendarMonth(month)
|
||||
if (this.type.toLowerCase() === 'month') {
|
||||
return this.selectDate(new Date(this.now))
|
||||
}
|
||||
this.dispatch('DatePicker', 'select-month', [month, this.index])
|
||||
this.showPanelDate()
|
||||
},
|
||||
selectTime (time) {
|
||||
this.$emit('select-time', time, false)
|
||||
},
|
||||
pickTime (time) {
|
||||
this.$emit('select-time', time, true)
|
||||
},
|
||||
changeCalendarYear (year) {
|
||||
this.updateNow(new Date(year, this.calendarMonth))
|
||||
},
|
||||
changeCalendarMonth (month) {
|
||||
this.updateNow(new Date(this.calendarYear, month))
|
||||
},
|
||||
getSibling () {
|
||||
const calendars = this.$parent.$children.filter(
|
||||
v => v.$options.name === this.$options.name
|
||||
)
|
||||
const index = calendars.indexOf(this)
|
||||
const sibling = calendars[index ^ 1]
|
||||
return sibling
|
||||
},
|
||||
handleIconMonth (flag) {
|
||||
const month = this.calendarMonth
|
||||
this.changeCalendarMonth(month + flag)
|
||||
this.$parent.$emit('change-calendar-month', {
|
||||
month,
|
||||
flag,
|
||||
vm: this,
|
||||
sibling: this.getSibling()
|
||||
})
|
||||
},
|
||||
handleIconYear (flag) {
|
||||
if (this.panel === 'YEAR') {
|
||||
this.changePanelYears(flag)
|
||||
} else {
|
||||
const year = this.calendarYear
|
||||
this.changeCalendarYear(year + flag)
|
||||
this.$parent.$emit('change-calendar-year', {
|
||||
year,
|
||||
flag,
|
||||
vm: this,
|
||||
sibling: this.getSibling()
|
||||
})
|
||||
}
|
||||
},
|
||||
handleBtnYear () {
|
||||
this.showPanelYear()
|
||||
},
|
||||
handleBtnMonth () {
|
||||
this.showPanelMonth()
|
||||
},
|
||||
handleTimeHeader () {
|
||||
if (this.type === 'time') {
|
||||
return
|
||||
}
|
||||
this.showPanelDate()
|
||||
},
|
||||
changePanelYears (flag) {
|
||||
this.firstYear = this.firstYear + flag * 10
|
||||
},
|
||||
showPanelNone () {
|
||||
this.panel = 'NONE'
|
||||
},
|
||||
showPanelTime () {
|
||||
this.panel = 'TIME'
|
||||
},
|
||||
showPanelDate () {
|
||||
this.panel = 'DATE'
|
||||
},
|
||||
showPanelYear () {
|
||||
this.panel = 'YEAR'
|
||||
},
|
||||
showPanelMonth () {
|
||||
this.panel = 'MONTH'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,337 @@
|
||||
<template>
|
||||
<div
|
||||
:class="[
|
||||
'mx-calendar',
|
||||
`mx-calendar-panel-${panel}`,
|
||||
{ 'mx-calendar-week-mode': type === 'week' },
|
||||
]"
|
||||
>
|
||||
<div class="mx-calendar-header">
|
||||
<button
|
||||
v-show="showIconDoubleArrow"
|
||||
class="mx-btn mx-btn-text mx-btn-icon-double-left"
|
||||
@click="handleIconDoubleLeftClick"
|
||||
>
|
||||
<i class="mx-icon-double-left"></i>
|
||||
</button>
|
||||
<button
|
||||
v-show="showIconArrow"
|
||||
class="mx-btn mx-btn-text mx-btn-icon-left"
|
||||
@click="handleIconLeftClick"
|
||||
>
|
||||
<i class="mx-icon-left"></i>
|
||||
</button>
|
||||
<button
|
||||
v-show="showIconDoubleArrow"
|
||||
class="mx-btn mx-btn-text mx-btn-icon-double-right"
|
||||
@click="handleIconDoubleRightClick"
|
||||
>
|
||||
<i class="mx-icon-double-right"></i>
|
||||
</button>
|
||||
<button
|
||||
v-show="showIconArrow"
|
||||
class="mx-btn mx-btn-text mx-btn-icon-right"
|
||||
@click="handleIconRightClick"
|
||||
>
|
||||
<i class="mx-icon-right"></i>
|
||||
</button>
|
||||
<span class="mx-calendar-header-label">
|
||||
<span v-if="panel === 'year'">{{ yearHeader }}</span>
|
||||
<button
|
||||
v-else-if="panel === 'month'"
|
||||
class="mx-btn mx-btn-text"
|
||||
@click="handelPanelChange('year')"
|
||||
>
|
||||
{{ monthHeader }}
|
||||
</button>
|
||||
<template v-else-if="panel === 'date'">
|
||||
<button
|
||||
v-for="item in dateHeader"
|
||||
:key="item.panel"
|
||||
:class="`mx-btn mx-btn-text mx-btn-current-${item.panel}`"
|
||||
@click="handelPanelChange(item.panel)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</button>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mx-calendar-content">
|
||||
<table-year
|
||||
v-show="panel === 'year'"
|
||||
:decade="calendarDecade"
|
||||
:get-cell-classes="getYearClasses"
|
||||
@select="handleSelectYear"
|
||||
></table-year>
|
||||
<table-month
|
||||
v-if="type !== 'year'"
|
||||
v-show="panel === 'month'"
|
||||
:get-cell-classes="getMonthClasses"
|
||||
@select="handleSelectMonth"
|
||||
></table-month>
|
||||
<table-date
|
||||
v-if="type !== 'year' && type !== 'month'"
|
||||
v-show="panel === 'date'"
|
||||
:calendar-year="calendarYear"
|
||||
:calendar-month="calendarMonth"
|
||||
:title-format="titleFormat"
|
||||
:show-week-number="typeof showWeekNumber === 'boolean' ? showWeekNumber : type === 'week'"
|
||||
:get-cell-classes="getDateClasses"
|
||||
:get-row-classes="getWeekState"
|
||||
@select="handleSelectDate"
|
||||
></table-date>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
subMonths,
|
||||
addMonths,
|
||||
subYears,
|
||||
addYears,
|
||||
setMonth,
|
||||
setYear,
|
||||
startOfYear,
|
||||
startOfMonth,
|
||||
startOfDay,
|
||||
} from 'date-fns';
|
||||
import localeMixin from '../mixin/locale';
|
||||
import formatMixin from '../mixin/format';
|
||||
import { getValidDate, isValidDate } from '../util/date';
|
||||
import TableDate from './table-date';
|
||||
import TableMonth from './table-month';
|
||||
import TableYear from './table-year';
|
||||
|
||||
export default {
|
||||
name: 'CalendarPanel',
|
||||
components: {
|
||||
TableDate,
|
||||
TableMonth,
|
||||
TableYear,
|
||||
},
|
||||
mixins: [localeMixin, formatMixin],
|
||||
props: {
|
||||
value: {},
|
||||
defaultValue: {
|
||||
validator: value => isValidDate(value),
|
||||
default() {
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
},
|
||||
},
|
||||
disabledDate: {
|
||||
type: Function,
|
||||
default: () => false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'date',
|
||||
},
|
||||
getClasses: {
|
||||
type: Function,
|
||||
default: () => [],
|
||||
},
|
||||
showWeekNumber: {
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
},
|
||||
titleFormat: {
|
||||
type: String,
|
||||
default: 'YYYY-MM-DD',
|
||||
},
|
||||
calendar: Date,
|
||||
},
|
||||
data() {
|
||||
const panels = ['date', 'year', 'month'];
|
||||
const panel = panels.indexOf(this.type) !== -1 ? this.type : 'date';
|
||||
return {
|
||||
panel,
|
||||
innerCalendar: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
innerValue() {
|
||||
const value = Array.isArray(this.value) ? this.value : [this.value];
|
||||
const map = {
|
||||
year: startOfYear,
|
||||
month: startOfMonth,
|
||||
date: startOfDay,
|
||||
};
|
||||
const start = map[this.type] || map.date;
|
||||
return value.filter(isValidDate).map(v => start(v));
|
||||
},
|
||||
calendarYear() {
|
||||
return this.innerCalendar.getFullYear();
|
||||
},
|
||||
calendarMonth() {
|
||||
return this.innerCalendar.getMonth();
|
||||
},
|
||||
calendarDecade() {
|
||||
return Math.floor(this.calendarYear / 10) * 10;
|
||||
},
|
||||
showIconDoubleArrow() {
|
||||
return this.panel === 'date' || this.panel === 'month' || this.panel === 'year';
|
||||
},
|
||||
showIconArrow() {
|
||||
return this.panel === 'date';
|
||||
},
|
||||
yearHeader() {
|
||||
return `${this.calendarDecade} ~ ${this.calendarDecade + 9}`;
|
||||
},
|
||||
monthHeader() {
|
||||
return this.calendarYear;
|
||||
},
|
||||
dateHeader() {
|
||||
const monthBeforeYear = this.t('monthBeforeYear');
|
||||
const yearFormat = this.t('yearFormat');
|
||||
const monthFormat = this.t('monthFormat') || 'MMM';
|
||||
const yearLabel = {
|
||||
panel: 'year',
|
||||
label: this.formatDate(this.innerCalendar, yearFormat),
|
||||
};
|
||||
const monthLabel = {
|
||||
panel: 'month',
|
||||
label: this.formatDate(this.innerCalendar, monthFormat),
|
||||
};
|
||||
return monthBeforeYear ? [monthLabel, yearLabel] : [yearLabel, monthLabel];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler: 'initCalendar',
|
||||
},
|
||||
calendar: {
|
||||
handler: 'initCalendar',
|
||||
},
|
||||
defaultValue: {
|
||||
handler: 'initCalendar',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initCalendar() {
|
||||
let calendarDate = this.calendar;
|
||||
if (!isValidDate(calendarDate)) {
|
||||
calendarDate = getValidDate(this.innerValue[0], this.defaultValue);
|
||||
}
|
||||
this.innerCalendar = calendarDate;
|
||||
},
|
||||
emitDate(date, type) {
|
||||
if (!this.disabledDate(new Date(date))) {
|
||||
this.$emit('select', date, type);
|
||||
}
|
||||
},
|
||||
updateCalendar(date) {
|
||||
this.innerCalendar = date;
|
||||
this.$emit('update:calendar', date);
|
||||
},
|
||||
handelPanelChange(panel) {
|
||||
this.panel = panel;
|
||||
},
|
||||
handleIconLeftClick() {
|
||||
const nextCalendar = subMonths(this.innerCalendar, 1);
|
||||
this.updateCalendar(nextCalendar);
|
||||
},
|
||||
handleIconRightClick() {
|
||||
const nextCalendar = addMonths(this.innerCalendar, 1);
|
||||
this.updateCalendar(nextCalendar);
|
||||
},
|
||||
handleIconDoubleLeftClick() {
|
||||
const nextCalendar = subYears(this.innerCalendar, this.panel === 'year' ? 10 : 1);
|
||||
this.updateCalendar(nextCalendar);
|
||||
},
|
||||
handleIconDoubleRightClick() {
|
||||
const nextCalendar = addYears(this.innerCalendar, this.panel === 'year' ? 10 : 1);
|
||||
this.updateCalendar(nextCalendar);
|
||||
},
|
||||
handleSelectYear(year) {
|
||||
if (this.type === 'year') {
|
||||
const date = this.getCellDate(year, 'year');
|
||||
this.emitDate(date, 'year');
|
||||
} else {
|
||||
const nextCalendar = setYear(this.innerCalendar, year);
|
||||
this.updateCalendar(nextCalendar);
|
||||
this.handelPanelChange('month');
|
||||
}
|
||||
},
|
||||
handleSelectMonth(month) {
|
||||
if (this.type === 'month') {
|
||||
const date = this.getCellDate(month, 'month');
|
||||
this.emitDate(date, 'month');
|
||||
} else {
|
||||
const nextCalendar = setMonth(this.innerCalendar, month);
|
||||
this.updateCalendar(nextCalendar);
|
||||
this.handelPanelChange('date');
|
||||
}
|
||||
},
|
||||
handleSelectDate(day) {
|
||||
const date = this.getCellDate(day, 'date');
|
||||
this.emitDate(date, this.type === 'week' ? 'week' : 'date');
|
||||
},
|
||||
getCellDate(value, type) {
|
||||
if (type === 'year') {
|
||||
return new Date(value, 0);
|
||||
}
|
||||
if (type === 'month') {
|
||||
return new Date(this.calendarYear, value);
|
||||
}
|
||||
return new Date(this.calendarYear, this.calendarMonth, value);
|
||||
},
|
||||
getDateClasses(day) {
|
||||
const cellDate = this.getCellDate(day, 'date');
|
||||
const notCurrentMonth = cellDate.getMonth() !== this.calendarMonth;
|
||||
const classes = [];
|
||||
if (cellDate.getTime() === new Date().setHours(0, 0, 0, 0)) {
|
||||
classes.push('today');
|
||||
}
|
||||
if (notCurrentMonth) {
|
||||
classes.push('not-current-month');
|
||||
}
|
||||
const state = this.getStateClass(cellDate);
|
||||
if (!(state === 'active' && notCurrentMonth)) {
|
||||
classes.push(state);
|
||||
}
|
||||
return classes.concat(this.getClasses(cellDate, this.innerValue, classes.join(' ')));
|
||||
},
|
||||
getMonthClasses(month) {
|
||||
if (this.type !== 'month') {
|
||||
return this.calendarMonth === month ? 'active' : '';
|
||||
}
|
||||
const classes = [];
|
||||
const cellDate = this.getCellDate(month, 'month');
|
||||
classes.push(this.getStateClass(cellDate));
|
||||
return classes.concat(this.getClasses(cellDate, this.innerValue, classes.join(' ')));
|
||||
},
|
||||
getYearClasses(year) {
|
||||
if (this.type !== 'year') {
|
||||
return this.calendarYear === year ? 'active' : '';
|
||||
}
|
||||
const classes = [];
|
||||
const cellDate = this.getCellDate(year, 'year');
|
||||
classes.push(this.getStateClass(cellDate));
|
||||
return classes.concat(this.getClasses(cellDate, this.innerValue, classes.join(' ')));
|
||||
},
|
||||
getStateClass(cellDate) {
|
||||
if (this.disabledDate(new Date(cellDate))) {
|
||||
return 'disabled';
|
||||
}
|
||||
if (this.innerValue.some(v => v.getTime() === cellDate.getTime())) {
|
||||
return 'active';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
getWeekState(row) {
|
||||
if (this.type !== 'week') return '';
|
||||
const start = this.getCellDate(row[0].day, 'date').getTime();
|
||||
const end = this.getCellDate(row[6].day, 'date').getTime();
|
||||
const active = this.innerValue.some(v => {
|
||||
const time = v.getTime();
|
||||
return time >= start && time <= end;
|
||||
});
|
||||
return active ? 'mx-active-week' : '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,119 @@
|
||||
import { addMonths, subMonths, differenceInCalendarMonths } from 'date-fns';
|
||||
import CalendarPanel from './calendar-panel';
|
||||
import { getValidDate, isValidDate, isValidRangeDate } from '../util/date';
|
||||
|
||||
export default {
|
||||
components: { CalendarPanel },
|
||||
props: {
|
||||
...CalendarPanel.props,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
innerValue: [],
|
||||
calendars: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// Minimum difference between start and end calendars
|
||||
calendarMinDiff() {
|
||||
const map = {
|
||||
date: 1, // type:date min 1 month
|
||||
month: 1 * 12, // type:month min 1 year
|
||||
year: 10 * 12, // type:year min 10 year
|
||||
};
|
||||
return map[this.type] || map.date;
|
||||
},
|
||||
calendarMaxDiff() {
|
||||
return Infinity;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
this.innerValue = isValidRangeDate(this.value)
|
||||
? this.value
|
||||
: [new Date(NaN), new Date(NaN)];
|
||||
this.calendars = this.innerValue.map(v => getValidDate(v, this.defaultValue));
|
||||
this.validateCalendars(1);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleSelect(date, type) {
|
||||
const [startValue, endValue] = this.innerValue;
|
||||
if (isValidDate(startValue) && !isValidDate(endValue)) {
|
||||
if (startValue.getTime() > date.getTime()) {
|
||||
this.innerValue = [date, startValue];
|
||||
} else {
|
||||
this.innerValue = [startValue, date];
|
||||
}
|
||||
this.emitDate(this.innerValue, type);
|
||||
} else {
|
||||
this.innerValue = [date, new Date(NaN)];
|
||||
}
|
||||
},
|
||||
emitDate(dates, type) {
|
||||
this.$emit('select', dates, type);
|
||||
},
|
||||
updateStartCalendar(value) {
|
||||
this.calendars.splice(0, 1, value);
|
||||
this.validateCalendars(1);
|
||||
},
|
||||
updateEndCalendar(value) {
|
||||
this.calendars.splice(1, 1, value);
|
||||
this.validateCalendars(0);
|
||||
},
|
||||
validateCalendars(index) {
|
||||
const gap = this.getCalendarGap();
|
||||
if (gap) {
|
||||
let calendar = this.calendars[index];
|
||||
if (index === 0) {
|
||||
calendar = subMonths(calendar, gap);
|
||||
} else {
|
||||
calendar = addMonths(calendar, gap);
|
||||
}
|
||||
this.calendars.splice(index, 1, calendar);
|
||||
}
|
||||
},
|
||||
getCalendarGap() {
|
||||
const diff = differenceInCalendarMonths(this.calendars[1], this.calendars[0]);
|
||||
const min = this.calendarMinDiff;
|
||||
const max = this.calendarMaxDiff;
|
||||
if (diff < min) {
|
||||
return min - diff;
|
||||
}
|
||||
if (diff > max) {
|
||||
return max - diff;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
getRangeClasses(cellDate, currentDates, classes) {
|
||||
if (
|
||||
!/disabled|active|not-current-month/.test(classes) &&
|
||||
currentDates.length === 2 &&
|
||||
cellDate.getTime() > currentDates[0].getTime() &&
|
||||
cellDate.getTime() < currentDates[1].getTime()
|
||||
) {
|
||||
return 'in-range';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const calendarRange = this.calendars.map((calendar, index) => {
|
||||
const props = {
|
||||
...this.$props,
|
||||
calendar,
|
||||
value: this.innerValue,
|
||||
getClasses: this.getRangeClasses,
|
||||
};
|
||||
const on = {
|
||||
select: this.handleSelect,
|
||||
'update:calendar': index === 0 ? this.updateStartCalendar : this.updateEndCalendar,
|
||||
};
|
||||
return <calendar-panel {...{ props, on }}></calendar-panel>;
|
||||
});
|
||||
return <div class="mx-range-wrapper">{calendarRange}</div>;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<table class="mx-table mx-table-date">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-if="showWeekNumber" class="mx-week-number-header"></th>
|
||||
<th v-for="day in days" :key="day">{{ day }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody @click="handleCellClick">
|
||||
<tr v-for="(row, i) in dates" :key="i" class="mx-date-row" :class="getRowClasses(row)">
|
||||
<td v-if="showWeekNumber" class="mx-week-number">
|
||||
{{ getWeekNumber(row[0].day) }}
|
||||
</td>
|
||||
<td
|
||||
v-for="(cell, j) in row"
|
||||
:key="j"
|
||||
:data-day="cell.day"
|
||||
class="cell"
|
||||
:class="getCellClasses(cell.day)"
|
||||
:title="getCellTitle(cell.day)"
|
||||
>
|
||||
<div>{{ cell.text }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getWeek } from 'date-format-parse';
|
||||
import localeMixin from '../mixin/locale';
|
||||
import formatMixin from '../mixin/format';
|
||||
import { chunk } from '../util/base';
|
||||
|
||||
export default {
|
||||
name: 'TableDate',
|
||||
mixins: [localeMixin, formatMixin],
|
||||
props: {
|
||||
calendarYear: {
|
||||
type: Number,
|
||||
default() {
|
||||
return new Date().getFullYear();
|
||||
},
|
||||
},
|
||||
calendarMonth: {
|
||||
type: Number,
|
||||
default() {
|
||||
return new Date().getMonth();
|
||||
},
|
||||
},
|
||||
showWeekNumber: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
titleFormat: {
|
||||
type: String,
|
||||
default: 'YYYY-MM-DD',
|
||||
},
|
||||
getRowClasses: {
|
||||
type: Function,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
getCellClasses: {
|
||||
type: Function,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
firstDayOfWeek() {
|
||||
return this.t('formatLocale.firstDayOfWeek') || 0;
|
||||
},
|
||||
days() {
|
||||
const days = this.t('days') || this.t('formatLocale.weekdaysMin');
|
||||
return days.concat(days).slice(this.firstDayOfWeek, this.firstDayOfWeek + 7);
|
||||
},
|
||||
dates() {
|
||||
const arr = [];
|
||||
const { firstDayOfWeek } = this;
|
||||
const year = this.calendarYear;
|
||||
const month = this.calendarMonth;
|
||||
|
||||
// change to the last day of the last month
|
||||
const calendar = new Date(year, month, 0);
|
||||
const lastDayInLastMonth = calendar.getDate();
|
||||
// getDay() 0 is Sunday, 1 is Monday
|
||||
const firstDayInLastMonth =
|
||||
lastDayInLastMonth - ((calendar.getDay() + 7 - firstDayOfWeek) % 7);
|
||||
for (let i = firstDayInLastMonth; i <= lastDayInLastMonth; i++) {
|
||||
const day = i - lastDayInLastMonth;
|
||||
arr.push({ day, text: i });
|
||||
}
|
||||
// change to the last day of the current month
|
||||
calendar.setMonth(month + 1, 0);
|
||||
const lastDayInCurrentMonth = calendar.getDate();
|
||||
for (let i = 1; i <= lastDayInCurrentMonth; i++) {
|
||||
arr.push({ day: i, text: i });
|
||||
}
|
||||
|
||||
const lastMonthLength = lastDayInLastMonth - firstDayInLastMonth + 1;
|
||||
const nextMonthLength = 6 * 7 - lastMonthLength - lastDayInCurrentMonth;
|
||||
for (let i = 1; i <= nextMonthLength; i++) {
|
||||
arr.push({ day: lastDayInCurrentMonth + i, text: i });
|
||||
}
|
||||
|
||||
return chunk(arr, 7);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleCellClick(evt) {
|
||||
let { target } = evt;
|
||||
if (target.tagName === 'DIV') {
|
||||
target = target.parentNode;
|
||||
}
|
||||
const day = target.getAttribute('data-day');
|
||||
if (day) {
|
||||
this.$emit('select', parseInt(day, 10));
|
||||
}
|
||||
},
|
||||
getCellTitle(day) {
|
||||
const year = this.calendarYear;
|
||||
const month = this.calendarMonth;
|
||||
const format = this.titleFormat;
|
||||
const date = new Date(year, month, day);
|
||||
return this.formatDate(date, format);
|
||||
},
|
||||
getWeekNumber(day) {
|
||||
const year = this.calendarYear;
|
||||
const month = this.calendarMonth;
|
||||
const date = new Date(year, month, day);
|
||||
return getWeek(date, this.t('formatLocale'));
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<table class="mx-table mx-table-month" @click="handleClick">
|
||||
<tr v-for="(row, i) in months" :key="i">
|
||||
<td
|
||||
v-for="(cell, j) in row"
|
||||
:key="j"
|
||||
:data-month="cell.month"
|
||||
class="cell"
|
||||
:class="getCellClasses(cell.month)"
|
||||
>
|
||||
<div>{{ cell.text }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import localeMixin from '../mixin/locale';
|
||||
import { chunk } from '../util/base';
|
||||
|
||||
export default {
|
||||
name: 'TableMonth',
|
||||
mixins: [localeMixin],
|
||||
props: {
|
||||
getCellClasses: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
months() {
|
||||
const monthsLocale = this.t('months') || this.t('formatLocale.monthsShort');
|
||||
const months = monthsLocale.map((text, month) => {
|
||||
return { text, month };
|
||||
});
|
||||
return chunk(months, 3);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleClick(evt) {
|
||||
let { target } = evt;
|
||||
if (target.tagName === 'DIV') {
|
||||
target = target.parentNode;
|
||||
}
|
||||
const month = target.getAttribute('data-month');
|
||||
if (month) {
|
||||
this.$emit('select', parseInt(month, 10));
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<table class="mx-table mx-table-year" @click="handleClick">
|
||||
<tr v-for="(row, i) in years" :key="i">
|
||||
<td
|
||||
v-for="(cell, j) in row"
|
||||
:key="j"
|
||||
:data-year="cell"
|
||||
class="cell"
|
||||
:class="getCellClasses(cell)"
|
||||
>
|
||||
<div>{{ cell }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { chunk } from '../util/base';
|
||||
|
||||
export default {
|
||||
name: 'TableYear',
|
||||
props: {
|
||||
decade: Number,
|
||||
getCellClasses: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
years() {
|
||||
const firstYear = this.decade;
|
||||
const years = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
years.push(firstYear + i);
|
||||
}
|
||||
return chunk(years, 2);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleClick(evt) {
|
||||
let { target } = evt;
|
||||
if (target.tagName === 'DIV') {
|
||||
target = target.parentNode;
|
||||
}
|
||||
const year = target.getAttribute('data-year');
|
||||
if (year) {
|
||||
this.$emit('select', parseInt(year, 10));
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,444 @@
|
||||
<template>
|
||||
<div
|
||||
:class="{
|
||||
'mx-datepicker': true,
|
||||
'mx-datepicker-range': range,
|
||||
'mx-datepicker-inline': inline,
|
||||
disabled: disabled,
|
||||
}"
|
||||
>
|
||||
<div v-if="!inline" class="mx-input-wrapper" @click="openPopup">
|
||||
<input
|
||||
ref="input"
|
||||
v-bind="{ name: 'date', type: 'text', autocomplete: 'off', ...inputAttr }"
|
||||
:class="inputClass"
|
||||
:disabled="disabled"
|
||||
:readonly="!editable"
|
||||
:value="text"
|
||||
:placeholder="placeholder"
|
||||
@keydown="handleInputKeydown"
|
||||
@focus="handleInputFocus"
|
||||
@blur="handleInputBlur"
|
||||
@input="handleInputInput"
|
||||
@change="handleInputChange"
|
||||
/>
|
||||
<i v-if="showClearIcon" class="mx-icon-clear" @click.stop="handleClear">
|
||||
<slot name="icon-clear">
|
||||
<icon-close></icon-close>
|
||||
</slot>
|
||||
</i>
|
||||
<i class="mx-icon-calendar">
|
||||
<slot name="icon-calendar">
|
||||
<icon-calendar></icon-calendar>
|
||||
</slot>
|
||||
</i>
|
||||
</div>
|
||||
<Popup
|
||||
ref="popup"
|
||||
:class="popupClass"
|
||||
:style="popupStyle"
|
||||
:inline="inline"
|
||||
:visible="popupVisible"
|
||||
:append-to-body="appendToBody"
|
||||
@clickoutside="handleClickOutSide"
|
||||
>
|
||||
<div v-if="hasSlot('header')" class="mx-datepicker-header">
|
||||
<slot name="header" :value="currentValue" :emit="emitValue"></slot>
|
||||
</div>
|
||||
<div class="mx-datepicker-content-wrapper">
|
||||
<div v-if="hasSlot('sidebar') || shortcuts.length" class="mx-datepicker-sidebar">
|
||||
<slot name="sidebar" :value="currentValue" :emit="emitValue"></slot>
|
||||
<button
|
||||
v-for="(v, i) in shortcuts"
|
||||
:key="i"
|
||||
type="button"
|
||||
class="mx-btn mx-btn-text mx-btn-shortcut"
|
||||
@click="handleSelectShortcut(v)"
|
||||
>
|
||||
{{ v.text }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="mx-datepicker-content">
|
||||
<slot name="content" :value="currentValue" :emit="emitValue">
|
||||
<component
|
||||
:is="currentComponent"
|
||||
ref="picker"
|
||||
v-bind="currentComponentProps"
|
||||
@select="handleSelectDate"
|
||||
></component>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="hasSlot('footer') || confirm" class="mx-datepicker-footer">
|
||||
<slot name="footer" :value="currentValue" :emit="emitValue"></slot>
|
||||
<button
|
||||
v-if="confirm"
|
||||
type="button"
|
||||
class="mx-btn mx-datepicker-btn-confirm"
|
||||
@click="handleConfirmDate"
|
||||
>
|
||||
{{ confirmText }}
|
||||
</button>
|
||||
</div>
|
||||
</Popup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { parse, format } from 'date-format-parse';
|
||||
import { isValidDate, isValidRangeDate, getValidDate } from './util/date';
|
||||
import { pick, isObject, mergeDeep } from './util/base';
|
||||
import { locale as getLocale } from './locale';
|
||||
import Popup from './popup';
|
||||
import IconCalendar from './icon/icon-calendar';
|
||||
import IconClose from './icon/icon-close';
|
||||
import CalendarPanel from './calendar/calendar-panel';
|
||||
import CalendarRange from './calendar/calendar-range';
|
||||
import TimePanel from './time/time-panel';
|
||||
import TimeRange from './time/time-range';
|
||||
import DatetimePanel from './datetime/datetime-panel';
|
||||
import DatetimeRange from './datetime/datetime-range';
|
||||
|
||||
const componentMap = {
|
||||
default: CalendarPanel,
|
||||
time: TimePanel,
|
||||
datetime: DatetimePanel,
|
||||
};
|
||||
const componentRangeMap = {
|
||||
default: CalendarRange,
|
||||
time: TimeRange,
|
||||
datetime: DatetimeRange,
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'DatePicker',
|
||||
components: {
|
||||
IconCalendar,
|
||||
IconClose,
|
||||
Popup,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
locale: this.locale,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
...DatetimePanel.props,
|
||||
value: {},
|
||||
valueType: {
|
||||
type: String,
|
||||
default: 'date', // date, format, timestamp, or token like 'YYYY-MM-DD'
|
||||
},
|
||||
type: {
|
||||
type: String, // ['date', 'datetime', 'time', 'year', 'month', 'week']
|
||||
default: 'date',
|
||||
},
|
||||
format: {
|
||||
type: [String, Object],
|
||||
default() {
|
||||
const map = {
|
||||
date: 'YYYY-MM-DD',
|
||||
datetime: 'YYYY-MM-DD HH:mm:ss',
|
||||
year: 'YYYY',
|
||||
month: 'YYYY-MM',
|
||||
time: 'HH:mm:ss',
|
||||
week: 'w',
|
||||
};
|
||||
return map[this.type] || map.date;
|
||||
},
|
||||
},
|
||||
range: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rangeSeparator: {
|
||||
type: String,
|
||||
default: ' ~ ',
|
||||
},
|
||||
lang: {
|
||||
type: [String, Object],
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
inputClass: {
|
||||
default: 'mx-input',
|
||||
},
|
||||
inputAttr: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
appendToBody: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
},
|
||||
popupClass: {},
|
||||
popupStyle: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
confirm: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: 'OK',
|
||||
},
|
||||
shortcuts: {
|
||||
type: Array,
|
||||
validator(value) {
|
||||
return (
|
||||
Array.isArray(value) &&
|
||||
value.every(
|
||||
v => isObject(v) && typeof v.text === 'string' && typeof v.onClick === 'function'
|
||||
)
|
||||
);
|
||||
},
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// cache the innervalue, wait to confirm
|
||||
currentValue: null,
|
||||
userInput: null,
|
||||
defaultOpen: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentComponent() {
|
||||
const map = this.range ? componentRangeMap : componentMap;
|
||||
return map[this.type] || map.default;
|
||||
},
|
||||
currentComponentProps() {
|
||||
const props = {
|
||||
...pick(this, Object.keys(this.currentComponent.props)),
|
||||
value: this.currentValue,
|
||||
};
|
||||
return props;
|
||||
},
|
||||
popupVisible() {
|
||||
return !this.disabled && (typeof this.open === 'boolean' ? this.open : this.defaultOpen);
|
||||
},
|
||||
innerValue() {
|
||||
let { value } = this;
|
||||
if (this.range) {
|
||||
value = Array.isArray(value) ? value.slice(0, 2) : [null, null];
|
||||
return value.map(this.value2date);
|
||||
}
|
||||
return this.value2date(this.value);
|
||||
},
|
||||
innerValueValid() {
|
||||
if (!this.range && isValidDate(this.innerValue)) {
|
||||
return true;
|
||||
}
|
||||
if (this.range && isValidRangeDate(this.innerValue)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
text() {
|
||||
if (this.userInput !== null) {
|
||||
return this.userInput;
|
||||
}
|
||||
if (!this.innerValueValid) {
|
||||
return '';
|
||||
}
|
||||
const fmt = this.format;
|
||||
if (Array.isArray(this.innerValue)) {
|
||||
return this.innerValue.map(v => this.formatDate(v, fmt)).join(this.rangeSeparator);
|
||||
}
|
||||
return this.formatDate(this.innerValue, fmt);
|
||||
},
|
||||
showClearIcon() {
|
||||
return !this.disabled && this.clearable && this.innerValueValid;
|
||||
},
|
||||
locale() {
|
||||
if (isObject(this.lang)) {
|
||||
return mergeDeep(getLocale(), this.lang);
|
||||
}
|
||||
return getLocale(this.lang);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.currentValue = val;
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleClickOutSide(evt) {
|
||||
const { target } = evt;
|
||||
if (!this.$el.contains(target)) {
|
||||
this.closePopup();
|
||||
}
|
||||
},
|
||||
parseDate(value, fmt) {
|
||||
if (isObject(this.format) && typeof this.format.parse === 'function') {
|
||||
return this.format.parse(value, fmt);
|
||||
}
|
||||
const backupDate = getValidDate(this.defaultValue);
|
||||
return parse(value, fmt, { locale: this.locale.formatLocale, backupDate });
|
||||
},
|
||||
formatDate(date, fmt) {
|
||||
if (isObject(this.format) && typeof this.format.stringify === 'function') {
|
||||
return this.format.stringify(date, fmt);
|
||||
}
|
||||
return format(date, fmt, { locale: this.locale.formatLocale });
|
||||
},
|
||||
// transform the outer value to inner date
|
||||
value2date(value) {
|
||||
switch (this.valueType) {
|
||||
case 'date':
|
||||
return value instanceof Date ? new Date(value.getTime()) : new Date(NaN);
|
||||
case 'timestamp':
|
||||
return typeof value === 'number' ? new Date(value) : new Date(NaN);
|
||||
case 'format':
|
||||
return typeof value === 'string' ? this.parseDate(value, this.format) : new Date(NaN);
|
||||
default:
|
||||
return typeof value === 'string' ? this.parseDate(value, this.valueType) : new Date(NaN);
|
||||
}
|
||||
},
|
||||
// transform the inner date to outer value
|
||||
date2value(date) {
|
||||
if (!isValidDate(date)) return null;
|
||||
switch (this.valueType) {
|
||||
case 'date':
|
||||
return date;
|
||||
case 'timestamp':
|
||||
return date.getTime();
|
||||
case 'format':
|
||||
return this.formatDate(date, this.format);
|
||||
default:
|
||||
return this.formatDate(date, this.valueType);
|
||||
}
|
||||
},
|
||||
emitValue(date, type) {
|
||||
const value = Array.isArray(date) ? date.map(this.date2value) : this.date2value(date);
|
||||
this.$emit('input', value);
|
||||
this.$emit('change', value, type);
|
||||
this.afterEmitValue(type);
|
||||
return value;
|
||||
},
|
||||
afterEmitValue(type) {
|
||||
if (!type || type === this.type) {
|
||||
this.closePopup();
|
||||
}
|
||||
},
|
||||
handleSelectDate(val, type) {
|
||||
if (this.confirm) {
|
||||
this.currentValue = val;
|
||||
} else {
|
||||
this.emitValue(val, type);
|
||||
}
|
||||
},
|
||||
handleClear() {
|
||||
this.emitValue(null);
|
||||
},
|
||||
handleConfirmDate() {
|
||||
const value = this.emitValue(this.currentValue);
|
||||
this.$emit('confirm', value);
|
||||
},
|
||||
handleSelectShortcut(item) {
|
||||
if (isObject(item) && typeof item.onClick === 'function') {
|
||||
const date = item.onClick(this);
|
||||
if (date instanceof Date) {
|
||||
this.emitValue(date);
|
||||
}
|
||||
}
|
||||
},
|
||||
openPopup() {
|
||||
this.defaultOpen = true;
|
||||
this.$emit('open');
|
||||
this.$emit('update:open', true);
|
||||
},
|
||||
closePopup() {
|
||||
this.defaultOpen = false;
|
||||
this.$emit('close');
|
||||
this.$emit('update:open', false);
|
||||
},
|
||||
blur() {
|
||||
this.$refs.input.blur();
|
||||
},
|
||||
focus() {
|
||||
this.$refs.input.focus();
|
||||
},
|
||||
handleInputChange() {
|
||||
if (!this.editable || this.userInput === null) return;
|
||||
const text = this.userInput;
|
||||
this.userInput = null;
|
||||
if (text === '') {
|
||||
this.handleClear();
|
||||
return;
|
||||
}
|
||||
let date = null;
|
||||
if (this.range) {
|
||||
date = text.split(this.rangeSeparator).map(v => this.parseDate(v, this.format));
|
||||
date = isValidRangeDate(date) ? date : null;
|
||||
} else {
|
||||
date = this.parseDate(text, this.format);
|
||||
date = isValidDate(date) ? date : null;
|
||||
}
|
||||
if (date !== null) {
|
||||
this.emitValue(date);
|
||||
this.blur();
|
||||
} else {
|
||||
this.$emit('input-error', text);
|
||||
}
|
||||
},
|
||||
handleInputInput(evt) {
|
||||
this.userInput = evt.target.value;
|
||||
},
|
||||
handleInputKeydown(evt) {
|
||||
const { keyCode } = evt;
|
||||
// Tab 9 or Enter 13
|
||||
if (keyCode === 9) {
|
||||
this.closePopup();
|
||||
} else if (keyCode === 13) {
|
||||
this.handleInputChange();
|
||||
}
|
||||
},
|
||||
handleInputBlur(evt) {
|
||||
// tab close
|
||||
this.$emit('blur', evt);
|
||||
},
|
||||
handleInputFocus(evt) {
|
||||
this.openPopup();
|
||||
this.$emit('focus', evt);
|
||||
},
|
||||
hasSlot(name) {
|
||||
return !!(this.$slots[name] || this.$scopedSlots[name]);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,86 @@
|
||||
import CalendarPanel from '../calendar/calendar-panel';
|
||||
import TimePanel from '../time/time-panel.vue';
|
||||
import { isValidDate } from '../util/date';
|
||||
import { pick } from '../util/base';
|
||||
|
||||
export default {
|
||||
name: 'DatetimePanel',
|
||||
props: {
|
||||
...CalendarPanel.props,
|
||||
...TimePanel.props,
|
||||
showTimePanel: {
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultTimeVisible: false,
|
||||
currentValue: this.value,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
timeVisible() {
|
||||
return typeof this.showTimePanel === 'boolean' ? this.showTimePanel : this.defaultTimeVisible;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
this.currentValue = val;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
closeTimePanel() {
|
||||
this.defaultTimeVisible = false;
|
||||
},
|
||||
openTimePanel() {
|
||||
this.defaultTimeVisible = true;
|
||||
},
|
||||
emitDate(date, type) {
|
||||
this.$emit('select', date, type);
|
||||
},
|
||||
handleSelect(date, type) {
|
||||
if (type === 'date') {
|
||||
this.openTimePanel();
|
||||
const time = isValidDate(this.value) ? this.value : new Date(this.defaultValue);
|
||||
const datetime = new Date(date);
|
||||
datetime.setHours(time.getHours(), time.getMinutes(), time.getSeconds());
|
||||
if (this.disabledTime(new Date(datetime))) {
|
||||
this.currentValue = date;
|
||||
} else {
|
||||
this.emitDate(datetime, type);
|
||||
}
|
||||
} else {
|
||||
this.emitDate(date, type);
|
||||
}
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const calendarProps = {
|
||||
props: {
|
||||
...pick(this, Object.keys(CalendarPanel.props)),
|
||||
value: this.currentValue,
|
||||
},
|
||||
on: {
|
||||
select: this.handleSelect,
|
||||
},
|
||||
};
|
||||
const timeProps = {
|
||||
props: {
|
||||
...pick(this, Object.keys(TimePanel.props)),
|
||||
showTimeHeader: true,
|
||||
value: this.currentValue,
|
||||
},
|
||||
on: {
|
||||
select: this.handleSelect,
|
||||
'title-click': this.closeTimePanel,
|
||||
},
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<CalendarPanel {...calendarProps} />
|
||||
{this.timeVisible && <TimePanel class="mx-calendar-time" {...timeProps} />}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
import CalendarRange from '../calendar/calendar-range';
|
||||
import TimeRange from '../time/time-range';
|
||||
import { pick } from '../util/base';
|
||||
import { isValidRangeDate } from '../util/date';
|
||||
|
||||
export default {
|
||||
name: 'DatetimeRange',
|
||||
props: {
|
||||
...CalendarRange.props,
|
||||
...TimeRange.props,
|
||||
showTimePanel: {
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultTimeVisible: false,
|
||||
currentValue: this.value,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
timeVisible() {
|
||||
return typeof this.showTimePanel === 'boolean' ? this.showTimePanel : this.defaultTimeVisible;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
this.currentValue = val;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
closeTimePanel() {
|
||||
this.defaultTimeVisible = false;
|
||||
},
|
||||
openTimePanel() {
|
||||
this.defaultTimeVisible = true;
|
||||
},
|
||||
emitDate(dates, type) {
|
||||
this.$emit('select', dates, type);
|
||||
},
|
||||
handleSelect(dates, type) {
|
||||
if (type === 'date') {
|
||||
this.openTimePanel();
|
||||
let datetimes = dates.map((v, i) => {
|
||||
const datetime = new Date(v);
|
||||
const time = isValidRangeDate(this.value) ? this.value[i] : new Date(this.defaultValue);
|
||||
datetime.setHours(time.getHours(), time.getMinutes(), time.getSeconds());
|
||||
return datetime;
|
||||
});
|
||||
if (datetimes[1].getTime() < datetimes[0].getTime()) {
|
||||
datetimes = [datetimes[0], datetimes[0]];
|
||||
}
|
||||
if (datetimes.some(this.disabledTime)) {
|
||||
this.currentValue = dates;
|
||||
} else {
|
||||
this.emitDate(datetimes, type);
|
||||
}
|
||||
} else {
|
||||
this.emitDate(dates, type);
|
||||
}
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const calendarProps = {
|
||||
props: {
|
||||
...pick(this, Object.keys(CalendarRange.props)),
|
||||
value: this.currentValue,
|
||||
},
|
||||
on: {
|
||||
select: this.handleSelect,
|
||||
},
|
||||
};
|
||||
const timeProps = {
|
||||
props: {
|
||||
...pick(this, Object.keys(TimeRange.props)),
|
||||
value: this.currentValue,
|
||||
showTimeHeader: true,
|
||||
},
|
||||
on: {
|
||||
select: this.handleSelect,
|
||||
'title-click': this.closeTimePanel,
|
||||
},
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<CalendarRange {...calendarProps} />
|
||||
{this.timeVisible && <TimeRange class="mx-calendar-time" {...timeProps} />}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
d="M940.218182 107.054545h-209.454546V46.545455h-65.163636v60.50909H363.054545V46.545455H297.890909v60.50909H83.781818c-18.618182 0-32.581818 13.963636-32.581818 32.581819v805.236363c0 18.618182 13.963636 32.581818 32.581818 32.581818h861.090909c18.618182 0 32.581818-13.963636 32.581818-32.581818V139.636364c-4.654545-18.618182-18.618182-32.581818-37.236363-32.581819zM297.890909 172.218182V232.727273h65.163636V172.218182h307.2V232.727273h65.163637V172.218182h176.872727v204.8H116.363636V172.218182h181.527273zM116.363636 912.290909V442.181818h795.927273v470.109091H116.363636z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
d="M810.005333 274.005333l-237.994667 237.994667 237.994667 237.994667-60.010667 60.010667-237.994667-237.994667-237.994667 237.994667-60.010667-60.010667 237.994667-237.994667-237.994667-237.994667 60.010667-60.010667 237.994667 237.994667 237.994667-237.994667z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
+10
-8
@@ -1,13 +1,15 @@
|
||||
import DatePicker from './index.vue'
|
||||
import './index.scss'
|
||||
/* istanbul ignore file */
|
||||
import DatePicker from './date-picker.vue';
|
||||
import { locale } from './locale';
|
||||
|
||||
DatePicker.install = function (Vue) {
|
||||
Vue.component(DatePicker.name, DatePicker)
|
||||
}
|
||||
DatePicker.locale = locale;
|
||||
|
||||
DatePicker.install = function install(Vue) {
|
||||
Vue.component(DatePicker.name, DatePicker);
|
||||
};
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (typeof window !== 'undefined' && window.Vue) {
|
||||
DatePicker.install(window.Vue)
|
||||
DatePicker.install(window.Vue);
|
||||
}
|
||||
|
||||
export default DatePicker
|
||||
export default DatePicker;
|
||||
|
||||
-336
@@ -1,336 +0,0 @@
|
||||
$default-color: #73879c;
|
||||
$primary-color: #1284e7;
|
||||
|
||||
.mx-datepicker {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 210px;
|
||||
color: $default-color;
|
||||
font: 14px/1.5 'Helvetica Neue', Helvetica, Arial, 'Microsoft Yahei',
|
||||
sans-serif;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
&.disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
.mx-datepicker-range {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.mx-datepicker-popup {
|
||||
position: absolute;
|
||||
margin-top: 1px;
|
||||
margin-bottom: 1px;
|
||||
border: 1px solid #d9d9d9;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.mx-input-wrapper {
|
||||
position: relative;
|
||||
.mx-clear-wrapper {
|
||||
display: none;
|
||||
}
|
||||
&:hover {
|
||||
.mx-clear-wrapper {
|
||||
display: block;
|
||||
}
|
||||
.mx-clear-wrapper + .mx-input-append {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx-input {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
padding: 6px 30px;
|
||||
padding-left: 10px;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
&:disabled,
|
||||
&.disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
&::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mx-input-append {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 30px;
|
||||
height: 100%;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.mx-input-icon {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-style: normal;
|
||||
color: #555;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx-calendar-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #555;
|
||||
stroke-width: 8px;
|
||||
stroke: currentColor;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.mx-clear-icon {
|
||||
&::before {
|
||||
display: inline-block;
|
||||
content: '\2716';
|
||||
vertical-align: middle;
|
||||
}
|
||||
&::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.mx-range-wrapper {
|
||||
width: 248px * 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mx-shortcuts-wrapper {
|
||||
text-align: left;
|
||||
padding: 0 12px;
|
||||
line-height: 34px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
.mx-shortcuts {
|
||||
background: none;
|
||||
outline: none;
|
||||
border: 0;
|
||||
color: #48576a;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: mix(#fff, $primary-color, 20%);
|
||||
}
|
||||
&:after {
|
||||
content: '|';
|
||||
margin: 0 10px;
|
||||
color: #48576a;
|
||||
}
|
||||
&:last-child::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx-datepicker-footer {
|
||||
padding: 4px;
|
||||
clear: both;
|
||||
text-align: right;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.mx-datepicker-btn {
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
padding: 7px 15px;
|
||||
margin: 0 5px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.mx-datepicker-btn-confirm {
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
color: #73879c;
|
||||
&:hover {
|
||||
color: #1284e7;
|
||||
border-color: #1284e7;
|
||||
}
|
||||
}
|
||||
|
||||
/* 日历组件 */
|
||||
.mx-calendar {
|
||||
float: left;
|
||||
color: $default-color;
|
||||
padding: 6px 12px;
|
||||
font: 14px/1.5 Helvetica Neue, Helvetica, Arial, Microsoft Yahei, sans-serif;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.mx-calendar-header {
|
||||
padding: 0 4px;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
> a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: mix(#fff, $primary-color, 20%);
|
||||
}
|
||||
}
|
||||
@at-root {
|
||||
.mx-icon-last-month,
|
||||
.mx-icon-next-month {
|
||||
padding: 0 6px;
|
||||
font-size: 20px;
|
||||
line-height: 30px;
|
||||
user-select: none;
|
||||
}
|
||||
.mx-icon-last-month {
|
||||
float: left;
|
||||
}
|
||||
.mx-icon-next-month {
|
||||
float: right;
|
||||
}
|
||||
.mx-icon-last-year {
|
||||
@extend .mx-icon-last-month;
|
||||
}
|
||||
.mx-icon-next-year {
|
||||
@extend .mx-icon-next-month;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx-calendar-content {
|
||||
width: 32px * 7;
|
||||
height: 32px * 7;
|
||||
.cell {
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #eaf8fe;
|
||||
}
|
||||
&.actived {
|
||||
color: #fff;
|
||||
background-color: $primary-color;
|
||||
}
|
||||
&.inrange {
|
||||
background-color: #eaf8fe;
|
||||
}
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: #ccc;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx-panel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mx-panel-date {
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
td,
|
||||
th {
|
||||
font-size: 12px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
td {
|
||||
&.today {
|
||||
color: mix(#fff, $primary-color, 10%);
|
||||
}
|
||||
&.last-month,
|
||||
&.next-month {
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx-panel-year {
|
||||
padding: 7px 0;
|
||||
.cell {
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
margin: 1px 5%;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx-panel-month {
|
||||
.cell {
|
||||
display: inline-block;
|
||||
width: 30%;
|
||||
line-height: 40px;
|
||||
margin: 8px 1.5%;
|
||||
}
|
||||
}
|
||||
|
||||
.mx-time-list {
|
||||
position: relative; // 定位 offsetParent
|
||||
float: left;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
border-left: 1px solid rgba(0, 0, 0, 0.05);
|
||||
overflow-y: auto;
|
||||
.mx-time-picker-item {
|
||||
display: block;
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
}
|
||||
&:first-child {
|
||||
border-left: 0;
|
||||
}
|
||||
.cell {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
/* 滚动条滑块 */
|
||||
&::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
border-radius: 10px;
|
||||
box-shadow: inset 1px 1px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:hover::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
-621
@@ -1,621 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="mx-datepicker"
|
||||
@mousedown="showPopup"
|
||||
@touchstart="showPopup"
|
||||
:class="{
|
||||
'mx-datepicker-range': range,
|
||||
'disabled': disabled
|
||||
}"
|
||||
:style="{
|
||||
'width': computedWidth
|
||||
}"
|
||||
>
|
||||
<div class="mx-input-wrapper">
|
||||
<input
|
||||
:class="inputClass"
|
||||
:name="inputName"
|
||||
v-bind="inputAttr"
|
||||
ref="input"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
:disabled="disabled"
|
||||
:readonly="!editable"
|
||||
:value="text"
|
||||
:placeholder="innerPlaceholder"
|
||||
@keydown="handleKeydown"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
@input="handleInput"
|
||||
@change="handleChange">
|
||||
<span
|
||||
v-if="showClearIcon"
|
||||
class="mx-input-append mx-clear-wrapper"
|
||||
@mousedown.stop="clearDate">
|
||||
<slot name="mx-clear-icon">
|
||||
<i class="mx-input-icon mx-clear-icon"></i>
|
||||
</slot>
|
||||
</span>
|
||||
<span class="mx-input-append">
|
||||
<slot name="calendar-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 200 200" class="mx-calendar-icon">
|
||||
<rect x="13" y="29" rx="14" ry="14" width="174" height="158" fill="transparent" />
|
||||
<line x1="46" x2="46" y1="8" y2="50" />
|
||||
<line x1="154" x2="154" y1="8" y2="50" />
|
||||
<line x1="13" x2="187" y1="70" y2="70" />
|
||||
<text x="50%" y="135" font-size="90" stroke-width="1" text-anchor="middle" dominant-baseline="middle">{{iconDay}}</text>
|
||||
</svg>
|
||||
</slot>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mx-datepicker-popup"
|
||||
:style="innerPopupStyle"
|
||||
v-show="popupVisible"
|
||||
@click.stop.prevent
|
||||
ref="calendar">
|
||||
<slot name="header">
|
||||
<div class="mx-shortcuts-wrapper"
|
||||
v-if="range && innerShortcuts.length">
|
||||
<button
|
||||
type="button"
|
||||
class="mx-shortcuts"
|
||||
v-for="(range, index) in innerShortcuts"
|
||||
:key="index"
|
||||
@click="selectRange(range)">{{range.text}}</button>
|
||||
</div>
|
||||
</slot>
|
||||
<calendar-panel
|
||||
v-if="!range"
|
||||
v-bind="$attrs"
|
||||
ref="calendarPanel"
|
||||
:index="-1"
|
||||
:type="innerType"
|
||||
:date-format="innerDateFormat"
|
||||
:value="currentValue"
|
||||
:visible="popupVisible"
|
||||
@select-date="selectDate"
|
||||
@select-time="selectTime"></calendar-panel>
|
||||
<div class="mx-range-wrapper"
|
||||
v-else>
|
||||
<calendar-panel
|
||||
style="box-shadow:1px 0 rgba(0, 0, 0, .1)"
|
||||
v-bind="$attrs"
|
||||
ref="calendarPanel"
|
||||
:index="0"
|
||||
:type="innerType"
|
||||
:date-format="innerDateFormat"
|
||||
:value="currentValue[0]"
|
||||
:end-at="currentValue[1]"
|
||||
:start-at="null"
|
||||
:visible="popupVisible"
|
||||
@select-date="selectStartDate"
|
||||
@select-time="selectStartTime"></calendar-panel>
|
||||
<calendar-panel
|
||||
v-bind="$attrs"
|
||||
:index="1"
|
||||
:type="innerType"
|
||||
:date-format="innerDateFormat"
|
||||
:value="currentValue[1]"
|
||||
:start-at="currentValue[0]"
|
||||
:end-at="null"
|
||||
:visible="popupVisible"
|
||||
@select-date="selectEndDate"
|
||||
@select-time="selectEndTime"></calendar-panel>
|
||||
</div>
|
||||
<slot name="footer" :confirm="confirmDate">
|
||||
<div class="mx-datepicker-footer"
|
||||
v-if="confirm">
|
||||
<button type="button"
|
||||
class="mx-datepicker-btn mx-datepicker-btn-confirm"
|
||||
@click="confirmDate">{{ confirmText }}</button>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import fecha from 'fecha'
|
||||
import { isValidDate, isValidRangeDate, isDateObejct, isPlainObject, formatDate, parseDate, throttle } from '@/utils/index'
|
||||
import { transformDate } from '@/utils/transform'
|
||||
import CalendarPanel from './calendar.vue'
|
||||
import locale from '@/mixins/locale'
|
||||
import Languages from '@/locale/languages'
|
||||
|
||||
export default {
|
||||
fecha,
|
||||
name: 'DatePicker',
|
||||
components: { CalendarPanel },
|
||||
mixins: [locale],
|
||||
props: {
|
||||
value: null,
|
||||
valueType: {
|
||||
default: 'date',
|
||||
validator: function (value) {
|
||||
return ['timestamp', 'format', 'date'].indexOf(value) !== -1 || isPlainObject(value)
|
||||
}
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
lang: {
|
||||
type: [String, Object],
|
||||
default: 'zh'
|
||||
},
|
||||
format: {
|
||||
type: [String, Object],
|
||||
default: 'YYYY-MM-DD'
|
||||
},
|
||||
dateFormat: {
|
||||
type: String // format the time header and date tooltip
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'date' // ['date', 'datetime'] zendy added 'month', 'year', mxie added "time"
|
||||
},
|
||||
range: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
rangeSeparator: {
|
||||
type: String,
|
||||
default: '~'
|
||||
},
|
||||
width: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: 'OK'
|
||||
},
|
||||
confirm: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
shortcuts: {
|
||||
type: [Boolean, Array],
|
||||
default: true
|
||||
},
|
||||
inputName: {
|
||||
type: String,
|
||||
default: 'date'
|
||||
},
|
||||
inputClass: {
|
||||
type: [String, Array],
|
||||
default: 'mx-input'
|
||||
},
|
||||
inputAttr: Object,
|
||||
appendToBody: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
popupStyle: {
|
||||
type: Object
|
||||
},
|
||||
iconDay: {
|
||||
type: [Number, String]
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
currentValue: this.range ? [null, null] : null,
|
||||
userInput: null,
|
||||
popupVisible: false,
|
||||
position: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler: 'handleValueChange'
|
||||
},
|
||||
popupVisible (val) {
|
||||
if (val) {
|
||||
this.initCalendar()
|
||||
} else {
|
||||
this.userInput = null
|
||||
this.blur()
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
transform () {
|
||||
const type = this.valueType
|
||||
if (isPlainObject(type)) {
|
||||
return { ...transformDate.date, ...type }
|
||||
}
|
||||
if (type === 'format') {
|
||||
return {
|
||||
value2date: this.parse.bind(this),
|
||||
date2value: this.stringify.bind(this)
|
||||
}
|
||||
}
|
||||
return transformDate[type] || transformDate.date
|
||||
},
|
||||
language () {
|
||||
if (isPlainObject(this.lang)) {
|
||||
return { ...Languages.en, ...this.lang }
|
||||
}
|
||||
return Languages[this.lang] || Languages.en
|
||||
},
|
||||
innerPlaceholder () {
|
||||
if (typeof this.placeholder === 'string') {
|
||||
return this.placeholder
|
||||
}
|
||||
return this.range ? this.t('placeholder.dateRange') : this.t('placeholder.date')
|
||||
},
|
||||
text () {
|
||||
if (this.userInput !== null) {
|
||||
return this.userInput
|
||||
}
|
||||
const { value2date } = this.transform
|
||||
if (!this.range) {
|
||||
return this.isValidValue(this.value)
|
||||
? this.stringify(value2date(this.value))
|
||||
: ''
|
||||
}
|
||||
return this.isValidRangeValue(this.value)
|
||||
? `${this.stringify(value2date(this.value[0]))} ${this.rangeSeparator} ${this.stringify(value2date(this.value[1]))}`
|
||||
: ''
|
||||
},
|
||||
computedWidth () {
|
||||
if (typeof this.width === 'number' || (typeof this.width === 'string' && /^\d+$/.test(this.width))) {
|
||||
return this.width + 'px'
|
||||
}
|
||||
return this.width
|
||||
},
|
||||
showClearIcon () {
|
||||
return !this.disabled && this.clearable && (this.range ? this.isValidRangeValue(this.value) : this.isValidValue(this.value))
|
||||
},
|
||||
innerType () {
|
||||
return String(this.type).toLowerCase()
|
||||
},
|
||||
innerShortcuts () {
|
||||
if (Array.isArray(this.shortcuts)) {
|
||||
return this.shortcuts
|
||||
}
|
||||
if (this.shortcuts === false) {
|
||||
return []
|
||||
}
|
||||
const pickers = this.t('pickers')
|
||||
const arr = [
|
||||
{
|
||||
text: pickers[0],
|
||||
onClick (self) {
|
||||
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.updateDate(true)
|
||||
}
|
||||
},
|
||||
{
|
||||
text: pickers[2],
|
||||
onClick (self) {
|
||||
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.updateDate(true)
|
||||
}
|
||||
}
|
||||
]
|
||||
return arr
|
||||
},
|
||||
innerDateFormat () {
|
||||
if (this.dateFormat) {
|
||||
return this.dateFormat
|
||||
}
|
||||
if (typeof this.format !== 'string') {
|
||||
return 'YYYY-MM-DD'
|
||||
}
|
||||
if (this.innerType === 'date') {
|
||||
return this.format
|
||||
}
|
||||
return this.format.replace(/[Hh]+.*[msSaAZ]|\[.*?\]/g, '').trim() || 'YYYY-MM-DD'
|
||||
},
|
||||
innerPopupStyle () {
|
||||
return { ...this.position, ...this.popupStyle }
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.appendToBody) {
|
||||
this.popupElm = this.$refs.calendar
|
||||
document.body.appendChild(this.popupElm)
|
||||
}
|
||||
// clickoutside close popup
|
||||
let mousedownTarget
|
||||
this._bindDocmentMousedown = evt => {
|
||||
mousedownTarget = evt.target
|
||||
}
|
||||
this._bindDocumentMouseup = evt => {
|
||||
const mouseupTarget = evt.target
|
||||
const el = this.$el
|
||||
const { popupElm } = this
|
||||
if (
|
||||
!mousedownTarget ||
|
||||
!mouseupTarget ||
|
||||
el.contains(mousedownTarget) ||
|
||||
el.contains(mouseupTarget) ||
|
||||
(popupElm && (popupElm.contains(mousedownTarget) || popupElm.contains(mouseupTarget)))
|
||||
) {
|
||||
return
|
||||
}
|
||||
mousedownTarget = null
|
||||
this.closePopup()
|
||||
}
|
||||
this._startEvt = 'mousedown'
|
||||
this._endEvt = 'mouseup'
|
||||
if ('ontouchend' in document) {
|
||||
this._startEvt = 'touchstart'
|
||||
this._endEvt = 'touchend'
|
||||
}
|
||||
document.addEventListener(this._startEvt, this._bindDocmentMousedown)
|
||||
document.addEventListener(this._endEvt, this._bindDocumentMouseup)
|
||||
|
||||
this._displayPopup = throttle(() => {
|
||||
if (this.popupVisible) {
|
||||
this.displayPopup()
|
||||
}
|
||||
}, 200)
|
||||
window.addEventListener('resize', this._displayPopup)
|
||||
window.addEventListener('scroll', this._displayPopup)
|
||||
},
|
||||
beforeDestroy () {
|
||||
if (this.popupElm && this.popupElm.parentNode === document.body) {
|
||||
document.body.removeChild(this.popupElm)
|
||||
}
|
||||
document.removeEventListener(this._startEvt, this._bindDocmentMousedown)
|
||||
document.removeEventListener(this._endEvt, this._bindDocumentMouseup)
|
||||
window.removeEventListener('resize', this._displayPopup)
|
||||
window.removeEventListener('scroll', this._displayPopup)
|
||||
},
|
||||
methods: {
|
||||
initCalendar () {
|
||||
this.handleValueChange(this.value)
|
||||
this.displayPopup()
|
||||
},
|
||||
stringify (date) {
|
||||
return (isPlainObject(this.format) && typeof this.format.stringify === 'function')
|
||||
? this.format.stringify(date)
|
||||
: formatDate(date, this.format)
|
||||
},
|
||||
parse (value) {
|
||||
return (isPlainObject(this.format) && typeof this.format.parse === 'function')
|
||||
? this.format.parse(value)
|
||||
: parseDate(value, this.format)
|
||||
},
|
||||
isValidValue (value) {
|
||||
const { value2date } = this.transform
|
||||
return isValidDate(value2date(value))
|
||||
},
|
||||
isValidRangeValue (value) {
|
||||
const { value2date } = this.transform
|
||||
return Array.isArray(value) && value.length === 2 && this.isValidValue(value[0]) &&
|
||||
this.isValidValue(value[1]) && (value2date(value[1]).getTime() >= value2date(value[0]).getTime())
|
||||
},
|
||||
dateEqual (a, b) {
|
||||
return isDateObejct(a) && isDateObejct(b) && a.getTime() === b.getTime()
|
||||
},
|
||||
rangeEqual (a, b) {
|
||||
return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((item, index) => this.dateEqual(item, b[index]))
|
||||
},
|
||||
selectRange (range) {
|
||||
if (typeof range.onClick === 'function') {
|
||||
const close = range.onClick(this)
|
||||
if (close !== false) {
|
||||
this.closePopup()
|
||||
}
|
||||
} else {
|
||||
this.currentValue = [new Date(range.start), new Date(range.end)]
|
||||
this.updateDate(true)
|
||||
this.closePopup()
|
||||
}
|
||||
},
|
||||
clearDate () {
|
||||
const date = this.range ? [null, null] : null
|
||||
this.currentValue = date
|
||||
this.updateDate(true)
|
||||
this.$emit('clear')
|
||||
},
|
||||
confirmDate () {
|
||||
const valid = this.range ? isValidRangeDate(this.currentValue) : isValidDate(this.currentValue)
|
||||
if (valid) {
|
||||
this.updateDate(true)
|
||||
}
|
||||
this.emitDate('confirm')
|
||||
this.closePopup()
|
||||
},
|
||||
updateDate (confirm = false) {
|
||||
if ((this.confirm && !confirm) || this.disabled) {
|
||||
return false
|
||||
}
|
||||
const equal = this.range ? this.rangeEqual(this.value, this.currentValue) : this.dateEqual(this.value, this.currentValue)
|
||||
if (equal) {
|
||||
return false
|
||||
}
|
||||
this.emitDate('input')
|
||||
this.emitDate('change')
|
||||
return true
|
||||
},
|
||||
emitDate (eventName) {
|
||||
const { date2value } = this.transform
|
||||
const value = this.range
|
||||
? this.currentValue.map(date2value)
|
||||
: date2value(this.currentValue)
|
||||
this.$emit(eventName, value)
|
||||
},
|
||||
handleValueChange (value) {
|
||||
const { value2date } = this.transform
|
||||
if (this.range) {
|
||||
this.currentValue = this.isValidRangeValue(value) ? value.map(value2date) : [null, null]
|
||||
} else {
|
||||
this.currentValue = this.isValidValue(value) ? value2date(value) : null
|
||||
}
|
||||
},
|
||||
selectDate (date) {
|
||||
this.currentValue = date
|
||||
this.updateDate() && this.closePopup()
|
||||
},
|
||||
selectStartDate (date) {
|
||||
this.$set(this.currentValue, 0, date)
|
||||
if (this.currentValue[1]) {
|
||||
this.updateDate()
|
||||
}
|
||||
},
|
||||
selectEndDate (date) {
|
||||
this.$set(this.currentValue, 1, date)
|
||||
if (this.currentValue[0]) {
|
||||
this.updateDate()
|
||||
}
|
||||
},
|
||||
selectTime (time, close) {
|
||||
this.currentValue = time
|
||||
this.updateDate() && close && this.closePopup()
|
||||
},
|
||||
selectStartTime (time) {
|
||||
this.selectStartDate(time)
|
||||
},
|
||||
selectEndTime (time) {
|
||||
this.selectEndDate(time)
|
||||
},
|
||||
showPopup () {
|
||||
if (this.disabled) {
|
||||
return
|
||||
}
|
||||
this.popupVisible = true
|
||||
},
|
||||
closePopup () {
|
||||
this.popupVisible = false
|
||||
},
|
||||
getPopupSize (element) {
|
||||
const originalDisplay = element.style.display
|
||||
const originalVisibility = element.style.visibility
|
||||
element.style.display = 'block'
|
||||
element.style.visibility = 'hidden'
|
||||
const styles = window.getComputedStyle(element)
|
||||
const width = element.offsetWidth + parseInt(styles.marginLeft) + parseInt(styles.marginRight)
|
||||
const height = element.offsetHeight + parseInt(styles.marginTop) + parseInt(styles.marginBottom)
|
||||
const result = { width, height }
|
||||
element.style.display = originalDisplay
|
||||
element.style.visibility = originalVisibility
|
||||
return result
|
||||
},
|
||||
displayPopup () {
|
||||
const dw = document.documentElement.clientWidth
|
||||
const dh = document.documentElement.clientHeight
|
||||
const InputRect = this.$el.getBoundingClientRect()
|
||||
const PopupRect = this._popupRect || (this._popupRect = this.getPopupSize(this.$refs.calendar))
|
||||
const position = {}
|
||||
let offsetRelativeToInputX = 0
|
||||
let offsetRelativeToInputY = 0
|
||||
if (this.appendToBody) {
|
||||
offsetRelativeToInputX = window.pageXOffset + InputRect.left
|
||||
offsetRelativeToInputY = window.pageYOffset + InputRect.top
|
||||
}
|
||||
if (
|
||||
dw - InputRect.left < PopupRect.width &&
|
||||
InputRect.right < PopupRect.width
|
||||
) {
|
||||
position.left = offsetRelativeToInputX - InputRect.left + 1 + 'px'
|
||||
} else if (InputRect.left + InputRect.width / 2 <= dw / 2) {
|
||||
position.left = offsetRelativeToInputX + 'px'
|
||||
} else {
|
||||
position.left = offsetRelativeToInputX + InputRect.width - PopupRect.width + 'px'
|
||||
}
|
||||
if (
|
||||
InputRect.top <= PopupRect.height &&
|
||||
dh - InputRect.bottom <= PopupRect.height
|
||||
) {
|
||||
position.top = offsetRelativeToInputY + dh - InputRect.top - PopupRect.height + 'px'
|
||||
} else if (InputRect.top + InputRect.height / 2 <= dh / 2) {
|
||||
position.top = offsetRelativeToInputY + InputRect.height + 'px'
|
||||
} else {
|
||||
position.top = offsetRelativeToInputY - PopupRect.height + 'px'
|
||||
}
|
||||
if (position.top !== this.position.top || position.left !== this.position.left) {
|
||||
this.position = position
|
||||
}
|
||||
},
|
||||
blur () {
|
||||
this.$refs.input.blur()
|
||||
},
|
||||
handleBlur (event) {
|
||||
this.$emit('blur', event)
|
||||
},
|
||||
handleFocus (event) {
|
||||
if (!this.popupVisible) {
|
||||
this.showPopup()
|
||||
}
|
||||
this.$emit('focus', event)
|
||||
},
|
||||
handleKeydown (event) {
|
||||
const keyCode = event.keyCode
|
||||
// Tab 9 or Enter 13
|
||||
if (keyCode === 9 || keyCode === 13) {
|
||||
// ie emit the watch before the change event
|
||||
this.handleChange()
|
||||
this.userInput = null
|
||||
this.closePopup()
|
||||
}
|
||||
},
|
||||
handleInput (event) {
|
||||
this.userInput = event.target.value
|
||||
},
|
||||
handleChange () {
|
||||
if (this.editable && this.userInput !== null) {
|
||||
const value = this.text
|
||||
const checkDate = this.$refs.calendarPanel.isDisabledTime
|
||||
if (!value) {
|
||||
this.clearDate()
|
||||
return
|
||||
}
|
||||
if (this.range) {
|
||||
const range = value.split(` ${this.rangeSeparator} `)
|
||||
if (range.length === 2) {
|
||||
const start = this.parse(range[0])
|
||||
const end = this.parse(range[1])
|
||||
if (start && end && !checkDate(start, null, end) && !checkDate(end, start, null)) {
|
||||
this.currentValue = [start, end]
|
||||
this.updateDate(true)
|
||||
this.closePopup()
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const date = this.parse(value)
|
||||
if (date && !checkDate(date, null, null)) {
|
||||
this.currentValue = date
|
||||
this.updateDate(true)
|
||||
this.closePopup()
|
||||
return
|
||||
}
|
||||
}
|
||||
this.$emit('input-error', value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,14 @@
|
||||
import enUS from './locale/en';
|
||||
|
||||
let defaultLocale = 'en';
|
||||
const locales = {};
|
||||
locales[defaultLocale] = enUS;
|
||||
|
||||
export function locale(name, object) {
|
||||
if (typeof name !== 'string') return locales[defaultLocale];
|
||||
if (object) {
|
||||
locales[name] = object;
|
||||
defaultLocale = name;
|
||||
}
|
||||
return locales[name] || locales[defaultLocale];
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import af from 'date-format-parse/lib/locale/af';
|
||||
|
||||
const lang = {
|
||||
formatLocale: af,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('af', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import arDZ from 'date-format-parse/lib/locale/ar-dz';
|
||||
|
||||
const lang = {
|
||||
formatLocale: arDZ,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('ar-dz', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import arSA from 'date-format-parse/lib/locale/ar-sa';
|
||||
|
||||
const lang = {
|
||||
formatLocale: arSA,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('ar-sa', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import ar from 'date-format-parse/lib/locale/ar';
|
||||
|
||||
const lang = {
|
||||
formatLocale: ar,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('ar', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import az from 'date-format-parse/lib/locale/az';
|
||||
|
||||
const lang = {
|
||||
formatLocale: az,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('az', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import be from 'date-format-parse/lib/locale/be';
|
||||
|
||||
const lang = {
|
||||
formatLocale: be,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('be', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import bg from 'date-format-parse/lib/locale/bg';
|
||||
|
||||
const lang = {
|
||||
formatLocale: bg,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('bg', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import bm from 'date-format-parse/lib/locale/bm';
|
||||
|
||||
const lang = {
|
||||
formatLocale: bm,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('bm', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import bn from 'date-format-parse/lib/locale/bn';
|
||||
|
||||
const lang = {
|
||||
formatLocale: bn,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('bn', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import ca from 'date-format-parse/lib/locale/ca';
|
||||
|
||||
const lang = {
|
||||
formatLocale: ca,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('ca', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import cs from 'date-format-parse/lib/locale/cs';
|
||||
|
||||
const lang = {
|
||||
formatLocale: cs,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('cs', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import cy from 'date-format-parse/lib/locale/cy';
|
||||
|
||||
const lang = {
|
||||
formatLocale: cy,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('cy', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import da from 'date-format-parse/lib/locale/da';
|
||||
|
||||
const lang = {
|
||||
formatLocale: da,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('da', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import de from 'date-format-parse/lib/locale/de';
|
||||
|
||||
const lang = {
|
||||
formatLocale: de,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('de', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import el from 'date-format-parse/lib/locale/el';
|
||||
|
||||
const lang = {
|
||||
formatLocale: el,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('el', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,10 @@
|
||||
import en from 'date-format-parse/lib/locale/en';
|
||||
|
||||
const lang = {
|
||||
formatLocale: en,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import eo from 'date-format-parse/lib/locale/eo';
|
||||
|
||||
const lang = {
|
||||
formatLocale: eo,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('eo', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import es from 'date-format-parse/lib/locale/es';
|
||||
|
||||
const lang = {
|
||||
formatLocale: es,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('es', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import et from 'date-format-parse/lib/locale/et';
|
||||
|
||||
const lang = {
|
||||
formatLocale: et,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('et', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import fi from 'date-format-parse/lib/locale/fi';
|
||||
|
||||
const lang = {
|
||||
formatLocale: fi,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('fi', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import fr from 'date-format-parse/lib/locale/fr';
|
||||
|
||||
const lang = {
|
||||
formatLocale: fr,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('fr', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import gl from 'date-format-parse/lib/locale/gl';
|
||||
|
||||
const lang = {
|
||||
formatLocale: gl,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('gl', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import gu from 'date-format-parse/lib/locale/gu';
|
||||
|
||||
const lang = {
|
||||
formatLocale: gu,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('gu', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import he from 'date-format-parse/lib/locale/he';
|
||||
|
||||
const lang = {
|
||||
formatLocale: he,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('he', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import hi from 'date-format-parse/lib/locale/hi';
|
||||
|
||||
const lang = {
|
||||
formatLocale: hi,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('hi', lang);
|
||||
|
||||
export default lang;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import hr from 'date-format-parse/lib/locale/hr';
|
||||
|
||||
const lang = {
|
||||
formatLocale: hr,
|
||||
yearFormat: 'YYYY',
|
||||
monthFormat: 'MMM',
|
||||
monthBeforeYear: true,
|
||||
};
|
||||
|
||||
DatePicker.locale('hr', lang);
|
||||
|
||||
export default lang;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user