手写日历【发布订阅】

思路/难点

一.排版

  • 需满足6 * 7 的排版,否则可能选择的某月无法显示完全
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NadMNZqy-1657784151904)(/Users/wangyongjie/Library/Application Support/typora-user-images/image-20220714104458559.png)]

二.获取传入时间的年份以及月份

  • 将传入时间通过格式化转为 ****年 **月

三.确定日历的第一个展示的是哪天

// date 为选中月的1号日期
date.setDate(date.getDate() - date.getDay() + 第一位排的是星期几)714日 该选中月份1号为 71日。则 date.setDate(1-5+0)
即 date.setDate(-4)   =>  626日

如果第一位排的不是周日,而是周六。 date.setDate(1-5+6)
即 date.setDate(2)  此时设置日期比初始1号日期还要大,那么就需要往前推一周
即 date.setDate(2-7)  =>  625

四.切换月份设定方式

不可以通过当前选中日期加减一个月!

如:当前选中日期 为 7月31日,此时减去一个月,直接变成了6月31日这个非法的值,2月的日期还不固定。

因此左右切换的时候,传入的永远为当前月的第一天。

如:传入7月1日,减去一个月 为6月1日,不会产生问题

五.观察者模式来处理数据传递

  • 发布者 【头部切换年月】
    • 触发notify,向订阅者发出通知
  • 订阅者 【主题部分日期变化】
    • 观察者注册,便于之后收到通知后执行更新方法

----全部代码----

Utils.js

import { Subject } from './subject'

let transfer = function (date, fmt) {
  const _date = new Date(date)
  let o = {
    'M+': _date.getMonth() + 1, // 月份
    'd+': _date.getDate(), // 日
    'h+': _date.getHours(), // 小时
    'm+': _date.getMinutes(), // 分
    's+': _date.getSeconds(), // 秒
    'q+': Math.floor((_date.getMonth() + 3) / 3), // 季度
    S: _date.getMilliseconds(), // 毫秒
  }

  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, (_date.getFullYear() + '').substr(4 - RegExp.$1.length))
  }
  for (let k in o) {
    if (new RegExp('(' + k + ')').test(fmt)) {
      fmt = fmt.replace(
        RegExp.$1,
        RegExp.$1.length === 1 ? o[k] + '' : ('00' + o[k]).substr(('' + o[k]).length)
      )
    }
  }

  return fmt
}

/**
 * 用于format日期格式
 * @param {*} timeSpan
 * @param {*} fmt
 * @param {*} formatDateNullValue
 */
export const dateFormat = function (timeSpan, fmt, formatDateNullValue) {
  if (!timeSpan) {
    if (formatDateNullValue) {
      return formatDateNullValue
    }
    return '无'
  }

  let date = new Date(timeSpan)

  return transfer(date, fmt)
}

/**
 * 获取日历header内容 格式为:****年 **月
 * @param {*} date
 */
export const getHeaderContent = function (date) {
  let _date = new Date(date)

  return dateFormat(_date, 'yyyy年 MM月')
}

/**
 * 获取当前月的第一天
 * @param {*} date
 */
export const getFirstDayOfMonth = function (date) {
  let _date = new Date(date)
  _date.setDate(1)

  return _date
}

/**
 * 获取当前月日历的第一天
 * @param {*} date
 */
export const getFirstDayOfCalendar = function (date, weekLabelIndex) {
  let _date = new Date(date)

  _date = new Date(_date.setDate(_date.getDate() - _date.getDay() + weekLabelIndex))
  // 如果当前日期大于当前月第一天,则需要减去7天
  if (_date > date) {
    _date = new Date(_date.setDate(_date.getDate() - 7))
  }

  return _date
}

/**
 * 根据传入index确认weeklabel的顺序
 * @param {*} weekIndexOfFirstWeekDay
 */
export const getWeekLabelList = function (weekIndexOfFirstWeekDay) {
  let weekLabelArray = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']

  for (let index = 0; index < weekIndexOfFirstWeekDay; index++) {
    let weekLabel = weekLabelArray.shift() || ''
    weekLabelArray.push(weekLabel)
  }

  return weekLabelArray
}

/**
 * 启动观察者模式,并且初始化
 */
