效果如下
Vue实现
工具类util:
export const getFirstWeekDay = (year, month) => {
const date = new Date(year, month - 1, 1)
return date.getDay()
}
export const getMonthLastDay = (year, month) => {
const date = new Date(year, month, 0)
return date.getDate()
}
export const prevMonthRestDay = (year, month) => { // [28, 29, 30]
const firstDayWeek = getFirstWeekDay(year, month)
let prevMonthLastDay = getMonthLastDay(year, month - 1)
const lastDayArr = []
for (let i = 0; i < firstDayWeek; i++) {
lastDayArr.unshift(prevMonthLastDay)
prevMonthLastDay--
}
return lastDayArr
}
export const nextMonthRestDay = (year, month) => { // [1, 2, 3]
const firstDayWeek = getFirstWeekDay(year, month)
const thisMonthLastDay = getMonthLastDay(year, month)
const nextDayCount = 42 - (firstDayWeek + thisMonthLastDay)
const nextDayArr = []
for (let i = 1; i <= nextDayCount; i++) {
nextDayArr.push(i)
}
return nextDayArr
}
export const getDateInfo = (timeStamp) => {
const date = new Date(timeStamp)
return {
year: date.getFullYear(),
month: date.getMonth() + 1,
date: date.getDate()
}
}
组件index:
<template>
<div class="container">
<div class="input-box">
<input type="text" @click="openCalendar" v-model="throwDate" />
<div v-if="isShow" class="calendar" @click="events">
<div class="control-area">
<span class="control-btn btn-year-lt"><<</span>
<span class="control-btn btn-month-lt"><</span>
<span class="control-show">
<span class="control-title">
<span class="title-year">{{ dateInfo.year }}</span>
</span>
<span>
<span class="title-month">{{ dateInfo.month }}</span>
</span>
</span>
<span class="control-btn btn-month-gt">></span>
<span class="control-btn btn-year-gt">>></span>
</div>
<table>
<thead v-if="timeType == 'DATE'">
<tr>
<th>日</th>
<th>一</th>
<th>二</th>
<th>三</th>
<th>四</th>
<th>五</th>
<th>六</th>
</tr>
</thead>
<tbody>
<template>
<tr v-for="(tr, index) in dateTrs" :key="index">
<td v-for="(td, dex) in tr" :key="dex"
:class="{ 'rest-day': td.type !== 'this', 'current-day': td.type === 'this', 'current': td.current, 'seleted': td.seleted }">
{{
td.name
}}</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script>
import { getDateInfo, prevMonthRestDay, nextMonthRestDay, getMonthLastDay } from './util'
export default defineComponent({
name: 'index',
emits: ['throwDate'],
props: {
timeStemp: {
type: Number,
default: +new Date()
}
},
setup(props, SetupContext) {
const TIME_TYPE = {
'DATE': 'DATE',
'YEAR': 'YEAR',
'MONTH': 'MONTH'
}
const dateInfo = ref(getDateInfo(props.timeStemp))
const data = reactive({
isShow: false,
throwDate: '',
dateTrs: [],
timeType: 'DATE'
})
onMounted(() => {
data.throwDate = dateInfo.value.year + '-' + (dateInfo.value.month < 10 ? '0' + dateInfo.value.month : dateInfo.value.month) + '-' + (dateInfo.value.date < 10 ? '0' + dateInfo.value.date : dateInfo.value.date)
})
const createDateTrs = () => {
const date = new Date()
data.dateTrs = []
const prevMonthRest = prevMonthRestDay(dateInfo.value.year, dateInfo.value.month)
const currentMonthDay = getMonthLastDay(dateInfo.value.year, dateInfo.value.month)
const nextMonthRest = nextMonthRestDay(dateInfo.value.year, dateInfo.value.month)
const prevMonthRestTd = []
const currentMonthDayTd = []
const nextMonthRestTd = []
prevMonthRest.forEach(i => prevMonthRestTd.push({ name: i, type: 'prev' }))
for (let i = 1; i <= currentMonthDay; i++) {
currentMonthDayTd.push({ name: i, type: 'this' })
if (i == date.getDate() && dateInfo.value.year == date.getFullYear() && dateInfo.value.month == date.getMonth() + 1) {
currentMonthDayTd[i - 1]['current'] = true
} else if (i == dateInfo.value.date && data.throwDate.split('-')[0] == dateInfo.value.year && data.throwDate.split('-')[1] == dateInfo.value.month) {
currentMonthDayTd[i - 1]['seleted'] = true
}
}
nextMonthRest.forEach(i => nextMonthRestTd.push({ name: i, type: 'next' }))
integTrs(prevMonthRestTd, currentMonthDayTd, nextMonthRestTd)
}
const integTrs = (...args) => {
const dateTds = [...(args.flat())]
let index = 0
for (let i = 0; i < 6; i++) {
if (!data.dateTrs[i]) data.dateTrs[i] = []
for (let j = 0; j < 7; j++) {
data.dateTrs[i].push(dateTds[index])
index++
}
}
}
const events = (e) => {
console.log(e.target.className);
switch (data.timeType) {
case (TIME_TYPE.DATE):
dateEvent(e.target)
break
case (TIME_TYPE.YEAR):
break
case (TIME_TYPE.MONTH):
break
}
}
const dateEvent = (target) => {
switch (target.className) {
case 'control-btn btn-year-gt':
dateInfo.value.year += 1
break
case 'control-btn btn-year-lt':
dateInfo.value.year -= 1
break
case 'control-btn btn-month-gt':
if (dateInfo.value.month === 12) {
dateInfo.value.month = 1
dateInfo.value.year += 1
} else {
dateInfo.value.month += 1
}
break
case 'control-btn btn-month-lt':
if (dateInfo.value.month === 1) {
dateInfo.value.month = 12
dateInfo.value.year -= 1
} else {
dateInfo.value.month -= 1
}
break
}
if (target.className.includes('current-day')) {
document.querySelectorAll('.current-day').forEach(td => {
td.className = td.className.replace(' seleted', '')
})
target.className += ' seleted'
dateInfo.value.date = Number(target.innerHTML)
data.throwDate = dateInfo.value.year + '-' + (dateInfo.value.month < 10 ? '0' + dateInfo.value.month : dateInfo.value.month) + '-' + (dateInfo.value.date < 10 ? '0' + dateInfo.value.date : dateInfo.value.date)
SetupContext.emit('throwDate', data.throwDate)
data.isShow = false
}
createDateTrs()
}
const openCalendar = () => {
data.isShow = true
createDateTrs()
}
return {
...toRefs(data),
dateInfo,
openCalendar,
events
}
}
})
</script>
<style lang="scss" scoped>
.input-box {
position: relative;
width: 200px;
margin-right: 100px;
@keyframes identifier {
0% {
height: 0;
}
100% {
height: 300px;
}
}
.calendar {
position: absolute;
top: 26px;
left: 0;
width: 324px;
background-color: aliceblue;
transform: translateX(-25%);
animation: identifier .3s ease-out 1 normal forwards;
z-index: 999;
overflow: hidden;
}
table {
width: 100%;
font-size: 12px;
thead {
border-bottom: 1px solid #ddd;
}
th {
font-weight: 400;
}
tr {
height: 38px;
}
td {
text-align: center;
&.rest-day {
color: #ccc;
}
&.current-day {
cursor: pointer;
}
&.current {
color: orange;
font-weight: 700;
}
&.seleted {
color: #fff !important;
background: orange !important;
}
}
border-collapse: collapse;
}
.control-area {
height: 30px;
border-bottom: 1px solid #ddd;
.control-btn {
float: left;
display: block;
width: 30px;
height: 30px;
color: orange;
text-align: center;
line-height: 30px;
cursor: pointer;
}
.control-show {
float: left;
display: block;
width: 204px;
height: 30px;
text-align: center;
line-height: 30px;
cursor: pointer;
color: orange;
font-weight: 700;
}
}
}
input {
outline: none;
border: 1px solid pink;
width: 100%;
height: 25px;
box-sizing: border-box;
padding-left: 10px;
cursor: pointer;
}
</style>
原生js实现
分三个模块,日期面板、年份面板、月份面板
项目结构:
入口文件:
import MyCalendar from './calendar/index';
(() => {
MyCalendar('#app', (date) => {
console.log(date);
})
})()
首页加载index
import event from './event'
import { reactive } from './store'
import { render } from './date/render'
import { getDateInfo } from './util'
import './index.scss'
export default (...args) => {
if (args.length === 2 && typeof args[1] === 'function') {
const [year, month] = getDateInfo()
const [el, hander] = args
init(el, [year, month], hander)
} else {
const [el, [year, month], hander] = args
init(el, [year, month], hander)
}
}
const init = (el, [year, month], handler) => {
const oApp = document.querySelector(el)
const oContainer = document.createElement('div')
oContainer.className = 'my-calendar'
const dateInfo = reactive({ year, month })
event(oContainer, handler, dateInfo) // 注册事件监听
render(oContainer, year, month)
oApp.appendChild(oContainer)
}
公共样式部分:
.my-calendar {
width: 324px;
border: 1px solid #ddd;
}
公共utils:
/*
公共utils
*/
// 创建几个tr
export const createTrs = (count) => {
const trArr = []
for (var i = 0; i < count; i++) {
const oTr = document.createElement('tr')
trArr.push(oTr)
}
return trArr
}
export const getDateInfo = (timeStamp) => { // 传入时间戳,返回当天日期
const date = timeStamp ? new Date(timeStamp) : new Date()
return [
date.getFullYear(),
date.getMonth() + 1,
date.getDate()
]
}
公共store,用于响应数据的变化,及时去创建或更新面板
/*
@description: 响应式文件,监听年、月的修改,及时去更新update
*/
import { update as dateUpdate, render as dateRender } from "./date/render";
import { update as yearUpdate, render as yearRender } from "./year/render";
import { update as monthUpdate, render as monthRender } from "./month/render";
export const ALLOWED_FLAGS = {
'YEAR': 'YEAR',
'MONTH': 'MONTH',
'DATE': 'DATE'
}
let currentFlag = ALLOWED_FLAGS.DATE
export function getFlag() {
return currentFlag
}
export function setFlag(value, container, { year, month }) {
if (ALLOWED_FLAGS[value]) {
currentFlag = ALLOWED_FLAGS[value]
}
switch (currentFlag) {
case ALLOWED_FLAGS.YEAR:
yearRender(container, year)
break
case ALLOWED_FLAGS.MONTH:
monthRender(container, year, month)
break
case ALLOWED_FLAGS.DATE:
dateRender(container, year, month)
break
}
}
export function reactive({ year, month }) {
const dateInfo = {}
const _dateInfo = [year, month]
Object.defineProperties(dateInfo, {
year: {
get() {
return _dateInfo[0]
},
set(year) {
_dateInfo[0] = year
update(..._dateInfo)
}
},
month: {
get() {
return _dateInfo[1]
},
set(month) {
_dateInfo[1] = month
update(..._dateInfo)
}
}
})
return dateInfo
}
function update(year, month) {
switch (currentFlag) {
case ALLOWED_FLAGS.YEAR:
yearUpdate(year)
break
case ALLOWED_FLAGS.MONTH:
monthUpdate(year)
break
case ALLOWED_FLAGS.DATE:
dateUpdate(year, month)
}
}
所有事件,利用事件代理方式,只绑定根元素点击事件,最后触发子节点通过冒泡触发事件
import { ALLOWED_FLAGS, getFlag, setFlag } from "./store"
let target = null
export default (container, handler, dateInfo) => { // hander: 切换日期时执行的回调
container.addEventListener('click', handlerClick.bind(null, container, handler, dateInfo), false)
}
const handlerClick = (...args) => {
const [container, handler, dateInfo, e] = args // 事件对象永远在最后一位
const tar = e.target
const className = tar.className
const flag = getFlag()
// console.log(className);
if (className.includes('current-day')) { // 日期面板点击日期
dateClick(tar, handler)
return
}
if (className.includes('decade-year')) { // 年份面板点击年份
yearClick(container, tar, dateInfo)
}
if (className.includes('static-month')) { // 月份面板点击月份
monthClick(container, tar, dateInfo)
}
if (className == 'title-year') { // 日期面板点击顶部年份
titleYearClick(container, dateInfo)
return
}
if (className == 'title-month') { // 日期面板点击顶部月份
titleMonthClick(container, dateInfo)
return
}
// 不论哪个面板点击控制栏时
switch (flag) {
case ALLOWED_FLAGS.YEAR:
yearControlClick(className, dateInfo) // 年份面板点击控制栏箭头
break
case ALLOWED_FLAGS.MONTH:
monthControlClick(className, dateInfo) // 月份面板点击控制栏箭头
break
case ALLOWED_FLAGS.DATE:
dateControlClick(className, dateInfo) // 日期面板点击控制栏箭头
}
}
// 日期面板点击日期
const dateClick = (tar, handler) => {
if (target) {
target.className = target.className.replace(' seleted', '')
}
target = tar
tar.className += ' seleted'
handler && handler(tar.dataset.date)
}
// 日期面板控制栏箭头
const dateControlClick = (className, dateInfo) => {
switch (className) {
case 'control-btn btn-year-lt':
dateInfo.year -= 1
break
case 'control-btn btn-month-lt':
dateInfo.month > 1 && (dateInfo.month -= 1)
break
case 'control-btn btn-year-gt':
dateInfo.year = dateInfo.year + 1
break
case 'control-btn btn-month-gt':
dateInfo.month < 12 && (dateInfo.month += 1)
break
}
}
console.log('------------------------------');
// 日期面板点击顶部年份
const titleYearClick = (container, dateInfo) => {
setFlag(ALLOWED_FLAGS.YEAR, container, dateInfo)
}
// 年份面板点击年份
const yearClick = (container, tar, dateInfo) => {
dateInfo.year = Number(tar.dataset.year)
setFlag(ALLOWED_FLAGS.DATE, container, dateInfo)
}
// 年份面板顶部控制栏箭头
const yearControlClick = (className, dateInfo) => {
switch (className) {
case 'year-control-btn btn-year-lt':
dateInfo.year -= 10
break
case 'year-control-btn btn-year-gt':
dateInfo.year += 10
break
}
}
console.log('------------------------------');
// 日期面板点击顶部月份
const titleMonthClick = (container, dateInfo) => {
setFlag(ALLOWED_FLAGS.MONTH, container, dateInfo)
}
// 年份面板点击年份
const monthClick = (container, tar, dateInfo) => {
dateInfo.month = Number(tar.dataset.month)
setFlag(ALLOWED_FLAGS.DATE, container, dateInfo)
}
// 月份面板点击控制栏左右箭头
const monthControlClick = (className, dateInfo) => {
switch (className) {
case 'month-control-btn btn-year-lt':
dateInfo.year -= 1
break
case 'month-control-btn btn-year-gt':
dateInfo.year += 1
break
}
}
日期模块
工具util:
export const getFirstWeekDay = (year, month) => { // 当月1号是周几 2
const date = new Date(year, month - 1, 1)
return date.getDay()
}
export const getMonthDayCount = (year, month) => { // 当月有多少天(最后一天的日期)
const date = new Date(year, month, 0) // 下个月的第0天也就是当月最后一天
return date.getDate()
}
export const getLastMonthRestDays = (year, month) => { // 当月1号前有几天(29,30,31,1,2,3,4)
const days = getFirstWeekDay(year, month) // 当月1号周几
let lastDate = getMonthDayCount(year, month - 1) // 上月最后一天
const restDays = []
while (restDays.length < days) {
restDays.unshift(lastDate--)
}
return restDays
}
export const getNextMonthRestDays = (year, month) => { // 当月最后一天后下月的天数 (26,27,28,1,2,3,4)
const lastMonthRestDayCount = getFirstWeekDay(year, month) // 1号前还有多少天
const currentMonthDayCount = getMonthDayCount(year, month) // 当月最后一天
const nextMonthRestDayCount = 42 - (lastMonthRestDayCount + currentMonthDayCount)
let restDays = []
for (let i = 1; i <= nextMonthRestDayCount; i++) {
restDays.push(i)
}
return restDays
}
export const getFormatDate = (year, month, date) => { // 点击展示当前日期
const dateArr = [year, month, date]
for (let i = 1; i < dateArr.length; i++) {
dateArr[i] < 10 && (dateArr[i] = '0' + dateArr[i])
}
return dateArr.join('-')
}
用于创建日期面板的节点 creator
import { createTrs } from "../util"
import { WEEK_DAYS } from "./config"
import { getFormatDate, getLastMonthRestDays, getMonthDayCount, getNextMonthRestDays } from "./util"
import { getDateInfo } from "../util"
// 生成表头(星期)
const domPool = { // keep-alive操作,固定不变的dom不必每次重新生成
weekDays: null,
controlArea: null
}
export const createWeekDayNode = () => {
if (!domPool.weekDays) {
domPool.weekDays = document.createElement('tr')
domPool.weekDays.className = 'week-day'
domPool.weekDays.innerHTML = WEEK_DAYS.map(item => {
return `<th>${item}</th>`
}).join('')
}
return domPool.weekDays
}
// 生成所有身体tr含td
export const createDateNode = (year, month) => {
const lastMonthRestDays = getLastMonthRestDays(year, month) // [29, 30, 31]
const currentMonthDayCount = getMonthDayCount(year, month) // 28
const nextMonthRestDays = getNextMonthRestDays(year, month) // [1, 2, 3, 4, 5]
const dateTrArr = createTrs(6)
const lastMonthRestDaysTd = createRestDaysTD(lastMonthRestDays) // [node(30), ...] 上月
const currentMonthDayCountTd = createCurrentDaysTD(currentMonthDayCount, year, month) // [node(1) ~ node(30)]
const nextMonthRestDaysTd = createRestDaysTD(nextMonthRestDays) // [node(1), ...] 下月
const dateArr = [
...lastMonthRestDaysTd,
...currentMonthDayCountTd,
...nextMonthRestDaysTd
]
let index = 0
dateTrArr.forEach(tr => { // 将6行tr依次循环,添加子节点td
for (let i = 0; i < 7; i++) {
tr.appendChild(dateArr[index])
index++
}
})
return dateTrArr
}
// 创建上月、下月目标日期
const createRestDaysTD = (restDays) => {
return restDays.map(item => {
const oTd = document.createElement('td')
oTd.className = 'day rest-day'
oTd.innerText = item
return oTd
})
}
// 生成当月td
const createCurrentDaysTD = (currentDayCount, year, month) => {
let tdArr = []
const [
currentYear,
currentMonth,
currentDate
] = getDateInfo()
for (let i = 1; i <= currentDayCount; i++) {
const oTd = document.createElement('td')
if (currentYear === year && currentMonth === month && currentDate === i) {
oTd.className = 'day current-day current'
} else {
oTd.className = 'day current-day'
}
oTd.innerText = i
oTd.setAttribute('data-date', getFormatDate(year, month, i))
tdArr.push(oTd)
}
return tdArr
}
// 生成顶部控制栏
export const createControlArea = (year, month) => {
if (!domPool.controlArea) {
domPool.controlArea = document.createElement('div')
domPool.controlArea.className = 'control-area'
domPool.controlArea.innerHTML = `
<span class="control-btn btn-year-lt"><<</span>
<span class="control-btn btn-month-lt"><</span>
<span class="control-show">
<span class="control-title">
<span class="title-year">${year}</span>
</span>
<span>
<span class="title-month">${month}</span>
</span>
</span>
<span class="control-btn btn-month-gt">></span>
<span class="control-btn btn-year-gt">>></span>
`
} else { // 每次更新只需要改值即可,不用每次生成dom
domPool.controlArea.querySelector('.title-year').innerText = year
domPool.controlArea.querySelector('.title-month').innerText = month
}
return domPool.controlArea
}
用于渲染节点的render:
import { createWeekDayNode, createDateNode, createControlArea } from "./creator"
import './index.scss'
export function render(oContainer, year, month) {
oContainer.innerHTML = ''
const oTable = document.createElement('table')
oTable.className = 'my-calendar-table'
const oThead = document.createElement('thead')
const oTbody = document.createElement('tbody')
const weekDayNode = createWeekDayNode() // 表头星期行
const dateTrs = createDateNode(year, month) // 所有身体tr含td
dateTrs.forEach(tr => oTbody.appendChild(tr))
oTbody.className = 'my-calendar-body'
oThead.appendChild(weekDayNode)
oTable.appendChild(oThead)
oTable.appendChild(oTbody)
const controlArea = createControlArea(year, month) // 控制栏
oContainer.appendChild(controlArea)
oContainer.appendChild(oTable)
}
export function update(year, month) { // 左右切换时的变化
// 表格数据的变化
const oTbody = document.querySelector('.my-calendar-body')
oTbody.innerHTML = ''
const dateTrs = createDateNode(year, month)
dateTrs.forEach(tr => oTbody.appendChild(tr))
// 控制台的年月文字变化
const oTitleYear = document.querySelector('.title-year')
const oTitleMonth = document.querySelector('.title-month')
oTitleYear.innerText = year
oTitleMonth.innerText = month
}
config:
export const WEEK_DAYS = [
'日',
'一',
'二',
'三',
'四',
'五',
'六'
]
样式文件:
.my-calendar-table {
width: 100%;
font-size: 12px;
color: #666;
border-collapse: collapse;
tHead {
border-bottom: 1px solid #ddd;
}
th {
font-weight: 400;
}
tr {
height: 38px;
}
td {
text-align: center;
}
.day {
&.rest-day {
color: #ccc;
}
&.current-day {
cursor: pointer;
}
&.current {
color: orange;
font-weight: 700;
}
&.seleted {
color: #fff;
background: orange;
}
}
}
.control-area {
height: 30px;
border-bottom: 1px solid #ddd;
.control-btn {
float: left;
display: block;
width: 30px;
height: 30px;
color: orange;
text-align: center;
line-height: 30px;
cursor: pointer;
}
.control-show {
float: left;
display: block;
width: 204px;
height: 30px;
text-align: center;
line-height: 30px;
cursor: pointer;
color: orange;
font-weight: 700;
}
}
年份模块
工具:
export function createDecadeYears(year) {
const str = year.toString().slice(0, 3)
const yearArr = []
for (let i = 0; i < 10; i++) {
yearArr.push(Number(str + i))
}
return yearArr
}
export function getStartAndEndYear(year) {
const str = year.toString().slice(0, 3)
return [Number(str + '0'), Number(str + '9')]
}
生成节点:
import { createTrs, getDateInfo } from "../util";
import { createDecadeYears, getStartAndEndYear } from "./util";
const domPool = {
controlArea: null
}
// 生成年份面板的控制区
export function createYearControlArea(year) {
const [startYear, endYear] = getStartAndEndYear(year) // [2020, 2029]
if (!domPool.controlArea) {
domPool.controlArea = document.createElement('div')
domPool.className = 'year-control-area'
domPool.controlArea.innerHTML = `
<span class="year-control-btn btn-year-lt"><<</span>
<span class="control-show">
<span class="control-title">
<span class="start-year">${startYear}</span>
-
<span class="end-year">${endYear}</span>
</span>
</span>
<span class="year-control-btn btn-year-gt">>></span>
`
} else {
domPool.controlArea.querySelector('.start-year').innerText = startYear
domPool.controlArea.querySelector('.end-year').innerText = endYear
}
return domPool.controlArea
}
// 生成年份面板的td
export function createYearTD(year) {
const decadeYearArr = createDecadeYears(year) // [2020, 2021, ..., 2029]
const [currentYear] = getDateInfo()
const tdArr = []
decadeYearArr.forEach(year => {
const oTd = document.createElement('td')
oTd.className = 'year decade-year'
if (currentYear === year) {
oTd.className += ' current-year'
}
oTd.innerText = year
oTd.setAttribute('data-year', year)
tdArr.push(oTd)
})
return tdArr
}
// 生成年份面板的tr含td
export function createYearNode(year) {
const yearTrArr = createTrs(3) // 3个tr
const yearTds = createYearTD(year)
let index = 0
yearTrArr.forEach(tr => {
for (let i = 0; i < 4 && yearTds[index]; i++) {
tr.appendChild(yearTds[index])
index++
}
})
return yearTrArr
}
渲染:
import { createYearControlArea, createYearNode } from "./creator";
import './index.scss'
import { getStartAndEndYear } from "./util";
export function render(container, year) {
container.innerHTML = ''
const oTable = document.createElement('table')
oTable.className = 'my-year-calendar-table'
const controlArea = createYearControlArea(year) // 控制器
const yearNode = createYearNode(year) // tr
yearNode.forEach(tr => {
oTable.appendChild(tr)
})
container.appendChild(controlArea)
container.appendChild(oTable)
}
export function update(year) { // 年份左右切花时的变化
const oTable = document.querySelector('.my-year-calendar-table')
const oStartYear = document.querySelector('.start-year')
const oEndStart = document.querySelector('.end-year')
const yearNode = createYearNode(year) // tr
const [startYear, endYear] = getStartAndEndYear(year)
oStartYear.innerText = startYear
oEndStart.innerText = endYear
oTable.innerHTML = ''
yearNode.forEach(tr => {
oTable.appendChild(tr)
})
}
样式:
.my-year-calendar-table {
width: 100%;
font-size: 12px;
color: #666;
tr {
height: 38px;
}
td {
text-align: center;
}
.year {
cursor: pointer;
&.current-year {
color: orange;
}
&.current {
color: orange;
font-weight: 700;
}
}
}
.year-control-area {
height: 30px;
border-bottom: 1px solid #ddd;
}
.year-control-btn {
float: left;
display: block;
width: 30px;
height: 30px;
color: orange;
text-align: center;
line-height: 30px;
cursor: pointer;
}
.control-show {
float: left;
display: block;
width: 264px;
height: 30px;
text-align: center;
line-height: 30px;
font-weight: 700;
&:hover {
cursor: pointer;
color: orange;
}
}
月份模块
生成节点:
import { createTrs } from "../util"
import { MONTHS } from "./config"
const domPool = {
controlArea: null,
monthTrs: null
}
// 月份面板生成控制器
export function createMonthControlArea(year) {
if (!domPool.controlArea) {
domPool.controlArea = document.createElement('div')
domPool.controlArea.className = 'month-control-area'
domPool.controlArea.innerHTML = `
<span class="month-control-btn btn-year-lt"><<</span>
<span class="control-show">
<span class="control-title">
<span class="title-year">${year}</span>
</span>
</span>
<span class="month-control-btn btn-year-gt">>></span>
`
} else {
domPool.controlArea.querySelector('.title-year').innerText = year
}
return domPool.controlArea
}
// 月份面板生成行tr含td
export function createMonthNode(month) {
if (!domPool.monthTrs) {
domPool.monthTrs = createTrs(4) // [tr, tr, tr, tr]
let index = 0
domPool.monthTrs.forEach(tr => {
for (let i = 0; i < 3; i++) {
const oTd = document.createElement('td')
oTd.className = 'static-month'
oTd.setAttribute('data-month', index)
oTd.innerText = MONTHS[index]
if (index === month) {
oTd.className += ' current'
}
tr.appendChild(oTd)
index++
}
})
}
return domPool.monthTrs
}
渲染节点:
import { createMonthNode, createMonthControlArea } from './creator'
import './index.scss'
export const render = (container, year, month) => {
container.innerHTML = ''
const oTable = document.createElement('table')
oTable.className = 'my-month-calendar-table'
const controlArea = createMonthControlArea(year) // 控制器
const monthNode = createMonthNode(month) // tr
monthNode.forEach(tr => {
oTable.appendChild(tr)
})
container.appendChild(controlArea)
container.appendChild(oTable)
}
export const update = (year) => {
const oYear = document.querySelector('.title-year')
oYear.innerText = year
}
样式:
.my-month-calendar-table {
width: 100%;
font-size: 12px;
color: #666;
tr {
height: 38px;
}
td {
text-align: center;
}
.static-month {
cursor: pointer;
&.current {
color: orange;
}
}
}
.month-control-area {
height: 30px;
border-bottom: 1px solid #ddd;
.month-control-btn {
float: left;
display: block;
width: 30px;
height: 30px;
color: orange;
text-align: center;
line-height: 30px;
cursor: pointer;
}
.control-show {
float: left;
display: block;
width: 264px;
height: 30px;
text-align: center;
line-height: 30px;
font-weight: 700;
&:hover {
cursor: pointer;
color: orange;
}
}
}
config配置:
.my-month-calendar-table {
width: 100%;
font-size: 12px;
color: #666;
tr {
height: 38px;
}
td {
text-align: center;
}
.static-month {
cursor: pointer;
&.current {
color: orange;
}
}
}
.month-control-area {
height: 30px;
border-bottom: 1px solid #ddd;
.month-control-btn {
float: left;
display: block;
width: 30px;
height: 30px;
color: orange;
text-align: center;
line-height: 30px;
cursor: pointer;
}
.control-show {
float: left;
display: block;
width: 264px;
height: 30px;
text-align: center;
line-height: 30px;
font-weight: 700;
&:hover {
cursor: pointer;
color: orange;
}
}
}