@prettyy/ui
Version:
vue2 UI
658 lines (572 loc) • 18.3 kB
JavaScript
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