Calendar 日历

示例

单日选择

默认情况下,单击日期可以选中一个日期。

Selected: Wednesday, September 14, 2022

支持 v-model,数据类型为原生 Date 类型。

在 GitHub 上编辑此示例编辑
<template>
<article>
  <veui-calendar v-model="date"/>
  <p>Selected: {{ readableDate }}</p>
</article>
</template>

<script>
import { Calendar } from 'veui'

export default {
  components: {
    'veui-calendar': Calendar
  },
  data () {
    return {
      date: new Date()
    }
  },
  computed: {
    readableDate () {
      return this.date.toLocaleDateString(this.$i18n.locale, {
        weekday: 'long',
        year: 'numeric',
        month: 'long',
        day: 'numeric'
      })
    }
  }
}
</script>

多日、日期范围选择

配置 multiple 属性时,可以选择多个日期。配置 range 属性时,可以选择一个日期范围。

Multiple dates

Selected: Nothing.

Date ranges

Selected: Nothing.

支持 v-model,选择多个单日时数据类型为 Array<Date>,选择日期范围时数据类型为 [Date, Date]

在 GitHub 上编辑此示例编辑
<template>
<article>
  <section class="col">
    <h4>Multiple dates</h4>
    <veui-calendar
      v-model="dates"
      multiple
    />
    <section>Selected: {{ readableDates }}</section>
  </section>
  <section class="col">
    <h4>Date ranges</h4>
    <veui-calendar
      v-model="range"
      range
    />
    <section>Selected: {{ readableRange }}</section>
  </section>
</article>
</template>

<script>
import { Calendar } from 'veui'

export default {
  components: {
    'veui-calendar': Calendar
  },
  data () {
    return {
      dates: null,
      range: null
    }
  },
  computed: {
    readableDates () {
      if (!this.dates || !this.dates.length) {
        return 'Nothing.'
      }
      return this.toReadable(this.dates).join(', ')
    },
    readableRange () {
      if (!this.range) {
        return 'Nothing.'
      }
      return this.toReadable(this.range).join(' to ')
    }
  },
  methods: {
    toReadable (dates) {
      return dates.map(date => date.toLocaleDateString(this.$i18n.locale))
    }
  }
}
</script>

<style lang="less" scoped>
article {
  overflow: hidden;
}

.col {
  float: left;
  width: 340px;
  margin-right: 20px;
}

h4 {
  margin-top: 0;
}
</style>

多日期范围选择

同时配置 multiplerange 属性时,可以选择多段日期范围。配置 panel 属性时,可以指定日历面板的数量。两次选择的时间段合并的策略为,若从未选日期开始选择则选中该时段,否则则取消选择该时段。

Selected: Nothing.

支持 v-model,选择多日期范围时数据类型为 Array<[Date, Date]>

在 GitHub 上编辑此示例编辑
<template>
<article>
  <veui-calendar
    v-model="ranges"
    range
    multiple
    :panel="2"
  />
  <section>Selected: {{ readableRanges }}</section>
</article>
</template>

<script>
import { Calendar } from 'veui'

export default {
  components: {
    'veui-calendar': Calendar
  },
  data () {
    return {
      ranges: null
    }
  },
  computed: {
    readableRanges () {
      if (!this.ranges || this.ranges.length === 0) {
        return 'Nothing.'
      }
      return this.ranges
        .map(range => this.toReadable(range).join(' to '))
        .join(', ')
    }
  },
  methods: {
    toReadable (dates) {
      return dates.map(date => date.toLocaleDateString(this.$i18n.locale))
    }
  }
}
</script>

日历类型

设置 type 来指定日历的类型:年、月、日。

在 GitHub 上编辑此示例编辑
<template>
<article>
  <veui-radio-group
    v-model="type"
    :items="types"
  />
  <veui-calendar :type="type"/>
</article>
</template>

<script>
import { Calendar, RadioGroup } from 'veui'

export default {
  components: {
    'veui-calendar': Calendar,
    'veui-radio-group': RadioGroup
  },
  data () {
    return {
      date: new Date(),
      type: 'date',
      types: [
        { label: 'date', value: 'date' },
        { label: 'month', value: 'month' },
        { label: 'year', value: 'year' }
      ]
    }
  }
}
</script>

<style lang="less" scoped>
.veui-radio-group {
  margin-bottom: 20px;
}
</style>

