目前element UI上的周控件只有单选周
前段时间 由于需要 本人仿照element单选周的风格写了一个单选周和多选周控件
最近整理了一下代码 分享给大家 希望大家可以相互交流
呼呼呼呼~
组件介绍:
- 以每年的1月1号所在的周数为第一周
- 日历中增加了周数的展示
- 可设置禁选的周数(可配置)
- 多选周组件可以设置一开始周为原点的周范围(可配置)
单选周
用法
单选周:"dateNow" @change="changeDate" :weekOptions="weekOptions">
复制代码
单选周相关Api
// 默认值 [开始值,结算值] 或者 []
dateNow:["2019-02-25", "2019-03-03"],
// 配置禁选周
weekOptions: {
disabledWeeks: {
'2019':[10,11,12] //禁用 2019年 第10,11,12 周
},
}
复制代码
要点一 当月所在的日历生成
单选周的主要突破点在于6排8列的日历数组的实现 要绘制出一个月的日历表 我们需要知道:
- 当月第一天是星期几
- 上个月有多少天
- 本月有多少天
以上可以推算出当前月中上月和下月的日期
主要实现代码:
rows() {
const date = new Date(this.year, this.month, 1) // 本月一号日期
let day = this.getFirstDayOfMonth(date) // 本月第一天的星期
day = (day === 0 ? 7 : day)
const dateCountOfMonth = this.getDayCountOfMonth(date.getFullYear(), date.getMonth()) // 本月的天数
const dateCountOfLastMonth = this.getDayCountOfPreMonth(date.getFullYear(), (date.getMonth() === 0 ? 11 : date.getMonth() - 1)) // 上个月的天数
const datesArry = this.tableRows
let count = 1
// 获取当前月需要渲染的每个单元格的日期
for (let i = 0; i < 6; i++) {
const row = datesArry[i]
for (let j = 0; j < 7; j++) {
let cell = row[j]
if (!cell) {
//初始化cell
cell = { row: i, column: j, type: 'normal', isToday: false, dateString: null, limit: false, week: null,year:null }
}
cell.type = 'normal'
// 获取每个cell对应的日期
if (i >= 0 && i <= 1) {
if (j + i * 7 >= (day - 1)) {
cell.text = count++
} else {
cell.text = dateCountOfLastMonth - (day - 1) + j + 1 - i * 7
cell.type = 'prev-month'
}
} else {
if (count <= dateCountOfMonth) {
cell.text = count++
} else {
cell.text = count++ - dateCountOfMonth
cell.type = 'next-month'
}
}
this.setCellToday(cell) //设置是否今天
this.setCellDateString(cell) //设置格式化的日期
this.setCellLimit(cell) //是否禁用
this.setCellYear(cell) //设置年
this.setCellWeek(cell) //设置周数
this.setDisabledWeeks(cell) //设置禁用周
this.$set(row, j, cell) // 保存
}
}
return (datesArry)
}
复制代码
这样我们可以得到一个6行7列的日历数组rows
以上代码主要是计算出了rows中每个cell展示的日期数字
当然每个cell中还会存储其他相关属性
通过该函数渲染出来的每个cell包含以下内容:
要点二 日历中切换上月下月以及上年下年
切换的实现方式主要运用了 Vue 中 computed 属性
每次点击切换月份或者年份的箭头时 会改变 计算属性 tableString 内引用的值
故计算属性 tableString 会同时被更新
而日历数组也作为计算属性 在计算的时候受 tableString 的影响 最终也会同步更新
点击切换-> 改变 计算属性 tableString-> 改变 计算属性 rows->自动触发日历的更新
这样就实现了 点击切换箭头时候 日历数组也会同步更新
// 日历table表渲染内容 受year/month/day等变化影响
tableString: {
get() {
let year = this.calender_year
let month = parseInt(this.calender_month) + 1
let day
if (month < 10) {
month = '0' + month
}
if (parseInt(this.calender_day) < 10) {
day = '0' + this.calender_day
} else {
day = this.calender_day
}
return year + '-' + month + '-' + day
},
set() {
}
}
复制代码
要点三 获取任意日期所处月份和年份
//以每年的1月1号为第一周 根据日期获取周数
getYearWeekByDate(date) {
if (!date) {
//清空input框时候
let curDate = new Date()
return {
year: curDate.getFullYear(),
week: 0
}
}
//讲日期移到周日计算
var temptDate = new Date(date)
if (temptDate.getDay() !== 0) {
date = this.addDate(temptDate, (7 - temptDate.getDay()))
}
var da = date; //日期格式2018-12-30
//当前日期
var date1 = new Date(da.substring(0, 4), parseInt(da.substring(5, 7)) - 1, da.substring(8, 10));
//1月1号
var date2 = new Date(da.substring(0, 4), 0, 1);
//获取1月1号星期(以周一为第一天,0周一~6周日)
var dateWeekNum = date2.getDay() - 1;
if (dateWeekNum < 0) {
dateWeekNum = 6;
}
date2.setDate(date2.getDate() - dateWeekNum);
var d = Math.round((date1.valueOf() - date2.valueOf()) / 86400000) + 1;
var week = Math.ceil(d / 7);
return {
year: da.substring(0, 4),
week: week
}
}
复制代码
因为我们是以每年的1月1号作为第一周
给组件赋值的时候 需要通过此函数计算默认选中了哪年哪周
如果通过getYear()方法获取默认值的年数和周数的话
跨年的时候
会导致前端展示的选中的周数不全或者错位 日历UI展示的周数和input框对应不上等诸多现象
比如 2018-12-31 直接通过时间函数获取结果是2018年第53周
而我们预期的该日期结果是 2019年第1周
多选周
用法:
多选周:"dateNowDouble" @change="changeDateDouble" :weekOptions="weekOptions">
复制代码
多选周Api:
// 默认值 [开始值,结算值] 或者 []
dateNowDouble:["2019-02-11", "2019-02-24"],
// 配置禁选周
weekOptions: {
disabledWeeks: {
'2019':[10,11,12] //禁用 2019年 第10,11,12 周
},
// 以选择的开始周为原点的可选周范围
weekRange: 2 //多周时候 设置可选的前后周范围
}
}
复制代码
要点一 初始化单元格背景颜色
多选周是在单选周的基础上改造的
核心代码类似 但是业务逻辑稍微复杂了些
多选周的日历数组分为存储在两个不同的数组中 rows_left 和 rows_right
与单选周相比 每个cell 额外增加了 select 属性 记录当前cell被选中的信息
setSelectValue(datesArry) {
datesArry.forEach((row) => {
row.forEach((cell, index) => {
var select = 0
if (cell.type == 'normal') { //本月的才设置选中状态
if ((row[0].year == row[6].year)) {
//如果该周起止日期是同一年 并且选择的结束周 > 开始周
if (cell.week >= this.weekNumStart && cell.week <= this.weekNumEnd && cell.year === Number(this.weekYearStart)) {
select = 1
if (index == 0 && cell.week == this.weekNumStart) {
select = 2
}
if (index == 6 && cell.week == this.weekNumEnd) {
select = 3
}
}
//如果该周跨年了 并且选择的结束周 < 开始周
if (this.weekYearStart != this.weekYearEnd) {
if ((cell.week >= this.weekNumStart && cell.year === Number(this.weekYearStart)) || (cell.week <= this.weekNumEnd && cell.year === Number(this.weekYearEnd))) {
select = 1
if (index == 0 && cell.week == this.weekNumStart) {
select = 2
}
if (index == 6 && cell.week == this.weekNumEnd) {
select = 3
}
}
}
} else {
//如果该周跨年 以该周的周日所在年和周为比较依据 决定该周是否被选中
if (row[6].week >= this.weekNumStart && row[6].week <= this.weekNumEnd && row[6].year === Number(this.weekYearStart)) {
select = 1
if (index == 0) {
select = 2
}
if (index == 6 && cell.week == this.weekNumEnd) {
select = 3
}
}
}
}
this.$set(cell, 'select', select)
})
})
},
复制代码
要点二 周范围的几种情况
周范围意思多选周组件中 选择起始周后 在哪些范围内能选择结束周
比如 周范围设置成 2
起始周选择了2019年第7周
则只能在以开始周为原点上下2周范围内选择结束周
也就是说结束周只能在6-8周中选择 除此之外的其他周将被禁用
//设置可选的周范围
setRngeWeeks(cell) {
//如果设置了特定的周范围 并且选择了开始周
if (this.isSetRange && this.selectFlag !== 0) {
// 获取结束周可选的周范围的起止时间
var weekNums_begin = this.weekNumStart - (Number(this.weekOptions.weekRange) - 1)
var weekNums_end = this.weekNumStart + (Number(this.weekOptions.weekRange) - 1)
//周区间时候 获取开始周的年份的总周数
var current_year = this.getYearWeekByDate(this.firstSelectValue[1].dateString).year
var totalWeeksOfCurrentYear = this.getWeekNumsOfYear(current_year).week
var totalWeeksOfPreYear = this.getWeekNumsOfYear(Number(current_year) - 1).week
var totalWeeksOfAftYear = this.getWeekNumsOfYear(Number(current_year) + 1).week
// 对结束周可选的前后区间的取值情况进行多重分类
if (weekNums_begin >= 1 && weekNums_end <= totalWeeksOfCurrentYear) {
//结束周可选的前后区间落在 今年
if ((cell.week < weekNums_begin && cell.year == this.weekYearStart) || (cell.week > weekNums_end && cell.year == this.weekYearStart)) {
this.$set(cell, 'limit', true)
}
if ((cell.year !== this.weekYearStart)) {
this.$set(cell, 'limit', true)
}
}
if (weekNums_begin < 1 && weekNums_end <= totalWeeksOfCurrentYear) {
//结束周可选的前后区间落在 今年和前一年
weekNums_begin = totalWeeksOfPreYear + weekNums_begin
if ((cell.week < weekNums_begin && (Number(cell.year) + 1) == this.weekYearStart) || (cell.week > weekNums_end && cell.year == this.weekYearStart)) {
this.$set(cell, 'limit', true)
}
if ((cell.year < this.weekYearStart - 1) || (cell.year > this.weekYearStart + 1)) {
this.$set(cell, 'limit', true)
}
}
if (weekNums_begin >= 1 && weekNums_end > totalWeeksOfCurrentYear) {
//结束周可选的前后区间落在 今年和后一年
weekNums_end = weekNums_end - totalWeeksOfCurrentYear
// debugger
if ((cell.week < weekNums_begin && (cell.year) == this.weekYearStart) || (cell.week > weekNums_end && Number(cell.year - 1) == this.weekYearStart)) {
this.$set(cell, 'limit', true)
}
if ((cell.year < this.weekYearStart) || (cell.year > this.weekYearStart + 1)) {
this.$set(cell, 'limit', true)
}
}
if (weekNums_begin < 1 && weekNums_end > totalWeeksOfCurrentYear) {
//结束周可选的前后区间落在 前一年和后一年
weekNums_begin = totalWeeksOfPreYear + weekNums_begin
weekNums_end = weekNums_end - totalWeeksOfCurrentYear
if ((cell.week < weekNums_begin && (cell.year + 1) == this.weekYearStart) || (cell.week > weekNums_end && Number(cell.year - 1) == this.weekYearStart)) {
this.$set(cell, 'limit', true)
}
if ((cell.year < this.weekYearStart - 1) || (cell.year > this.weekYearStart + 1)) {
this.$set(cell, 'limit', true)
}
}
}
}
复制代码
这块逻辑稍微繁琐一点
需要对可选的周范围进行多重分类:
- 结束周可选的前后区间落在 今年
- 结束周可选的前后区间落在 今年和后一年
- 结束周可选的前后区间落在 今年和前一年
- 结束周可选的前后区间落在 前一年和后一年
区间落在哪一年 就需要知道哪一年的总周数 然后再进行加减计算 禁用相关周数
完整单选周多选周源码请移步: github.com/zenda96/wee…
有问题或者交流可以添加VX(请备注: 掘金)