UNPKG

@prettyy/ui

Version:

vue2 UI

658 lines (572 loc) 18.3 kB
import dateUtil from './date' import fixedDate from "./fixedDate" /** * 获取上个月剩下的天数(距离这个月一周内) * @param {Date} date * @param {Number} weekStart 星期开始量 * @return {Array} */ function getPrevMonthDays(date, weekStart) { date = new Date(date.getTime()) date.setDate(1) // 本月1号是星期几 const weekDay = date.getDay() // 临界条件: 正好本月的第一天是【设置的一周的起始量】 // 不需要数据 if (weekDay === weekStart) { return [] } // 上月需要计入的天数 let lastDays if (weekStart === 0) { lastDays = weekDay } else if (weekDay === 0) { lastDays = 7 - weekStart } else if (weekDay < weekStart) { // 如果当前星期量小于起始量 lastDays = (7 + weekDay - weekStart) % 7 } else { // 当前的星期量大于起始量 // 使用差值就行了 lastDays = weekDay - weekStart } // 将时间跳转到上月的最后一天 date.setDate(0) // 上月的最后一天是几号 const day = date.getDate() // 年 const year = date.getFullYear() // 月 const month = date.getMonth() + 1 // 天数集合 const days = [] // 上月的最后一天不是星期六(星期天是一周第一天) // 那就要在这月显示 for (let i = 1; i <= lastDays; i++) { const value = day - lastDays + i date.setDate(value) days.push({ overflow: true, date: value, value: date, year, month, day: date.getDay() }) } return days } /** * 获取当月的天数 * @param {Date} date * @return {Array} */ function getCurrentMonthDays(date) { date = new Date(date.getTime()) // 设置日期到下个月 // 将时间跳转到本月的最后一天 date.setMonth(date.getMonth() + 1, 0) // 本月的最后一天是几号 const day = date.getDate() // 年 const year = date.getFullYear() // 月 const month = date.getMonth() + 1 // 天数集合 const days = [] for (let i = 1; i <= day; i++) { date.setDate(i) days.push({ date: i, value: date, year, month, day: date.getDay() }) } return days } /** * 获取这个月底到下个月的天数(距离这个月最后一天的一周内) * @param {Date} date * @param {number} remain 剩下的空余数量 * @return {Array} */ function getNextMonthDays(date, remain) { date = new Date(date.getTime()) // 设置日期到下个月 date.setMonth(date.getMonth() + 1) // 年 const year = date.getFullYear() // 月 const month = date.getMonth() + 1 // 天数集合 const days = [] // 多显示7天 for (let i = 1; i <= remain + 7; i++) { date.setDate(i) days.push({ overflow: true, date: i, value: date, year, month, day: date.getDay() }) } return days } /** * 解析日期偏移串 * @param {Object|string} offset * @return {{year: number, month: number, date: number}} */ function resolveDateOffset(offset) { if (!offset) { return { year: 0, month: 0, date: 0 } } // 值是偏移对象 if (typeof offset === 'object') { return offset } // 不是字符串,数据无效 if (typeof offset !== 'string') { throw Error(`[datepicker] Invalid offset type: ${typeof offset}`) } // 值是偏移串 const matches = /^((?<year>-?(\d+)?)y)?((?<month>-?(\d+)?)m)?((?<date>-?(\d+)?)d)?$/i.exec(offset) if (!matches) { throw Error(`[datepicker] Invalid offset value: ${offset}`) } return { year: parseInt(matches.groups.year) || 0, month: parseInt(matches.groups.month) || 0, date: parseInt(matches.groups.date) || 0 } } const util = { /** * 获取当前日期是一年中的第几天 * @param date * @param includeCurrentMonth * @return {number} */ getDayOfYear(date, includeCurrentMonth) { date = date ? this.parse(date) : fixedDate.getDate() let days = 0 if (includeCurrentMonth !== false) { days += date.getDate() } // 前面的月份 let month = date.getMonth() for (let i = 0; i < month; i++) { date.setMonth(i + 1, 0) days += date.getDate() } return days }, /** * 获取传入日期处于一年中的第多少周 * @param {Date|Date[]} date * @param {object} [option] * @param {number} [option.start=0] 周的偏移值 * @return {{year: Number, week: Number}} */ getWeekOfYear(date, option) { option = { start: 0, ...option } const [start] = Array.isArray(date) ? date.map(d => this.parse(d)) : this.getWeekRange(date, { start: option.start }) // 当传入的是日期范围时,date + 3 ,表示一周中间的那一天 // 得到周所在的日期 const weekDate = Array.isArray(date) ? this.offsetDate(start, { date: 3 }) : date // 当年的第一天是星期几 const weekDayOfFirstDay = this.setDate(weekDate, { month: 0, date: 1 }).getDay() // 周日期所在年的第一天的星期数 + 周日期所在其年的天数 - 起始日期 / 7 // 得到周数 const offset = weekDayOfFirstDay > option.start ? option.start : option.start - 7 const days = weekDayOfFirstDay + this.getDayOfYear(weekDate) - offset return { year: weekDate.getFullYear(), week: Math.ceil(Math.abs(days) / 7) } }, /** * 获取传入日期处于一月中的第多少周 * @param {Date|Date[]} date * @param {object} [option] * @param {number} [option.start=0] 周的偏移值 * @return {{year: Number, month: Number, week: Number}} */ getWeekOfMonth(date, option) { option = { start: 0, ...option } const [start] = Array.isArray(date) ? date.map(d => this.parse(d)) : this.getWeekRange(date, { start: option.start }) // 当传入的是日期范围时,date + 3 ,表示一周中间的那一天 // 得到周所在的日期 const weekDate = Array.isArray(date) ? this.offsetDate(start, { date: 3 }) : date // 当月的第一天是星期几 const weekDayOfFirstDay = this.setDate(weekDate, { date: 1 }).getDay() // 周日期所在月的第一天的星期数 + 周日期所在其月的天数 - 起始日期 / 7 // 得到周数 const offset = weekDayOfFirstDay > option.start ? option.start : option.start - 7 const days = weekDayOfFirstDay + weekDate.getDate() - offset return { year: weekDate.getFullYear(), month: weekDate.getMonth(), week: Math.ceil(Math.abs(days) / 7) } }, /** * 根据传入日期生成日期所在月的日历视图 * @param {Date|String|Number} date * @param {Number} [weekStart=0] * @return {*[]} */ makeDateView(date, weekStart) { date = date ? this.parse(date) : fixedDate.getDate() // 一共是7列5行 const size = 7 * 5 const prevMonthDays = getPrevMonthDays(date, weekStart || 0) const currentMonthDays = getCurrentMonthDays(date) const nextMonthDays = getNextMonthDays(date, size - prevMonthDays.length - currentMonthDays.length) return [...prevMonthDays, ...currentMonthDays, ...nextMonthDays] }, /** * 给指定的起始日期设置时间范围 00:00:00 和 23:59:59 * @param {Date} begin * @param {Date} end * @private */ appendTime(begin, end) { begin.setHours(0, 0, 0) end.setHours(23, 59, 59) }, /** * 按指定规则对日期进行偏移 * @param {Date} date * @param {Object|string} offset 日期的偏移量 * @return {Date} 偏移后的日期对象(新对象) */ offsetDate(date, offset) { offset = resolveDateOffset(offset) const newValue = { year: date.getFullYear() + (parseInt(offset.year) || 0), month: date.getMonth() + (parseInt(offset.month) || 0), date: 1 } const newDate = new Date(newValue.year, newValue.month, newValue.date) const newLastDate = date.getDate() + (parseInt(offset.date) || 0) if (offset.date) { newDate.setDate(newLastDate) return newDate } // 当未指定日期偏移时 // 使用原日期的 date // 此处的逻辑是为了防止日期大于本月的最后一天 const lastDate = this.getLastDayOfMonth(newDate) if (newLastDate <= lastDate) { newDate.setDate(newLastDate) return newDate } newDate.setDate(lastDate) return newDate }, /** * 获取指定月份的最后一天是几号 * @param {Date} date * @param {number} [month] 不指定时,使用当前日期的月份 * @return {number} */ getLastDayOfMonth(date, month) { month = arguments.length > 1 ? month : date.getMonth() // 获取当月最后一天 const temp = this.setDate(date, { month: month + 1, date: 0 }) return temp.getDate() }, /** * 根据一个日期以及偏移参数获取日期范围 * @param {Date} date * @param {Object|string} [beginOffset] 开始日期的偏移量 * @param {Object|string} [endOffset] 结束日期的偏移量 * @param {object} [option] * @param {string} [option.format] 格式化串,不指定时返回 Date 类型 * @param {boolean} [option.time=false] 是否附带时间串 * @return {Date[]|String[]} */ getDateRange(date, beginOffset, endOffset, option) { const { time, format } = option || {} const begin = this.offsetDate(date, beginOffset) const end = this.offsetDate(date, endOffset) if (time) { this.appendTime(begin, end) } const range = [begin, end] return format ? range.map(d => this.format(d, format)) : range }, /** * 根据一个日期,谋算出其所在周的起止日期 * @param {Date} date * @param {Object} [option] * @param {number} [option.start=0] 周起始量,0-6分别表示星期天到星期六 * @param {number} [option.offset=0] 周偏移量,可以是任意整数 * @param {boolean} [option.time=false] 是否附带时间串 * @param {string} [option.format] 格式化串,不指定时返回 Date 类型 * @return {Date[]|String[]} */ getWeekRange(date, option) { const { start, offset, time, format } = { start: 0, offset: 0, ...option } const weekDay = date.getDay() const begin = new Date(date.getTime()) // 先找出星期天为第一天的日期 begin.setDate(begin.getDate() - weekDay) // ----判断 start 的位置 与传入日期的位置差---- // 如果 start 大于 传入日期,则直接使用 start 对应的日期为起始 // 否则,将开始日期 - 7(跳转到上一周) // 再移动 start 的天数,就是正确的起始日期 if (start > weekDay) { begin.setDate(begin.getDate() - 7) } // ----判断 结束---- if (start) { // 再移动 start 的天数,就是正确的起始日期 begin.setDate(begin.getDate() + start) } const end = new Date(begin.getFullYear(), begin.getMonth(), begin.getDate() + 6) if (offset) { begin.setDate(begin.getDate() + Math.round(offset) * 7) end.setFullYear(begin.getFullYear(), begin.getMonth(), begin.getDate() + 6) } if (time) { this.appendTime(begin, end) } const range = [begin, end] return format ? range.map(d => this.format(d, format)) : range }, /** * 根据一个日期,谋算出其所在月的起止日期 (月的第一天和最后一天) * @param {Date} date * @param {Object} [option] * @param {number} [option.offset=0] 月偏移量,可以是任意整数 * @param {boolean} [option.time=false] 是否附带时间串 * @param {string} [option.format] 格式化串,不指定时返回 Date 类型 * @return {Date[]|String[]} */ getMonthRange(date, option) { const { offset, time, format } = { offset: 0, ...option } const begin = new Date(date.getTime()) const end = new Date(date.getTime()) begin.setMonth(begin.getMonth() + Math.round(offset), 1) end.setMonth(end.getMonth() + Math.round(offset) + 1, 0) if (time) { this.appendTime(begin, end) } const range = [begin, end] return format ? range.map(d => this.format(d, format)) : range }, /** * 根据一个日期,谋算出其所在季度的起止日期 * @param {Date} date * @param {Object} [option] * @param {number} [option.offset=0] 季度偏移量,可以是任意整数 * @param {boolean} [option.time=false] 是否附带时间串 * @param {string} [option.format] 格式化串,不指定时返回 Date 类型 * @return {Date[]|String[]} */ getQuarterRange(date, option) { const { offset, time, format } = { offset: 0, ...option } const month = date.getMonth() const beginMonth = Math.floor(month / 3) * 3 const begin = new Date(date.getTime()) begin.setMonth(beginMonth, 1) const end = new Date(date.getTime()) // 设置为月底 end.setMonth(begin.getMonth() + 3, 0) if (offset) { begin.setMonth(begin.getMonth() + Math.round(offset) * 3, 1) end.setFullYear(begin.getFullYear(), end.getMonth() + Math.round(offset) * 3 + 1, 0) } if (time) { this.appendTime(begin, end) } const range = [begin, end] return format ? range.map(d => this.format(d, format)) : range }, /** * 按照指定的值设置 Date 对象 * @param {Date|String|Number} date * @param {Object} [option] * @param {Number} [option.year] * @param {Number} [option.month] * @param {Number} [option.date] * @param {Number} [option.hour] * @param {Number} [option.minute] * @param {Number} [option.second] * @param {Boolean} [option.copy=true] * @return {Date} */ setDate(date, option) { const { year, month, date: day, hour, minute, second, copy } = { copy: true, ...option } const temp = copy ? new Date(date.getTime()) : date if (year !== undefined && year !== null) { temp.setFullYear(year) } if (month !== undefined && month !== null) { temp.setMonth(month, 1) } if (day !== undefined && day !== null) { temp.setDate(day) } if (hour !== undefined && hour !== null) { temp.setHours(hour, 0, 0) } if (minute !== undefined && minute !== null) { temp.setMinutes(minute, 0) } if (second !== undefined && second !== null) { temp.setSeconds(second) } return temp }, /** * 将任意类型的日期格式转换成 Date 类型 * @param {Date|String|Number} date * @param {String} [format] 当 date 是字符串时,通过此参数指定格式 * @return {Date} */ parse(date, format) { if (date instanceof Date) { return new Date(date.getTime()) } if (date instanceof Number) { return new Date(date) } if (typeof date === 'string') { const now = fixedDate.getDate() let today = this.format(now, 'yyyy-MM-dd') if (/^\d{4}$/.test(date)) { // 2018 -> 2018-01-01 date = `${date}-01-01` if (!format) { format = 'yyyy-MM-dd' } } else if (/^\d{2}:\d{2}$/.test(date)) { // 10:10 -> 20xx-01-01 10:10:00 date = `${today} ${date}:00` if (!format) { format = 'yyyy-MM-dd HH:mm:ss' } } else if (/^\d{4}-\d{2}$/.test(date)) { // 2018-10 -> 2018-10-01 date = `${date}-01` if (!format) { format = 'yyyy-MM-dd' } } else if (/^\d{2}:\d{2}:\d{2}$/.test(date)) { // 10:10:10 -> 20xx-01-01 10:10:10 date = `${today} ${date}` if (!format) { format = 'yyyy-MM-dd HH:mm:ss' } } else if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(date)) { // 2018-10-10 10:10 -> 2018-10-10 10:10:00 date = `${date}:00` if (!format) { format = 'yyyy-MM-dd HH:mm:ss' } } else if (!format) { format = 'yyyy-MM-dd' } } return dateUtil.parse(date, format) || new Date(date) }, /** * 将任意格式的日期格式化成指定的格式 * @param {Date|String|Number} date * @param {String} format 输出格式 * @param {String} [inputFormat] 当 date 是字符串时,通过此参数指定格式,不指定时使用 format 的值 * @return {string} */ format(date, format, inputFormat) { if (!date) { return '' } const value = this.parse(date, inputFormat || format) return dateUtil.format(value, format) }, /** * * @param {Date|String|Number} date1 * @param {Date|String|Number} date2 * @param {String} format 日期格式 * @return {boolean} */ equals(date1, date2, format) { if (!date1 && !date2) { return true } if (!date1 || !date2) { return false } return this.format(date1, format) === this.format(date2, format) }, pad(val, len, fill) { val = String(val) len = len || 2 fill = fill || '0' while (val.length < len) { val = fill + val } return val }, /** * * @param {HTMLElement} element * @param {HTMLElement} test */ isParent(element, test) { if (!element || !test) { return false } if (element === test) { return true } if (element === document.body) { return false } const parentElement = element.parentElement if (parentElement === test) { return true } if (parentElement === document.body) { return false } return this.isParent(parentElement, test) } } export default util