123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679 |
- <template>
- <view class="fu-calendar-wrap">
- <!-- 日历选择 start -->
- <view class="fu-calendar-data fu-flex fu-justify-center fu-align-center fu-margin fu-height-72" v-if="changeTitle">
- <block v-if="changeYear">
- <view class="fu-arrow fu-left fu-flex fu-justify-between fu-align-center" @click="prevYear">
- <text class="iconfont icon-changyongicon-" :style="[yaColor]"></text>
- </view>
- </block>
- <block v-if="changeMonth">
- <view class="fu-arrow single fu-left fu-flex fu-justify-between fu-align-center" @click="prevMonth">
- <text class="iconfont icon-jiantouyou" :style="[maColor]"></text>
- </view>
- </block>
- <view class="fu-title fu-text-32 fu-text-333">{{ currentDate[0] }}年{{ currentDate[1] }}月</view>
- <block v-if="changeMonth">
- <view class="fu-arrow single fu-right fu-flex fu-justify-between fu-align-center" @click="nextMonth">
- <text class="iconfont icon-jiantouyou" :style="[maColor]"></text>
- </view>
- </block>
- <block v-if="changeYear">
- <view class="fu-arrow fu-flex fu-right fu-justify-between fu-align-center" @click="nextYear">
- <text class="iconfont icon-changyongicon-" :style="[yaColor]"></text>
- </view>
- </block>
- </view>
- <!-- 日历选择 end -->
- <!-- 星期 start -->
- <view class="fu-week fu-flex fu-justify-between fu-align-center fu-text-333 fu-height-72">
- <view class="fu-week-item fu-text-center" v-for="item in weekData" :key="item">{{ item }}</view>
- </view>
- <!-- 星期 end -->
- <!-- 日历 start -->
- <view class="fu-calendar fu-flex fu-justify-between fu-align-center fu-flex-wrap fu-text-333">
- <!-- mode == date || mode == range -->
- <view
- class="fu-calendar-item fu-text-center"
- @click="changeCalendar(item, index)"
- :class="{ isNot: item.isNot, isFuture: item.isFuture && !item.isNot }"
- v-for="(item, index) in calendar"
- :key="index"
- >
- <view
- class="fu-calendar-num fu-flex fu-justify-center fu-align-center"
- :class="{ signin: mode == 'signin', range: mode == 'range', start: item.start, end: item.end }"
- :style="[(item.dayActive || item.start || item.end) && !item.isNot ? dayBg : {}, item.active && !item.isNot ? activeBg : {}]"
- >
- {{ item.day }}
- <view class="fu-calendar-text" v-show="item.start">{{ startText }}</view>
- <view class="fu-calendar-text" v-show="item.end">{{ endText }}</view>
- </view>
- </view>
- </view>
- <!-- 日历 end -->
- </view>
- </template>
- <script>
- /**
- * @author 邓东方
- */
- export default {
- props: {
- // 月份切换按钮箭头颜色
- 'month-arrow-color':{
- type: String,
- default: '#666666'
- },
- // 年份切换按钮箭头颜色
- 'year-arrow-color':{
- type: String,
- default: '#999999'
- },
- // 是否显示顶部年月
- 'change-title': {
- type: Boolean,
- default: true
- },
- // 是否显示顶部的切换年份方向的按钮
- 'change-year': {
- type: Boolean,
- default: true
- },
- // 是否显示顶部的切换月份方向的按钮
- 'change-month': {
- type: Boolean,
- default: true
- },
- // 日期模式 date 单个日期选择模式 range 日期段选择模式 signin 签到日期展示模式
- mode: {
- type: String,
- default: 'date'
- },
- // 未来日期是否可选择 默认不可选择
- future: {
- type: Boolean,
- default: false
- },
- // 日期段回显时 若只传入一个日期则不显示
- //日期段选择时默认显示开始时间
- 'start-time': {
- type: String,
- default: ''
- },
- //日期段选择时默认显示结束时间
- 'end-time': {
- type: String,
- default: ''
- },
- // 起始日期底部的提示文字
- 'start-text': {
- type: String,
- default: '开始'
- },
- // 结束日期底部的提示文字
- 'end-text': {
- type: String,
- default: '结束'
- },
- // 选择日期开始结束当天背景色
- 'active-bg-color': {
- type: String,
- default: 'rgba(41,121,255,1)'
- },
- // 选择日期中间的背景色
- 'range-bg-color': {
- type: String,
- default: 'rgba(41,121,255,0.13)'
- },
- // 当前日期是否高亮
- isDefaultDay: {
- type: Boolean,
- default: true
- },
- // 单个日期选择默认选中时间 xxxx-yy-dd
- currenTime: {
- type: String,
- default: ''
- },
- // 签到模式数据 [xxxx-yy-dd]
- signinData: {
- type: Array,
- default: function() {
- return [];
- }
- }
- },
- data() {
- return {
- weekData: ['日', '一', '二', '三', '四', '五', '六'], //星期
- calendar: [], //日历数组
- currentDate: [], //当前日期
- currentDay: '', //当天时间
- start: '', //开始时间
- end: '', //结束时间
- isEmit: true //mode == range是否推送
- };
- },
- computed: {
- // 背景色
- dayBg() {
- return { background: this.activeBgColor, color: '#ffffff' };
- },
- // 时间段背景色
- activeBg() {
- return { background: this.rangeBgColor, color: '#ffffff' };
- },
- // 年份箭头颜色
- yaColor(){
- return { color: this.yearArrowColor };
- },
- // 月份箭头颜色
- maColor(){
- return { color: this.monthArrowColor };
- }
- },
- watch: {
- signinData: {
- deep: true,
- handler(newVal, oldVal) {
- if (mode == 'signin') {
- this.singinFun(this.calendar, newVal);
- }
- }
- }
- },
- mounted() {
- let currentDate = this.currentime();
- let y = currentDate[0];
- let m = currentDate[1] > 9 ? currentDate[1] : '0' + currentDate[1];
- let d = currentDate[2] > 9 ? currentDate[2] : '0' + currentDate[2];
- this.currentDate = currentDate;
- // mode == 'date' 在传入单个时间时 currentDay不再显示默认的当前时间
- if (this.mode == 'date') {
- if (this.currenTime) {
- this.currentDay = this.currenTime;
- } else {
- if (this.isDefaultDay) {
- this.currentDay = `${y}-${m}-${d}`;
- }
- }
- }
- // mode == 'date' isDefaultDay == true时 当前时间才显示
- if (this.mode == 'range') {
- if (this.isDefaultDay) {
- this.currentDay = `${y}-${m}-${d}`;
- }
- }
- // 日历赋值
- this.calendar = this.getCalendar(currentDate[0], currentDate[1], currentDate[2]);
- },
- methods: {
- /**
- * @description 获取当前时间函数
- */
- currentime() {
- var date = new Date();
- var y = Number(date.getFullYear());
- var m = Number(date.getMonth() + 1);
- var d = Number(date.getDate());
- return [y, m, d];
- },
- isLeapYear(y){
- const cond1 = y % 4 == 0; //条件1:年份必须要能被4整除
- const cond2 = y % 100 != 0; //条件2:年份不能是整百数
- const cond3 = y % 400 == 0; //条件3:年份是400的倍数
- //当条件1和条件2同时成立时,就肯定是闰年,所以条件1和条件2之间为“与”的关系。
- //如果条件1和条件2不能同时成立,但如果条件3能成立,则仍然是闰年。所以条件3与前2项为“或”的关系。
- //所以得出判断闰年的表达式:
- return (cond1 && cond2) || cond3;
- },
- /**
- * @description 获取日历
- * @param {String,Number} y 年
- * @param {String,Number} m 月
- */
- getCalendar(y, m) {
- // 求解cy年cm月cd日是星期几,parseInt代表取整 d=1是去每个月第一天
- var cc = parseInt(y / 100); //c
- var cy = y - cc * 100; //y
- var cm = m; //m
- var cd = 1; //d
- // 某年的1、2月要看作上一年的13、14月来计算,比如2003年1月1日要看作2002年的13月1日来计算
- if (m == 1 || m == 2) {
- cc = parseInt((y - 1) / 100);
- cy = y - 1 - cc * 100;
- cm = 12 + m;
- }
- //w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
- // var csum = y + [y / 4] + [c / 4] - 2c+[26(m + 1)/10]+d-1;
- var csum = cy + parseInt(cy / 4) + parseInt(cc / 4) - 2 * cc + parseInt((26 * (cm + 1)) / 10) + cd - 1;
- // 注意使用蔡勒公式时出现负数情况 fd 每月第一天星期几
- if (csum < 0) {
- var fd = parseInt(((csum % 7) + 7) % 7);
- } else {
- var fd = parseInt(csum % 7);
- }
- // 上个月天数
- var cond1 = y % 4 == 0; //条件1:年份必须要能被4整除
- var cond2 = y % 100 != 0; //条件2:年份不能是整百数
- var cond3 = y % 400 == 0; //条件3:年份是400的倍数
- //当条件1和条件2同时成立时,就肯定是闰年,所以条件1和条件2之间为“与”的关系。
- //如果条件1和条件2不能同时成立,但如果条件3能成立,则仍然是闰年。所以条件3与前2项为“或”的关系。
- //所以得出判断闰年的表达式:
- var cond = this.isLeapYear(y);
- //判断当月有多少天
- var allDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m - 1];
- if (cond && m == 2) {
- allDays = 29;
- }
- //上个月是不是去年
- let prevYear = y;
- let prevMonth = m - 1;
- if (m == 1) {
- prevYear = y - 1;
- prevMonth = 12;
- }
- let _prevMonth = prevMonth > 9 ? prevMonth : '0' + prevMonth;
- let _m = m > 9 ? m : '0' + m;
- //判断上个月天数
- var prevDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][prevMonth - 1];
- if(prevMonth === 2 && cond) prevDays = 29;
- var calendar = [];
- //这里塞入上个月末尾日期
- //day 日 active 是否选择 isNot 是不是这个月 formData 日期格式 isBg 是否加背景色(日期段筛选时使用)
- for (let i = 1; i <= fd; i++) {
- let prevDay = prevDays - fd + i;
- let _prevDay = prevDay > 9 ? prevDay : '0' + prevDay;
- calendar.push({
- day: prevDay,
- active: false,
- isNot: true,
- formData: prevYear + '-' + _prevMonth + '-' + _prevDay
- });
- }
- //这里塞入正常这个月的日期
- let week = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
- for (let i = 1; i <= allDays; i++) {
- let _i = i > 9 ? i : '0' + i;
- calendar.push({
- day: i,
- active: false,
- isNot: false,
- formData: y + '-' + _m + '-' + _i,
- week: week[((i + fd - 1) % 7) % 7],
- year: y,
- month: m,
- allDays: allDays
- });
- }
- //下个月是不是下一年
- let nextYear = y;
- let nextMonth = m + 1;
- if (m == 12) {
- nextYear = y + 1;
- nextMonth = 1;
- }
- let _nextMonth = nextMonth > 9 ? nextMonth : '0' + nextMonth;
- //判断数组最后一排剩余几个位置塞入下个月日期
- let takedie = calendar.length % 7;
- if (7 - takedie > 0 && 7 - takedie < 7) {
- for (let i = 1; i <= 7 - takedie; i++) {
- let _i = i > 9 ? i : '0' + i;
- calendar.push({
- day: i,
- active: false,
- isNot: true,
- formData: nextYear + '-' + _nextMonth + '-' + _i
- });
- }
- }
- // 默认当天时间加背景色
- if (this.mode == 'date') {
- // 在range模式时 start end不存在时走这个
- if (this.isDefaultDay || this.currentDay) {
- this.currentDayFun(calendar, this.currentDay, true);
- }
- }
- if (this.mode == 'range') {
- // 在range模式时 start end不存在时走这个
- if (this.isDefaultDay || this.currentDay) {
- if (!this.startTime || !this.endTime) {
- this.currentDayFun(calendar, this.currentDay, true);
- }
- }
- // 当开始时间与结束时间存在时
- if (this.startTime && this.endTime && this.startTime != this.endTime) {
- // 当没有选择过时 进行赋值操作 若是已经点击过 start end存在值 就不需要赋值了
- if (!this.start && !this.end) {
- this.start = this.startTime;
- this.end = this.endTime;
- // 判断开始结束值存入本地
- if (!uni.getStorageSync('startItem')) {
- let startItem = calendar.find(item => item.formData == this.start);
- uni.setStorageSync('startItem', JSON.stringify(startItem));
- }
- if (!uni.getStorageSync('endItem')) {
- let endItem = calendar.find(item => item.formData == this.end);
- uni.setStorageSync('endItem', JSON.stringify(endItem));
- }
- }
- }
- this.rangeDayFun(calendar,true);
- }
- if (this.mode == 'signin') {
- this.singinFun(calendar, this.signinData);
- }
- // 判断未来日期是否可选择
- if (!this.future) {
- let nowData = new Date().getTime();
- calendar.forEach(val => {
- val.isFuture = false;
- if (new Date(val.formData).getTime() > nowData) {
- val.isFuture = true;
- }
- });
- }
- return calendar;
- },
- /**
- * @description 高亮日期 签到模式
- */
- singinFun(calendar, data) {
- calendar.forEach(a => {
- a.dayActive = false;
- data.forEach(b => {
- if (a.formData == b.date) {
- a.dayActive = true;
- }
- });
- });
- this.calendar = calendar;
- this.$forceUpdate();
- },
- /**
- * @description 单个日期是否高亮
- * @param {Array} calendar 日历数组
- * @param {String} day 选择的日期
- * @param {Boolen} isInit 初始化触发change时间参数
- */
- currentDayFun(calendar, day,isInit = false) {
- let that = this;
- calendar.forEach(item => {
- item.dayActive = false;
- if (Math.floor(new Date(item.formData).getTime() / 1000) == Math.floor(new Date(day).getTime() / 1000)) {
- item.dayActive = true;
- if (this.mode == 'date') {
- that.$emit('change', { day: item.day, time: item.formData, week: item.week, year: item.year, month: item.month, allDays: item.allDays,isInit:isInit });
- }
- }
- });
- this.calendar = calendar;
- this.$forceUpdate();
- },
- /**
- * @description 日期范围选择
- */
- rangeDayFun(calendar,isInit = false) {
- // 判断 start end 和中间的时间
- calendar.forEach(val => {
- val.start = false;
- val.end = false;
- val.active = false;
- val.dayActive = false;
- // 开始时间
- if (new Date(val.formData).getTime() == new Date(this.start).getTime()) {
- val.start = true;
- }
- // 结束时间
- if (new Date(val.formData).getTime() == new Date(this.end).getTime()) {
- val.end = true;
- }
- // 当开始结束时间选择完毕 开始时间大于结束时间时进行翻转
- if (this.start && this.end && new Date(this.start).getTime() > new Date(this.end).getTime()) {
- let start = this.start;
- this.start = this.end;
- this.end = start;
- }
- // 开始结束中间时间段
- if (new Date(val.formData).getTime() > new Date(this.start).getTime() && new Date(val.formData).getTime() < new Date(this.end).getTime()) {
- val.active = true;
- }
- });
- if (this.start && this.end && this.isEmit) {
- let startItem = JSON.parse(uni.getStorageSync('startItem'));
- let endItem = JSON.parse(uni.getStorageSync('endItem'));
- this.$emit('change', {
- start: this.start,
- startYear: startItem.year,
- startWeek: startItem.week,
- startMonth: startItem.month,
- startDay: startItem.day,
- end: this.end,
- endYear: endItem.year,
- endWeek: endItem.week,
- endMonth: endItem.month,
- endDay: endItem.day,
- isInit:isInit
- });
- this.isEmit = false;
- }
- this.calendar = calendar;
- this.$forceUpdate();
- },
- /**
- * @description 点击日期
- * @param {Object} item 入参 所点击日期信息
- */
- changeCalendar(item, index) {
- // 签到模式纯展示模式
- if (this.mode == 'signin') return;
- // 未来日期 上下月日期不可选择
- if (item.isFuture || item.isNot) return;
- if (this.mode == 'date') {
- if(this.currentDay == item.formData) return;
- // 选择日期记录
- this.currentDay = item.formData;
- this.currentDayFun(this.calendar, item.formData);
- }
- if (this.mode == 'range') {
- // 当已经选择了开始结束时间时 再次点击时先清空之前的时间
- if (this.start && this.end) {
- this.start = '';
- this.end = '';
- this.isEmit = true;
- uni.removeStorageSync('startItem');
- uni.removeStorageSync('endItem');
- }
- // 默认先赋值开始时间
- if (!this.start && !this.end) {
- this.start = item.formData;
- uni.setStorageSync('startItem', JSON.stringify(item));
- } else if (this.start && !this.end) {
- // 当第二次选择时间和第一次相同时 不再触发赋值
- if (this.start == item.formData) return;
- this.end = item.formData;
- uni.setStorageSync('endItem', JSON.stringify(item));
- }
- this.rangeDayFun(this.calendar);
- }
- },
- /**
- * @description 上年今月
- */
- prevYear() {
- let currentDate = this.currentDate;
- currentDate = [currentDate[0] - 1, currentDate[1]];
- this.currentDate = currentDate;
- this.calendar = this.getCalendar(currentDate[0], currentDate[1]);
- },
- /**
- * @description 下年今月
- */
- nextYear() {
- let currentDate = this.currentDate;
- currentDate = [Number(currentDate[0]) + 1, Number(currentDate[1])];
- this.currentDate = currentDate;
- this.calendar = this.getCalendar(currentDate[0], currentDate[1]);
- },
- /**
- * @description 上月
- */
- prevMonth() {
- let currentDate = this.currentDate;
- if (currentDate[1] - 1 == 0) {
- currentDate = [currentDate[0] - 1, 12];
- } else {
- currentDate = [currentDate[0], currentDate[1] - 1];
- }
- this.currentDate = currentDate;
- this.calendar = this.getCalendar(currentDate[0], currentDate[1]);
- },
- /**
- * @description 下月
- */
- nextMonth() {
- let currentDate = this.currentDate;
- if (currentDate[1] == 12) {
- currentDate = [Number(currentDate[0]) + 1, 1];
- } else {
- currentDate = [Number(currentDate[0]), Number(currentDate[1]) + 1];
- }
- this.currentDate = currentDate;
- this.calendar = this.getCalendar(currentDate[0], currentDate[1]);
- }
- }
- };
- </script>
- <style scoped lang="scss">
- @import '@/static/font/iconfont.css';
- .fu-flex {
- display: flex;
- }
- .fu-justify-center {
- justify-content: center;
- }
- .fu-justify-between {
- justify-content: space-between;
- }
- .fu-align-center {
- align-items: center;
- }
- .fu-text-32 {
- font-size: 32rpx;
- }
- .fu-text-333 {
- color: #333333;
- }
- .fu-text-center {
- text-align: center;
- }
- .fu-margin {
- margin: 10rpx 0;
- }
- .fu-height-72 {
- height: 72rpx;
- line-height: 72rpx;
- }
- .fu-flex-wrap {
- flex-wrap: wrap;
- }
- // 日历选择 start
- .fu-calendar-data {
- .fu-title{
- padding: 0 10rpx;
- }
- .fu-arrow {
- height: 100%;
- // padding: 0 10rpx;
- text{
- font-size: 36rpx;
- }
- &.fu-left{
- transform: rotate(180deg);
- padding-left: 10rpx;
- }
- &.fu-right{
- padding-left: 10rpx;
- }
- &.single {
- text{
- font-size: 32rpx;
- }
- }
- }
- }
- // 日历选择 end
- // 星期 start
- .fu-week {
- .fu-week-item {
- width: calc(100% / 7);
- }
- }
- // 星期 end
- // 日历 start
- .fu-calendar {
- .fu-calendar-item {
- width: calc(100% / 7);
- padding-bottom: calc(100% / 7);
- position: relative;
- overflow: hidden;
- .fu-calendar-num {
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- &.start {
- border-radius: 16rpx 0 0 16rpx;
- }
- &.end {
- border-radius: 0 16rpx 16rpx 0;
- }
- .fu-calendar-text {
- position: absolute;
- bottom: 4rpx;
- left: 0;
- width: 100%;
- font-size: 20rpx;
- color: #ffffff;
- text-align: center;
- }
- &.range {
- position: absolute;
- width: 100%;
- height: 90%;
- top: 50%;
- transform: translateY(-50%);
- }
- &.signin {
- position: absolute;
- width: 60%;
- height: 60%;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
- border-radius: 50%;
- }
- }
- &.isNot {
- color: #eee;
- }
- &.isFuture {
- color: #999;
- }
- }
- }
- // 日历 end
- </style>
|