export const initObserver = function () {
  let subject = new Subject()

  return subject
}

/**
 * 格式化日期为两个单词,例如:‘1’号 格式为 ‘01’
 * @param {*} dateNumber
 */
export const formatDayWithTwoWords = function (dateNumber) {
  if (dateNumber < 10) {
    return '0' + dateNumber
  }

  return dateNumber
}

/**
 * 比较当前日期是否为本月日期,用于进行本月数据高亮显示
 * @param {*} firstDayOfMonth
 * @param {*} date
 */
export const isCurrentMonth = function (firstDayOfMonth, date) {
  return firstDayOfMonth.getMonth() === date.getMonth()
}

/**
 * 比较当前日期是否为系统当前日期
 * @param {*} date
 */
export const isCurrentDay = function (date) {
  let _date = new Date()
  return (
    date.getFullYear() === _date.getFullYear() &&
    date.getMonth() === _date.getMonth() &&
    date.getDate() === _date.getDate()
  )
}

/**
 *
 * @param {*} firstDayOfCurrentMonth
 * @returns
 */
export const isMoreCurrentDay = function (date) {
  let _date = new Date().getTime()

  return new Date(date).getTime() > _date
}

/**
 * 以传入参数作为基准获取下个月的第一天日期
 * @param {*} firstDayOfCurrentMonth
 */
export const getFirstDayOfNextMonth = function (firstDayOfCurrentMonth) {
  return new Date(
    firstDayOfCurrentMonth.getFullYear(),
    firstDayOfCurrentMonth.getMonth() + 1,
    1
  )
}

/**
 * 以传入参数作为基准获取上个月的第一天日期
 * @param {*} firstDayOfCurrentMonth
 */
export const getFirstDayOfPrevMonth = function (firstDayOfCurrentMonth) {
  return new Date(
    firstDayOfCurrentMonth.getFullYear(),
    firstDayOfCurrentMonth.getMonth() - 1,
    1
  )
}

/**
 * 以传入参数作为基准获取当前月的日期
 * @param {*} firstDayOfCurrentMonth
 */
export const getFirstDayOfCurrntMonth = function (firstDayOfCurrentMonth) {
  return new Date(firstDayOfCurrentMonth.getFullYear(), firstDayOfCurrentMonth.getMonth(), 1)
}

subject.js

/*
 * Subject
 * 内部创建了三个方法,内部维护了一个ObserverList。
 */
export class Subject {
  constructor() {
    this._observers = new ObserverList()
  }

  // addObserver: 调用内部维护的ObserverList的add方法
  addObserver(observer) {
    this._observers.add(observer)
  }

  // removeObserver: 调用内部维护的ObserverList的removeAt方法
  removeObserver(observer) {
    this._observers.removeAt(this._observers.indexOf(observer, 0))
  }

  // notify: 通知函数,用于通知观察者并且执行update函数,update是一个实现接口的方法,是一个通知的触发方法。
  notify(context) {
    let observerCount = this._observers.count()

    for (let i = 0; i < observerCount; i++) {
      this._observers.get(i).update(context)
    }
  }
}

/*
 * ObserverList
 * 内部维护了一个数组,4个方法用于数组的操作,这里相关的内容还是属于subject,因为ObserverList的存在是为了将subject和内部维护的observers分离开来,清晰明了的作用。
 */
class ObserverList {
  constructor() {
    this._observerList = []
  }

  add(obj) {
    return this._observerList.push(obj)
  }

  count() {
    return this._observerList.length
  }

  get(index) {
    if (index > -1 && index < this._observerList.length) {
      return this._observerList[index]
    }

    throw new Error(`_observerList ${index} 未知为null`)
  }

  indexOf(obj, startIndex) {
    let i = startIndex

    while (i < this._observerList.length) {
      if (this._observerList[i] === obj) {
        return i
      }
      i++
    }

    return -1
  }

  removeAt(index) {
    this._observerList.splice(index, 1)
  }
}

export class Observer {
  update() {}
}

index.vue






Calendar.vue







CalendarHeader.vue







CalendarBody.vue







你可能感兴趣的:(javascript,前端,Vue,javascript,前端,开发语言)