Vue 组件(component)教程之实现精美的日历方法示例
组件(component)是vue最强大的功能之一。组件可以扩展html元素,封装可重用的代码,根据项目需求,抽象出一些组件,每个组件里包含了展现、功能和样式。每个页面,根据自己的需要,使用不同的组件来拼接页面。这种开发模式使得前端页面易于扩展,且灵活性高,而且组件之间也实现了解耦。
最近应公司的要求,需要开发一个精美的日历组件(ios , 安卓, pc 的ie9+都能运行),写完后想把它分享出来,希望大家批评。
先来个截图
代码已经分享到 https://github.com/zhangkunusergit/vue-component ()
使用方法
根据需求先说一下怎么用吧 (上面是:html, 下面是js )
<date-picker v-if="showdatepicker" :date="date" :min-date="mindate" :max-date="maxdate" @confirm="confirm" @cancel="cancel" ></date-picker>
import datapicker from './components/datepicker.vue'; import './style.scss'; new vue({ el: '#app', data() { return { date: '2017-09-11', mindate: '2000-09-11', maxdate: '2020-09-11', showdatepicker: false, selecteddate: '点击选择日期', }; }, methods: { opendatepicker() { this.showdatepicker = true; }, confirm(value) { this.showdatepicker = false; this.selecteddate = value; }, cancel() { this.showdatepicker = false; }, }, components: { datapicker, }, });
我们提供了最大值、最小值和初始值,唯一不足的地方是时间格式只能是yyyy-mm-dd (2017-12-12) ,大家可以从github上拉取代码运行看一下(由于没有仔细测试,可能会有bug和性能问题,希望指出)。
(一)先画好界面
这个不是重点,html 和 css,应该很简单,大家看我的css 可能感觉我的命名太长了,只因为我们公司用,担心其他样式影响它(可能有其他方式吧,希望大神指出)
(二)组装日期列表
先看代码:
rows() { const { year, month } = this.showdate; const months = (new date(year, month, 0)).getdate(); const result = []; let row = []; let weekvalue; // 按照星期分组 for (let i = 1; i <= months; i += 1) { // 根据日期获取星期,并让开头是1,而非0 weekvalue = (new date(year, month, i)).getday() + 1; // 判断月第一天在星期几,并填充前面的空白区域 if (i === 1 && weekvalue !== 1) { this.addrowemptyvalue(row, weekvalue); this.addrowdayvalue(row, i); } else { this.addrowdayvalue(row, i); // 判断月最后一天在星期几,并填充后面的空白区域 if (i === months && weekvalue !== 7) { this.addrowemptyvalue(row, (7 - weekvalue) + 1); } } // 按照一周分组 if (weekvalue % 7 === 0 || i === months) { result.push(row); row = []; } } this.showdate.monthstr = monthjson[this.showdate.month]; return result; },
我的思路是:
(1)获取月份天数,并按照星期分组;
(2)如果月份第一天不在星期一,前面填充空值。同理,如何月份最后一天不在周日,最后面填充空值,目的是:让分的组 长度都是7,也就是一周。这样可以用flex布局方式快速开发了;
(3)里面也包含一些限制,比如小于mindate和大于maxdate, 不让点击等等
(三)切换月份
(1)上一个月份
/** * 切换到上一个月 */ prevmonth() { if (this.prevmonthclick) { return; } this.prevmonthclick = true; settimeout(() => { this.prevmonthclick = false; }, 500); this.fadextype = 'fadex_prev'; // 如何当前月份已经小于等于minmonth 就不让其在执行 if (this.isminlimitmonth()) { return; } const { year, month } = this.showdate; // 判断当前月份,如果已经等于1(1就是一月,而不是二月) if (month <= 1) { this.showdate.year = year - 1; this.showdate.month = 12; } else { this.showdate.month -= 1; } },
settimeout()主要是让其显示动画后自动消失。 fadextype 是动画类型
(2)下一个月份
/** * 切换到下一个月 */ nextmonth() { if (this.nextmonthclick) { return; } this.nextmonthclick = true; settimeout(() => { this.nextmonthclick = false; }, 500); this.fadextype = 'fadex_next'; // 如何当前月份已经大于等于maxmonth 就不让其在执行 if (this.ismaxlimitmonth()) { return; } const { year, month } = this.showdate; // 判断当前月份,如果已经等于12(12就是十二月) if (month >= 12) { this.showdate.year = year + 1; this.showdate.month = 1; } else { this.showdate.month += 1; } },
这里面的settimeout() 和prevmonth方法的原理一样。
上面两种切换月份的功能主要注意:
a. 因为有mindate和maxdate,所以首先考虑的是不能超出这个限制。
b. 要考虑切换月份后年的变化,当月份大于12后,年加1 ,月变成 1。
(四)选择年份
(1)点击最上面的年,显示年份列表
openyearlist() { if (this.showyear) { this.showyear = false; return; } const index = this.yearlist.indexof(this.selectdate.year); this.showyear = true; // 打开年列表,让其定位到选中的位置上 settimeout(() => { this.$refs.yearlist.scrolltop = (index - 3) * 40; }); },
(2)选择年份
selectyear(value) { this.showyear = false; this.showdate.year = value; let type; // 当日期在最小值之外,月份换成最小值月份 或者 当日期在最大值之外,月份换成最大值月份 if (this.isminlimitmonth()) { type = 'copymindate'; } else if (this.ismaxlimitmonth()) { // 当日期在最大值之外,月份换成最大值月份 type = 'copymaxdate'; } if (type) { this.showdate.month = this[type].month; this.showdate.day = this[type].day; this.resetselectdate(this.showdate.day); return; } let dayvalue = this.selectdate.day; // 判断日是最大值,防止另一个月没有这个日期 if (this.selectdate.day > 28) { const months = (new date(this.showdate.year, this.showdate.month, 0)).getdate(); // 当前月份没有这么多天,就把当前月份最大值赋值给day dayvalue = months < dayvalue ? months : dayvalue; } this.resetselectdate(dayvalue); },
在切换年份时注意一下方面:
a. 考虑mindate和maxdate, 因为如果之前你选择的月份是1月,但是限制是9月,在大于mindate(比如2017) 年份没有问题,但是到了mindate 的具体年份(比如2010),那么月份最小值只能是九月,需要修改月份,maxdate同理。
b. 如何之前你选择的day是31,由于切换年份后,这个月只有30天,记得把day 换成这个月最大值,也就是30。
(五)处理原始数据
其实这一条正常情况下,应该放在第一步讲,但是我是根据我的开发习惯来写步骤的。我一般都是先写功能,数据是模拟的,等写好了,再考虑原始数据格式和暴露具体的方法等等,因为这样不会改来改去,影响开发和心情。
initdatepicker() { this.showdate = { ...this.splitdate(this.date, true) }; this.copymindate = { ...this.splitdate(this.mindate) }; this.copymaxdate = { ...this.splitdate(this.maxdate) }; this.selectdate = { ...this.showdate }; }, splitdate(date, addstr) { let result = {}; const splitvalue = date.split('-'); try { if (!splitvalue || splitvalue.length < 3) { throw new error('时间格式不正确'); } result = { year: number(splitvalue[0]), month: number(splitvalue[1]), day: number(splitvalue[2]), }; if (addstr) { result.week = (new date(result.year, result.month, result.day)).getday() + 1; result.monthstr = monthjson[result.month]; result.weekstr = weekjson[result.week]; } } catch (error) { console.error(error); } return result; },
这里目的是:
a. 处理原始数据,把原始数据查分,用json缓存下来,这样方便后面操作和显示。这里面我只兼容yyyy-mm-dd的格式,其他的都不兼容,如果你想兼容其他格式,你可以修改其代码,或者用moment.js 等其他库帮你做这件事情。
b. 拆分后的格式如下:
year: '', month: '', day: '', week: '', weekstr: '', monthstr: '',
总结
上面就是开发这个组件的详细讲解,如有不妥,请指出批评。 仔细想想,其实这个不难,就是有点琐碎。仔细就好
这里的所有动画都是用的vue 的 transition,大家可以看看官网,非常详细。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
上一篇: Java类加载的过程
下一篇: 淘宝下架“区块链白皮书代写服务”