最近遇到一个需求:选择一年中的任意天数作为非工作日,后端接口提供的是同一保存的方法。

这就需要把一年中的每一天同时展示在页面上,一般的calender组件都是只显示一个月,只好自己画一个了。

分析需求:

  1. 根据确定的年、月得出这个月的日期面板
  2. 只要确定了是那一年哪一个月,可以通过moment.js的api知道这个月一共有几天,第一天是星期几
  3. 观察日历的日期面板一般都是分为6行,7列,(1号是一周最后几天,31号会使用到第六行)
  4. 考虑1号不一定是周一的情况,会有一个偏移量

实现代码:

getDateList() {
// 获取日期的列表
const arr = [
[0,0,0,0,0,0,0],
[0,0,0,0,0,0,0],
[0,0,0,0,0,0,0],
[0,0,0,0,0,0,0],
[0,0,0,0,0,0,0],
[0,0,0,0,0,0,0],
]
const date = moment(`${this.year}-${this.month}`)
let offset
let week = generateConfig.getWeekDay(date) - 1
let endDate = generateConfig.getEndDate(date).date()
let i
let j
Array(42).fill(0).forEach((item,index) => {
if(week < 1) {
offset = - 1
} else if( week > 1) {
offset = week - 1
} else {
offset = 0
}
i = Math.floor(index/7)
j = index%7
if(week > index || index > endDate + offset ) {
arr[i][j] = ''
} else {
arr[i][j] = index - offset
}
})
this.list = arr
},

这里就获得了一个可以直接渲染的面板数据,在页面中渲染:

<tr class="list" v-for="(item,index) in list" :key="index" >
<td
v-for="(ite,ind) in item"
:key="ind"
@click="handleSelect(ite)" >
<span
class="item"
:class="{'not-empty': !!ite, 'item-selected': isSelected(ite), 'red': (ind === 5 || ind === 6)}"
>{{ite}}</span>
</td>
</tr>

搞完这些之后觉得会不会有更好的方法,看了antd的源码,rc-picker里面具体的面板实现的思路基本是一样的。

// =============================== Body ===============================
const rows: React.ReactNode[] = [];

for (let i = 0; i < rowNum; i += 1) {
const row: React.ReactNode[] = [];
let rowStartDate: DateType;

for (let j = 0; j < colNum; j += 1) {
const offset = i * colNum + j;
const currentDate = getCellDate(baseDate, offset);
const disabled = disabledDate && disabledDate(currentDate);

if (j === 0) {
rowStartDate = currentDate;

if (prefixColumn) {
row.push(prefixColumn(rowStartDate));
}
}

const title = titleCell && titleCell(currentDate);

row.push(
<td
key={j}
title={title}
className={classNames(cellPrefixCls, {
[`${cellPrefixCls}-disabled`]: disabled,
[`${cellPrefixCls}-start`]: getCellText(currentDate) === 1 || picker === 'year' && Number(title) % 10 === 0,
[`${cellPrefixCls}-end`]: title === getLastDay(generateConfig, currentDate) || picker === 'year' && Number(title) % 10 === 9,
...getCellClassName(currentDate),
})}
onClick={() => {
if (!disabled) {
onSelect(currentDate);
}
}}
onMouseEnter={() => {
if (!disabled && onDateMouseEnter) {
onDateMouseEnter(currentDate);
}
}}
onMouseLeave={() => {
if (!disabled && onDateMouseLeave) {
onDateMouseLeave(currentDate);
}
}}
>
{getCellNode ? (
getCellNode(currentDate)
) : (
<div className={`${cellPrefixCls}-inner`}>{getCellText(currentDate)}</div>
)}
</td>,
);
}

rows.push(
<tr key={i} className={rowClassName && rowClassName(rowStartDate!)}>
{row}
</tr>,
);
}