123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- <template>
- <!-- 日历滚动插件 -->
- <view class="zsy_calendar">
- <!-- 日历顶部信息 -->
- <view class="calendar_info">
- <text class="title">打卡记录</text>
- <text class="desc">
- [{{ getAssignDateInfo(false, 0) === getAssignDateInfo(true, 0) ? '' : getAssignDateInfo(false, 0) + '年' }}{{ getAssignDateInfo(false, 1) }}月]
- </text>
- <text v-show="showBackToTodayBtn" class="backToToday" :style="{color: dateActiveColor}"
- @tap="goToDate()">回到今天</text>
- </view>
- <!-- 日历周数 -->
- <view class="calendar_week">
- <view v-for="(item, index) in week" :key="index" class="calendar_week__item">{{ item }}</view>
- </view>
- <!-- 日历轮播 -->
- <view class="calendar_swiper">
- <!-- 展开情况下的日历轮播 -->
- <swiper v-if="swiperMode === 'open'" key="normalSwiper" circular :style="{height: swiperHeight('open')}"
- :current="current" :duration="duration" :skip-hidden-item-layout="true"
- @change="e => current = e.detail.current">
- <swiper-item v-for="(swiper, swiperIndex) in 3" :key="swiperIndex" class="swiper-item">
- <DateBox :dates="calendarSwiperDates[swiperIndex]" :cellHeight="cellHeight"
- :dateActiveColor="dateActiveColor" :swiperMode="swiperMode" @chooseDate="chooseDate" />
- </swiper-item>
- </swiper>
- </view>
- </view>
- </template>
- <script>
- import {
- mapState,
- mapMutations
- } from 'vuex'
- import {
- parseTime,
- deepClone
- } from './js/utils.js'
- import DateBox from './dateBox.vue'
- export default {
- name: 'ZsyCalendar',
- components: {
- DateBox
- },
- props: {
- duration: { // 轮播图动画时长
- type: Number,
- default: 300
- },
- cellHeight: { // 一列的高度
- type: Number,
- default: 75
- },
- dateActiveColor: { // 日期选中颜色
- type: String,
- default: '#4169E1'
- },
- sundayIndex: { // 星期天所在索引,0表示第一个、6表示最后一个
- type: Number,
- default: 6
- },
- mode: { // 日历模式
- type: String,
- default: 'open'
- },
- changeSetDefault: { // 月份切换时是否显示一号还是当前月份选中高亮
- type: Boolean,
- default: true
- }
- },
- data() {
- return {
- today: parseTime(new Date(), '{y}-{m}-{d}'), // 今天日期
- selectedDate: null, // 选中日期
- week: [], // 日历周数
- current: 1, // 当前日历轮播默认显示索引
- // shrinkCurrent: 1, // 缩放日历轮播默认显示索引
- calendarSwiperDates: [], // 日历轮播日期信息
- calendarSwiperShrinkDates: [], // 日历轮播收缩时的日期信息
- dateActive: -1, // 日期选中索引
- swiperByClick: false, // 是否通过点击上月份或下月份的日期进行轮播切换
- shrinkSwiperByClick: false, // 是否通过点击上月份或下月份的日期进行收缩日历的轮播切换
- swiperMode: this.mode, // 日历轮播显示模式 open:展开 close:收缩
- dateCache: {}, // 日期缓存
- emitTimer: null, // 日期改变向父级传递当前选中日期计时器
- dateClick: false, // 是否进行了日期的点击选择
- }
- },
- computed: {
- ...mapState('m_children', ['records']),
- /* 获取指定日期信息
- isCurDate: 是否获取当天的信息还是选中日期的信息
- index: 0 表示年份 1 表示月份 2 表示日期 */
- getAssignDateInfo() {
- return (isCurDate, index) => {
- return (isCurDate ? this.today : this.selectedDate).split('-')[index] * 1
- }
- },
- // 是否显示回到今天按钮
- showBackToTodayBtn() {
- return this.getAssignDateInfo(false, 0) !== this.getAssignDateInfo(true, 0) || this.getAssignDateInfo(
- false, 1) !== this.getAssignDateInfo(true, 1)
- },
- // 返回轮播图高度
- swiperHeight() {
- return (swiperMode) => {
- const normalHeight = (this.calendarSwiperDates[this.current] || []).length / 7 * (this.cellHeight +
- 20) + 'rpx'
- const shrinkHeight = this.cellHeight + 20 + 'rpx'
- return swiperMode === 'open' ? normalHeight : shrinkHeight
- }
- }
- },
- watch: {
- // 展开日历轮播切换
- current(newV, oldV) {
- if (newV === 0 && oldV === 2) { // 右滑
- this.swiperChange(1)
- return
- }
- if (newV === 2 && oldV === 0) { // 左滑
- this.swiperChange(-1)
- return
- }
- if (newV > oldV) { // 右滑
- this.swiperChange(1)
- } else { // 左滑
- this.swiperChange(-1)
- }
- },
- selectedDate: {
- deep: true,
- handler(newV, oldV) {
- if (newV && (oldV === null || this.dateClick)) { // 初始化/日历点击选择时直接返回
- this.emitDate()
- this.dateClick = false
- } else { // 其它情况做防抖处理
- if (this.emitTimer !== null) {
- clearTimeout(this.emitTimer)
- }
- this.emitTimer = setTimeout(() => {
- this.emitDate()
- }, this.duration + 1)
- }
- }
- }
- },
- created() {
- this.init() // 初始化数据
- },
- methods: {
- ...mapMutations('m_children', ['updateNoAttendNum']),
- // 初始化数据
- init() {
- if (this.selectedDate === null) this.selectedDate = this.today // 默认选中日期为当天
- this.initWeek() // 初始化要显示的周数
- this.initCalendarSwiperDates() // 初始化日历轮播日期信息
- // 解决swiperMode初始化为收缩时没有初始化日历收缩轮播日期信息
- },
- // 初始化周数
- initWeek() {
- const normalWeek = ['日', '一', '二', '三', '四', '五', '六'] // 正常周数
- const sIndex = this.sundayIndex < 0 ? 0 : this.sundayIndex >= normalWeek.length ? normalWeek.length - 1 :
- this.sundayIndex
- normalWeek.unshift(...normalWeek.slice(-sIndex))
- normalWeek.length = 7
- this.week = normalWeek
- },
- // 初始化展开时的日历轮播日期信息
- initCalendarSwiperDates(cb) {
- const year = this.getAssignDateInfo(false, 0)
- const month = this.getAssignDateInfo(false, 1)
- const cur = this.generateCalendar(year, month)
- const prev = this.generateCalendar(month === 1 ? year - 1 : year, month === 1 ? 12 : month - 1)
- const next = this.generateCalendar(month === 12 ? year + 1 : year, month === 12 ? 1 : month + 1)
- // 根据current来判断相邻的轮播存放哪些日历数据
- if (this.current === 0) {
- this.calendarSwiperDates = [cur, next, prev]
- } else if (this.current === 1) {
- this.calendarSwiperDates = [prev, cur, next]
- } else if (this.current === 2) {
- this.calendarSwiperDates = [next, prev, cur]
- }
- this.swiperByClick = false
- // 初始化日期信息完毕执行回调函数
- cb && cb()
- },
- // 生成展开的日历数据
- generateCalendar(year, month) {
- let calendarDate = []
- // 先获取缓存里面有没有该月的日期数据
- if (this.dateCache[`${year}-${month}`]) {
- calendarDate = deepClone(this.dateCache[`${year}-${month}`])
- } else { // 进行月份日期的计算
- const monthDates = new Date(year, month, 0).getDate() // 获取此月份总天数
- const normalWeek = ['一', '二', '三', '四', '五', '六', '日'] // 正常周数
- const monthFirstDay = normalWeek[new Date(year, month - 1, 0).getDay()] // 获取本月一号为星期几
- const monthFirstDayIndex = this.week.indexOf(monthFirstDay) // 计算本月一号在日历周数中的索引,索引之前的填充上个月的后几天
- // 本月一号在日历中不是第一个位置,需要进行填充
- if (monthFirstDayIndex !== 0) {
- const prevMonthDates = new Date(year, month - 1, 0).getDate() // 获取上一个月份的总天数
- // 填充本月一号之前的数据
- for (let i = 0; i < monthFirstDayIndex; i++) {
- const item = {
- year: month === 1 ? year - 1 : year,
- month: month === 1 ? 12 : month - 1,
- date: prevMonthDates - i,
- type: 'prev',
- isAttend: 0,//未打卡
- }
- this.theDateIsToday(item)
- calendarDate.unshift(item)
- }
- }
- // 循环生成当月所有日期
- for (let i = 1; i <= monthDates; i++) {
- let item = {
- year,
- month,
- date: i,
- isSelected: false,
- isToday: false,
- type: 'cur',
- isAttend: 0, //未打卡
- }
- // 今天的日期在不在里面
- this.theDateIsToday(item)
- calendarDate.push(item)
- }
- const residue = calendarDate.length % 7
- // 判断是否需要填充下个月的前几天
- if (residue !== 0) {
- for (let i = 1; i <= 7 - residue; i++) {
- const item = {
- year: month === 12 ? year + 1 : year,
- month: month === 12 ? 1 : month + 1,
- date: i,
- type: 'next',
- isAttend: 0, //未打卡
- }
- this.theDateIsToday(item)
- calendarDate.push(item)
- }
- }
- this.dateCache[`${year}-${month}`] = deepClone(calendarDate)
- }
- // 进行日期的默认选中
- if (year === this.getAssignDateInfo(false, 0) && month === this.getAssignDateInfo(false, 1)) {
- for (let i = 0, len = calendarDate.length; i < len; i++) {
- if (calendarDate[i].type === 'cur' && calendarDate[i].date === this.getAssignDateInfo(false, 2)) {
- calendarDate[i].isSelected = true
- this.dateActive = i
- break
- }
- }
- }
- //当月时间
- let currentMonth = (new Date).getMonth() + 1;
- //判断是否打卡和时间对比并修改数据
- let todayDate = Date.now() //毫秒数
- calendarDate.forEach(item => {
- if (this.records.find(x => x.month === item.month && x.year === item.year && x.date === item
- .date)) {
- item.isAttend = 1 //已打卡
- }
- //判断是否在当前日期之后
- let valueTime = (new Date(`${item.year}/${item.month}/${item.date}`)).getTime();
- if (valueTime > todayDate) {
- item.isAttend = -1 //时间未到
- }
- //判断是否为周末
- // if(){}
- })
- return calendarDate
- },
- // 判断日期是否为今天
- theDateIsToday(item) {
- if (item.year + '-' + item.month + '-' + item.date === this.getAssignDateInfo(true, 0) + '-' + this
- .getAssignDateInfo(true, 1) + '-' + this.getAssignDateInfo(true, 2)) {
- item.isToday = true
- }
- },
- // 展开日历轮播切换
- swiperChange(type) {
- // 通过点击上个月/下个月日期进行切换,不需要默认选中下个月的一号,直接选中点击的那个日期
- if (!this.swiperByClick && this.swiperMode === 'open') {
- this.getPrevOrNextDate(type)
- }
- setTimeout(() => { // 设置定时器是为了防止轮播切换时生成数据造成页面卡顿
- this.initCalendarSwiperDates(() => {
- this.swiperMode === 'close'
- }) // 初始化日历轮播日期信息
- }, this.swiperMode === 'open' ? this.duration : 0)
- },
- // 获取上一个月/下一个月的一号日期
- getPrevOrNextDate(type) {
- const year = this.getAssignDateInfo(false, 0)
- let month = this.getAssignDateInfo(false, 1)
- month = month + type
- // 判断切换月份时选中当前日期高亮还是一号,若选中当前日期高亮需进行大小判断
- const curActiveDate = this.getAssignDateInfo(false, 2)
- const maxDate = new Date(year, month, 0).getDate()
- const date = this.changeSetDefault ? 1 : curActiveDate > maxDate ? maxDate : curActiveDate
- this.selectedDate = parseTime(new Date(year, month - 1, date), '{y}-{m}-{d}')
- },
- // 获取上个星期/下一星期的开始日期
- getPrevOrNextStartDate(type) {
- const date = this.calendarSwiperShrinkDates[this.shrinkCurrent][0]
- this.selectedDate = parseTime(new Date(date.year, date.month - 1, date.date), '{y}-{m}-{d}')
- },
- // 前往某一天 格式 YYYY-MM | YYYY-MM-DD
- goToDate(date = this.today) {
- try {
- if (date.split('-').length < 2 || date.split('-').length > 3) throw '参数有误'
- if (date.split('-').length === 2) {
- date += '-01'
- }
- } catch (err) {
- throw Error('请检查参数是否符合规范')
- }
- this.selectedDate = date
- this.initCalendarSwiperDates(() => {
- })
- },
- // 日历轮播展开的情况下选择日期
- chooseDate(dateInfo, dateIndex) {
- // 重复点击后续不做处理
- if (dateInfo.isSelected) return false
- // 是否点击了上个月份的后几天或者点击了下个月份的前几天
- if (dateInfo.type !== 'cur') {
- if (dateInfo.type === 'prev') { // 点击了上个月份的后几天,滑到上个月
- this.current = this.current === 0 ? 2 : this.current - 1
- } else { // 点击了下个月份的前几天,滑到下个月
- this.current = this.current === 2 ? 0 : this.current + 1
- }
- // 将选中日期赋值为当前点击的那个日期
- this.selectedDate = parseTime(new Date(dateInfo.year, dateInfo.month - 1, dateInfo.date),
- '{y}-{m}-{d}')
- this.swiperByClick = true
- this.$emit('chooseDate', dateInfo, dateIndex)
- return false
- }
- // 将当前选中的日期清空并选中最新的日期
- this.calendarSwiperDates[this.current][this.dateActive].isSelected = false
- this.dateActive = dateIndex
- const date = this.calendarSwiperDates[this.current][this.dateActive]
- date.isSelected = true
- this.selectedDate = parseTime(new Date(date.year, date.month - 1, date.date), '{y}-{m}-{d}')
- this.dateClick = true
- this.$emit('chooseDate', dateInfo, dateIndex)
- },
- // 向父组件传递当前选中数据
- emitDate() {
- const {
- year,
- month,
- date
- } = this.calendarSwiperDates[this.current][this.dateActive]
- const e = {
- selectedDate: this.selectedDate,
- year,
- month,
- date
- }
- this.$emit('change', e)
- },
- }
- }
- </script>
- <style>
- .zsy_calendar {
- width: 100%;
- padding: 30rpx 10rpx 0rpx 10rpx;
- box-sizing: border-box;
- background-color: #fff;
- border-radius: 20rpx;
- }
- /* 日历顶部信息 */
- .calendar_info {
- display: flex;
- align-items: center;
- padding: 0 20rpx;
- }
- .calendar_info .title {
- font-size: 32rpx;
- font-weight: bold;
- color: $color-title;
- }
- .calendar_info .desc {
- margin-left: 29rpx;
- font-size: 28rpx;
- color: #959595;
- }
- .calendar_info .backToToday {
- margin-left: auto;
- font-size: 26rpx;
- }
- /* 日历顶部信息 */
- /* 日历周数 */
- .calendar_week {
- display: flex;
- align-items: center;
- justify-content: space-between;
- font-size: 26rpx;
- color: #959595;
- margin: 20rpx 0rpx;
- }
- .calendar_week .calendar_week__item {
- width: calc(100% / 7);
- text-align: center;
- }
- /* 日历周数 */
- /* 日历切换模式 */
- .calendar_toggle {
- position: relative;
- padding: 10rpx 0;
- margin: 10rpx 20rpx 0;
- display: flex;
- justify-content: center;
- }
- /* .calendar_toggle .icon {
- width: 30rpx;
- height: 30rpx;
- background-image: url('../../subpkg/zsy-calendar/icon/arrow.png');
- background-size: contain;
- background-repeat: no-repeat;
- margin: 0 auto;
- transition: all .3s;
- }
- .icon.down {
- transform: rotate(180deg);
- } */
- .calendar_toggle::before,
- .calendar_toggle::after {
- width: calc(50% - 30rpx);
- border-top: solid 2rpx #EAEAEA;
- content: '';
- display: block;
- position: absolute;
- top: 50%;
- transform: translateY(-50%);
- }
- .calendar_toggle::before {
- left: 0;
- }
- .calendar_toggle::after {
- right: 0;
- }
- /* 日历切换模式 */
- </style>
|