显示非本月日期

设置 fill-month 来控制是否显示非本月日期。

在 GitHub 上编辑此示例编辑
<template>
<article>
  <section>
    <veui-checkbox v-model="isFill">
      Fill month
    </veui-checkbox>
  </section>
  <veui-calendar :fill-month="isFill"/>
</article>
</template>

<script>
import { Calendar, Checkbox } from 'veui'

export default {
  components: {
    'veui-calendar': Calendar,
    'veui-checkbox': Checkbox
  },
  data () {
    return {
      date: new Date(),
      isFill: true
    }
  }
}
</script>

<style lang="less" scoped>
section {
  margin-bottom: 20px;
}
</style>

禁用日期

设置 disabled-date 来自定义指定日期是否禁用。

只允许选未来的日期

在 GitHub 上编辑此示例编辑
<template>
<article>
  <section>
    <h4>只允许选未来的日期</h4>
    <veui-calendar :disabled-date="disabledDate"/>
  </section>
</article>
</template>

<script>
import { Calendar } from 'veui'

export default {
  components: {
    'veui-calendar': Calendar
  },
  created () {
    this.now = new Date()
    this.now.setHours(0, 0, 0, 0)
  },
  methods: {
    disabledDate (date) {
      return this.now.getTime() > date.getTime()
    }
  }
}
</script>

自定义日期样式

设置 date-class 来自定义指定日期的 class

在 GitHub 上编辑此示例编辑
<template>
<article>
  <section>
    <veui-calendar
      class="demo-date-class"
      :date-class="holidayClass"
    />
  </section>
</article>
</template>

<script>
import { isString, inRange } from 'lodash'
import { Calendar } from 'veui'

const holidays = [
  [true, ['2022-1-1', '2022-1-3']],

  [true, ['2022-1-31', '2022-2-6']],
  [false, ['2022-1-29', '2022-1-30']],

  [true, ['2022-4-3', '2022-4-5']],
  [false, '2022-4-2'],

  [true, ['2022-4-30', '2022-5-4']],
  [false, '2022-4-24', '2022-5-7'],

  [true, ['2022-6-3', '2022-6-5']],

  [true, ['2022-9-10', '2022-9-12']],

  [true, ['2022-10-1', '2022-10-7']],
  [false, ['2022-10-8', '2022-10-9']]
]

export default {
  components: {
    'veui-calendar': Calendar
  },
  computed: {
    isHoliday () {
      const items = holidays.map(function ([isHoliday, ...dateRanges]) {
        dateRanges = dateRanges.map(range => isString(range) ? [range, range] : range)
          .map(([start, end]) => [parseDateString(start), parseDateString(end, true)])
        return [isHoliday, dateRanges]
      })
      return function (date) {
        const time = date.getTime()
        const match = items.find(([, ranges]) => ranges.some(([start, end]) => inRange(time, start, end)))
        return match ? match[0] : undefined
      }
    }
  },
  methods: {
    holidayClass (date) {
      const r = this.isHoliday(date)
      if (r !== undefined) {
        return r ? 'x-holiday' : 'x-workday'
      }
      return [0, 6].includes(date.getDay()) ? 'x-weekend' : undefined
    }
  }
}

function parseDateString (str, isEnd) {
  const [year, month, day] = str.split('-')
  return new Date(year, month - 1, isEnd ? +day + 1 : day, 0, 0, 0).getTime()
}
</script>

<style lang="less">
.demo-date-class {
  .x-holiday button,
  .x-workday button {
    position: relative;
    border: 1px solid red;

    &::before {
      content: "";
      position: absolute;
      top: -1px;
      right: -1px;
      width: 0;
      height: 0;
      border-style: solid;
      border-width: 0 20px 20px 0;
      border-color: transparent #ff2600 transparent transparent;

    }
    &::after {
      content: "假";
      position: absolute;
      top: -1px;
      right: -1px;
      font-size: 9px;
      font-weight: bold;
      color: white;
    }
  }
  .x-workday button {
    border: 1px solid blue;
    &::before {
      border-color: transparent blue transparent transparent;
    }
    &::after {
      content: "班";
    }
  }
  .x-weekend button {
    opacity: .6;
  }
  .veui-calendar-aux {
    button {
      display: none;
    }
  }
}
</style>

自定义日期内容

通过 before 插槽来自定义前置内容。

在 GitHub 上编辑此示例编辑
<template>
<article>
  <veui-calendar class="default" v-model="date">
    <template #before>
      <div class="before">
        <veui-button ui="s basic" @click="pickDay(-1)">Yesterday</veui-button>
        <veui-button ui="s basic" @click="pickDay(0)">Today</veui-button>
        <veui-button ui="s basic" @click="pickDay(1)">Tomorrow</veui-button>
      </div>
    </template>
    <template #date="d">
      <sup v-if="d.date === 1" class="month-tip">{{ formatMonth(d) }}</sup>
      {{ d.date }}
    </template>
  </veui-calendar>
</article>
</template>

<script>
import { Calendar, Button } from 'veui'

const formatter = new Intl.DateTimeFormat('zh-CN', { month: 'long' })

export default {
  components: {
    'veui-calendar': Calendar,
    'veui-button': Button
  },
  data () {
    return {
      date: new Date()
    }
  },
  methods: {
    pickDay (d) {
      let date = new Date()
      date.setDate(date.getDate() + d)
      this.date = date
    },
    formatMonth ({ year, month, date }) {
      return formatter.format(new Date(year, month, date))
    }
  }
}
</script>

<style lang="less" scoped>
.default {
  /deep/ .veui-calendar-day,
  /deep/ .veui-calendar-aux {
    button {
      position: relative;
    }
  }
}
.month-tip {
  position: absolute;
  top: -1px;
  left: -2px;
  right: -2px;
  text-align: center;
  color: #848b99;
}
.before {
  padding: 4px;
  border-bottom: 1px solid #d8d8d8;
  display: flex;
  justify-content: space-around;
}
</style>

API

属性

名称类型默认值描述
typestring='date'日历的类型,可用值为 'date' / 'month' / 'year',分别对应日期/月/年视图。
multipleboolean=false是否可以选择多个日期(范围)。
rangeboolean=false是否选择日期范围。
selectedDate | Array=-

v-model

已选日期(范围)的值,根据 multiplerange 属性值的不同,数据格式不同。

multiplerange类型
falsefalseDate
truefalseArray<Date>
falsetrue[Date, Date]
truetrueArray<[Date, Date]>
panelnumber=1日历面板数量。
todayDate=new Date()「今天」的日期。
week-startnumber=calendar.weekStart一周的起始。可进行全局配置
fill-monthboolean=true当只有一个面板时,是否要在当前月份面板显示非本月日期。
date-classstring | Array | Object | function={}特定日期的自定义 HTML class。传非函数时,数据格式为所有 Vue 支持的 class 表达式;传函数时,签名为 function(Date): string | Array<string>|Object<string, boolean>,返回值格式亦为所有 Vue 支持的 class 表达式。
disabled-datefunction(Date, Date=): boolean=() => false用于自定义指定日期是否禁用。第一个参数为需要判断是否禁用的日期。当处于范围选择过程中且已经选择了一个日期,会作为第二个参数传入。
disabledboolean=false是否为禁用状态。
readonlyboolean=false是否为只读状态。

插槽

名称描述
before日历内,面板上方可供定制的区域。
after日历内,面板下方可供定制的区域。
date

单日单元格内的区域,可用来定制每一天对应区域的内容。

默认内容:对应日期的 date

名称类型描述
yearnumber完整年份。
monthnumber月份数,0 表示一月。
datenumber月份内的日期。

事件

名称描述
select

v-model

选择修改后触发,回调参数为 (selected)。数据类型和 selected 属性一致。

selectstart选择日期范围时,选择完起始日期时触发,回调参数 (picking: Date),表示已选的起始项日期。
selectprogress

选择日期范围时,在已经选择开始日期后,通过鼠标或键盘交互标记到的结束日期变更时触发。回调参数为 (picking),表示当前标记的日期范围,类型取决于 multiple 属性的值。

multiple类型
false[Date, Date]
trueArray<[Date, Date]>
viewchange面板显示的月份发生变化时触发。回调参数 (month: Object<{year: number, month: number, index: number}>),表示当前年月(month0 表示一月)。

全局配置

名称类型默认值描述
calendar.weekStartnumber1一周的第一天是星期几。周一到周日分别对应 17

图标

名称描述
backward上一年。
prev上一页。
next下一页。
forward下一年。
expand展开下拉菜单。
在 GitHub 上编辑此页面编辑