vue仿钉钉的考勤日历(基于vue-calendar-component组件)

直接上效果图,需要再往下看
vue仿钉钉的考勤日历(基于vue-calendar-component组件)_第1张图片
GitHub地址:vue-calendar-component
由于需要对此组件的样式及功能的扩展,直接复制代码过来修改,开始贴代码,很长很长 慢慢看

checkCalendar.vue(子组件)

<style lang="scss" rel="stylesheet/scss">
    @media screen and (min-width: 460px) {
        .wh_item_date:hover {
            background: #71c7a5;
            cursor: pointer;
        }
    }

    * {
        margin: 0;
        padding: 0;
    }

    .wh_container {
        max-width: 410px;
        margin: auto;
    }

    li {
        list-style-type: none;
    }

    .wh_top_title {
        display: flex;
    }

    .wh_top_title li {
        cursor: pointer;
        display: flex;
        color: #fff;
        font-size: 18px;
        flex: 1;
        justify-content: center;
        align-items: center;
        height: 47px;
    }

    .wh_top_title .wh_content_li {
        cursor: auto;
        flex: 2.5;
        color: black;
    }

    .wh_content_all {
        font-family: -apple-system, BlinkMacSystemFont, "PingFang SC",
        "Helvetica Neue", STHeiti, "Microsoft Yahei", Tahoma, Simsun, sans-serif;
        background-color: white;
        width: 100%;
        overflow: hidden;
        padding-bottom: 8px;
    }

    .wh_content {
        display: flex;
        flex-wrap: wrap;
        padding: 0 3% 0 3%;
        width: 100%;
    }

    .wh_content:first-child .wh_content_item_tag,
    .wh_content:first-child .wh_content_item {
        color: #ddd;
        font-size: 16px;
    }

    .wh_content_item,
    wh_content_item_tag {
        font-size: 15px;
        width: 13.4%;
        text-align: center;
        color: #fff;
        position: relative;
    }

    .wh_content_item {
        height: 40px;
    }

    .wh_top_tag {
        width: 40px;
        height: 40px;
        line-height: 40px;
        margin: auto;
        display: flex;
        justify-content: center;
        align-items: center;
        color: black;
    }

    .wh_item_date {
        width: 30px;
        height: 30px;
        line-height: 30px;
        margin: auto;
        display: flex;
        justify-content: center;
        align-items: center;
        color: black;

        .smallDot {
            background-color: #f99341;
            width: 5px;
            height: 5px;
            border-radius: 50%;
            text-align: center;
            margin-left: 13px;
        }

        .smallDot1 {
            background-color: #1989fa;
            width: 5px;
            height: 5px;
            border-radius: 50%;
            text-align: center;
            margin-left: 13px;
        }
    }

    .wh_left {
        width: 12px;
        height: 12px;
        border-top: 2px solid #ffffff;
        border-left: 2px solid #ffffff;
        transform: rotate(-45deg);
        border-color: black;
    }

    .wh_left:active,
    .wh_right:active {
        border-color: #ddd;
    }

    .wh_right {
        width: 12px;
        height: 12px;
        border-top: 2px solid #ffffff;
        border-right: 2px solid #ffffff;
        transform: rotate(45deg);
        border-color: black;
    }

    .wh_content_item > .wh_isMark {
        margin: auto;
        border-radius: 50%;
        background: blue;
        z-index: 2;
    }

    .wh_content_item .wh_other_dayHide {
        color: #bfbfbf;
    }

    .wh_content_item .wh_want_dayHide {
        color: #bfbfbf;
    }

    .wh_content_item .wh_isToday {
        background: #77adfa;
        border-radius: 50%;
    }

    .wh_content_item .wh_chose_day {
        background: #1989fa;
        border-radius: 50%;
    }
</style>

<template>
    <section class="wh_container">
        <div class="wh_content_all">
            <div class="wh_top_title">
                <li @click="PreMonth(myDate,false)">
                    <div class="wh_left"></div>
                </li>
                <li class="wh_content_li">{{dateTop}}</li>
                <li @click="NextMonth(myDate,false)">
                    <div class="wh_right"></div>
                </li>
            </div>
            <div class="wh_content">
                <div class="wh_content_item" v-for="tag in textTop">
                    <div class="wh_top_tag">{{tag}}</div>
                </div>
            </div>
            <div class="wh_content">
                <div class="wh_content_item" v-for="(item,index) in list" @click="clickDay(item,index)">
                    <div class="wh_item_date" style="display: block"
                         v-bind:class="[{ wh_isMark: item.isMark},{wh_other_dayHide:item.otherMonth!=='nowMonth'},{wh_want_dayHide:item.dayHide},{wh_isToday:item.isToday},{wh_chose_day:item.chooseDay},setClass(item)]">
                        {{item.id}}
                        <!--这里是控制异常、正常的那个小圆点-->
                        <span v-for="(date,index) in dateList" :key="index">
                            <span v-if="date.offDutyTime&&date.onDutyTime&&formatDate(date.recordDate)==item.date&&(isLate(date.serverEndTime,date.offDutyTime)||isLate(date.onDutyTime,date.serverStartTime))">
                                    <div class="smallDot"></div>
                            </span>
                            <span v-if="date.offDutyTime&&date.onDutyTime&&formatDate(date.recordDate)==item.date&&!isLate(date.serverEndTime,date.offDutyTime)&&!isLate(date.onDutyTime,date.serverStartTime)">
                                <div class="smallDot1"></div>
                            </span>
                        </span>
                    </div>
                </div>
            </div>
        </div>
    </section>
</template>

<script>
    import timeUtil from "./calendar";

    export default {
        data() {
            return {
                myDate: [],
                list: [],
                historyChose: [],
                dateTop: "",
                loginNannyUser: {},
                loginGzhUser: {},
                dateList: []
            };
        },
        props: {
            markDate: {
                type: Array,
                default: () => []
            },
            markDateMore: {
                type: Array,
                default: () => []
            },
            textTop: {
                type: Array,
                default: () => ["一", "二", "三", "四", "五", "六", "日"]
            },
            sundayStart: {
                type: Boolean,
                default: () => false
            },
            agoDayHide: {
                type: String,
                default: `0`
            },
            futureDayHide: {
                type: String,
                default: `2554387200`
            },
            joinWishId: {
                default: null
            }
        },
        created() {
            let $this = this;
            this.getLoginAllUser("nanny", {}, function () {//这里只是我这边的业务,删除就行

            });
            this.intStart();//初始化数据
            this.myDate = new Date();
        },
        methods: {
            isLate(str, str1) {//判断两个时分秒大小
                return new Date((this.formatDates(new Date()) + " " + str).replace(/-/g, '/')) > new Date((this.formatDates(new Date()) + " " + str1).replace(/-/g, '/'));
            },
            formatDate(date) {
                date = typeof date === 'string' ? new Date(date.replace(/\-/g, '/')) : date;
                return date.getFullYear() + '/' + (date.getMonth() + 1) + '/'
                    + date.getDate();
            },
            intStart() {
                timeUtil.sundayStart = this.sundayStart;
            },
            setClass(data) {
                let obj = {};
                obj[data.markClassName] = data.markClassName;
                return obj;
            },
            clickDay(item, index) {
                if (item.otherMonth === "nowMonth" && !item.dayHide) {
                    this.getList(this.myDate, item.date);
                }
                if (item.otherMonth !== "nowMonth") {
                    item.otherMonth === "preMonth"
                        ? this.PreMonth(item.date)
                        : this.NextMonth(item.date);
                }
            },
            ChoseMonth(date, isSelectedDay = true) {
                date = timeUtil.dateFormat(date);
                this.myDate = new Date(date);
                this.$emit("changeMonth", timeUtil.dateFormat(this.myDate));
                if (isSelectedDay) {
                    this.getList(this.myDate, date, isSelectedDay);
                } else {
                    this.getList(this.myDate);
                }
            },
            PreMonth(date, isSelectedDay = true) {
                date = timeUtil.dateFormat(date);
                this.myDate = timeUtil.getOtherMonth(this.myDate, "preMonth");
                this.$emit("changeMonth", timeUtil.dateFormat(this.myDate));
                this.axiosPost("/nannyCheckIn/findMonthList.n", {date: this.myDate,joinWishListId: this.joinWishId,}, function (resData) {
                    this.dateList = resData.list;
                });
                if (isSelectedDay) {
                    this.getList(this.myDate, date, isSelectedDay);
                } else {
                    this.getList(this.myDate);
                }
            },
            NextMonth(date, isSelectedDay = true) {
                date = timeUtil.dateFormat(date);
                this.myDate = timeUtil.getOtherMonth(this.myDate, "nextMonth");
                this.$emit("changeMonth", timeUtil.dateFormat(this.myDate));
                this.axiosPost("/nannyCheckIn/findMonthList.n", {date: this.myDate,joinWishListId: this.joinWishId,}, function (resData) {
                    this.dateList = resData.list;
                });
                if (isSelectedDay) {
                    this.getList(this.myDate, date, isSelectedDay);
                } else {
                    this.getList(this.myDate);
                }
            },
            forMatArgs() {
                let markDate = this.markDate;
                let markDateMore = this.markDateMore;
                markDate = markDate.map(k => {
                    return timeUtil.dateFormat(k);
                });
                markDateMore = markDateMore.map(k => {
                    k.date = timeUtil.dateFormat(k.date);
                    return k;
                });
                return [markDate, markDateMore];
            },
            getList(date, chooseDay, isSelectedDay = true) {
                const [markDate, markDateMore] = this.forMatArgs();
                this.dateTop = `${date.getFullYear()}${date.getMonth() + 1}月`;

                let arr = timeUtil.getMonthList(this.myDate);
                for (let i = 0; i < arr.length; i++) {
                    let markClassName = "";
                    let k = arr[i];
                    k.chooseDay = false;
                    const nowTime = k.date;
                    const t = new Date(nowTime).getTime() / 1000;
                    //看每一天的class
                    for (const c of markDateMore) {
                        if (c.date === nowTime) {
                            markClassName = c.className || "";
                        }
                    }
                    //标记选中某些天 设置class
                    k.markClassName = markClassName;
                    k.isMark = markDate.indexOf(nowTime) > -1;
                    //无法选中某天
                    k.dayHide = t < this.agoDayHide || t > this.futureDayHide;
                    if (k.isToday) {
                        this.$emit("isToday", nowTime);
                    }
                    let flag = !k.dayHide && k.otherMonth === "nowMonth";
                    if (chooseDay && chooseDay === nowTime && flag) {
                        this.$emit("choseDay", nowTime);
                        this.historyChose.push(nowTime);
                        k.chooseDay = true;
                    } else if (
                        this.historyChose[this.historyChose.length - 1] === nowTime &&
                        !chooseDay &&
                        flag
                    ) {
                        k.chooseDay = true;
                    }
                }
                this.list = arr;
            }
        },
        mounted() {
            this.getList(this.myDate);
            this.axiosPost("/nannyCheckIn/findMonthList.n", {//业务,根据自己需要修改
                joinWishListId: this.joinWishId,
            }, function (resData) {
                this.dateList = resData.list;
            });
        },
        watch: {
            markDate: {
                handler(val, oldVal) {
                    this.getList(this.myDate);
                },
                deep: true
            },
            markDateMore: {
                handler(val, oldVal) {
                    this.getList(this.myDate);
                },
                deep: true
            },
            agoDayHide: {
                handler(val, oldVal) {
                    this.getList(this.myDate);
                },
                deep: true
            },
            futureDayHide: {
                handler(val, oldVal) {
                    this.getList(this.myDate);
                },
                deep: true
            },
            sundayStart: {
                handler(val, oldVal) {
                    this.intStart();
                    this.getList(this.myDate);
                },
                deep: true
            },
            joinWishId: {//监听这个是因为要切换工单,换数据
                handler(val, oldVal) {
                    this.axiosPost("/nannyCheckIn/findMonthList.n", {
                        joinWishListId: val,
                    }, function (resData) {
                        this.dateList = resData.list;
                    });
                },
                deep: true
            }
        }
    };
</script>

calendar.js(日期工具类)

export default {
  // 当某月的天数
  getDaysInOneMonth(date) {
    const year = date.getFullYear();
    const month = date.getMonth() + 1;
    const d = new Date(year, month, 0);
    return d.getDate();
  },
  // 向前空几个
  getMonthweek(date) {
    const year = date.getFullYear();
    const month = date.getMonth() + 1;
    const dateFirstOne = new Date(year + '/' + month + '/1');
    return this.sundayStart ?
      dateFirstOne.getDay() == 0 ? 7 : dateFirstOne.getDay() :
      dateFirstOne.getDay() == 0 ? 6 : dateFirstOne.getDay() - 1;
  },
  /**
   * 获取当前日期上个月或者下个月
   */
  getOtherMonth(date, str = 'nextMonth') {
    const timeArray = this.dateFormat(date).split('/');
    const year = timeArray[0];
    const month = timeArray[1];
    const day = timeArray[2];
    let year2 = year;
    let month2;
    if (str === 'nextMonth') {
      month2 = parseInt(month) + 1;
      if (month2 == 13) {
        year2 = parseInt(year2) + 1;
        month2 = 1;
      }
    } else {
      month2 = parseInt(month) - 1;
      if (month2 == 0) {
        year2 = parseInt(year2) - 1;
        month2 = 12;
      }
    }
    let day2 = day;
    const days2 = new Date(year2, month2, 0).getDate();
    if (day2 > days2) {
      day2 = days2;
    }
    if (month2 < 10) {
      month2 = '0' + month2;
    }
    if (day2 < 10) {
      day2 = '0' + day2;
    }
    const t2 = year2 + '/' + month2 + '/' + day2;
    return new Date(t2);
  },
  // 上个月末尾的一些日期
  getLeftArr(date) {
    const arr = [];
    const leftNum = this.getMonthweek(date);
    const num = this.getDaysInOneMonth(this.getOtherMonth(date, 'preMonth')) - leftNum + 1;
    const preDate = this.getOtherMonth(date, 'preMonth');
    // 上个月多少开始
    for (let i = 0; i < leftNum; i++) {
      const nowTime = preDate.getFullYear() + '/' + (preDate.getMonth() + 1) + '/' + (num + i);
      arr.push({
        id: num + i,
        date: nowTime,
        isToday: false,
        otherMonth: 'preMonth',
      });
    }
    return arr;
  },
  // 下个月末尾的一些日期
  getRightArr(date) {
    const arr = [];
    const nextDate = this.getOtherMonth(date, 'nextMonth');
    const leftLength = this.getDaysInOneMonth(date) + this.getMonthweek(date);
    const _length = 7 - leftLength % 7;
    for (let i = 0; i < _length; i++) {
      const nowTime = nextDate.getFullYear() + '/' + (nextDate.getMonth() + 1) + '/' + (i + 1);
      arr.push({
        id: i + 1,
        date: nowTime,
        isToday: false,
        otherMonth: 'nextMonth',
      });
    }
    return arr;
  },
  // format日期
  dateFormat(date) {
    date = typeof date === 'string' ? new Date(date.replace(/\-/g, '/')) : date;
    return date.getFullYear() + '/' + (date.getMonth() + 1) + '/'
      + date.getDate();
  },
  // 获取某月的列表不包括上月和下月
  getMonthListNoOther(date) {
    const arr = [];
    const num = this.getDaysInOneMonth(date);
    const year = date.getFullYear();
    const month = date.getMonth() + 1;
    const toDay = this.dateFormat(new Date());

    for (let i = 0; i < num; i++) {
      const nowTime = year + '/' + month + '/' + (i + 1);
      arr.push({
        id: i + 1,
        date: nowTime,
        isToday: toDay === nowTime,
        otherMonth: 'nowMonth',
      });
    }
    return arr;
  },
  // 获取某月的列表 用于渲染
  getMonthList(date) {
    return [ ...this.getLeftArr(date), ...this.getMonthListNoOther(date), ...this.getRightArr(date) ];
  },
  // 默认是周一开始
  sundayStart: false,
};

然后引入到组件中

import nCalendar from './checkCalendar'

export default (Vue) => {
    Vue.component("nCalendar", nCalendar);
}

然后应用在页面中

<style lang="scss" rel="stylesheet/scss">
    .nCalender {

        .detailDiv {
            margin: 20px 0;

            .imgDiv {
                img {
                    width: 60px;
                    height: 60px;
                }
            }

            .hourDiv {
                background-color: white;
                padding-top: 10px;

                .clockStyle {
                    font-size: 16px;
                    color: #4b90ed;
                }

                .hourText {
                    font-size: 14px;
                    margin-left: 5px;
                }
            }

            .stepDiv {
                .tagDiv {
                    margin-top: 10px;
                }
            }
        }
    }
</style>
<template>
    <div class="nCalender">
        <navBar
                :showLeft="true"
                :borderLine=true
                background="#f2f2f2"
                title="考勤日历">
            <div @click="$router.push('/h5nAddCard')" slot="right">补卡</div>
        </navBar>
        <van-field label="选择工单" v-if="list.length>1"
                   :value="obj1!=null&&obj1.joinWishId!=null?obj1.joinWishId:null">
            <selectJoinTemp slot="input" name="joinWishId" name1="auditUserId"
                            v-model="obj1" :isDefault="true"/>
        </van-field>
        <!--日历-->
        <div class="CalendarDiv" v-if="obj1&&obj1.joinWishId&&obj1.joinWishId>0">
            <nCalendar
                    :joinWishId="obj1.joinWishId"
                    v-on:choseDay="clickDay"
                    v-on:changeMonth="changeDate"/>
        </div>

        <div v-if="list.length==0" class="detailDiv" style="text-align: center;color: #bfbfbf">
            <div class="imgDiv">
                <img src="../img/rest.png"/>
            </div>
            <div style="margin-top: 2%">
                无合同
            </div>
        </div>
        <!--当日详情-->
        <span></span>
        <div class="detailDiv"
             v-if="obj!=null&&obj.id>0&&obj.recordDate&&new Date(obj.recordDate.replace(/-/g,'/'))>
            <div class="hourDiv">
                <van-row>
                    <van-col offset="1">
                        <van-icon name="clock" class="clockStyle"/>
                    </van-col>
                    <van-col class="hourText">工时共计:<span v-if="obj.totalHour!=null">{{obj.totalHour}}小时</span>
                        <span v-else>暂无</span></van-col>
                </van-row>
            </div>
            <div class="stepDiv">
                <van-steps direction="vertical" :active="-1">
                    <van-step>
                        <div>
                            签到时间&nbsp;
                            <span v-if="obj.onDutyTime">{{formatMinutes(obj.onDutyTime)}}</span>
                            <span v-else>暂无</span>(上班时间:{{formatMinutes(obj.serverStartTime)}}</div>
                        <div class="tagDiv">
                            <van-tag v-if="obj.onDutyTime&&!isLate(obj.onDutyTime,obj.serverStartTime)" round
                                     type="primary">正常
                            </van-tag>
                            <van-tag v-else round
                                     type="warning">迟到
                            </van-tag>
                        </div>
                    </van-step>
                    <van-step>
                        <div>
                            签退时间&nbsp;
                            <span v-if="obj.offDutyTime">{{formatMinutes(obj.offDutyTime)}}</span>
                            <span v-else>暂无</span>(下班时间:{{formatMinutes(obj.serverEndTime)}}</div>
                        <div class="tagDiv">
                            <van-tag v-if="obj.offDutyTime&&isLate(obj.serverEndTime,obj.offDutyTime)" round
                                     type="warning">早退
                            </van-tag>
                            <van-tag v-if="obj.offDutyTime&&!isLate(obj.serverEndTime,obj.offDutyTime)" round
                                     type="primary">正常
                            </van-tag>
                        </div>
                    </van-step>
                </van-steps>
            </div>

        </div>
        <div v-if="obj!=null&&obj.id>0&&!obj.offDutyTime&&!obj.onDutyTime" class="detailDiv"
             style="text-align: center;color: #bfbfbf">
            <div class="imgDiv">
                <img src="../img/rest.png"/>
            </div>
            <div style="margin-top: 2%">
                当天无打卡记录
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: "nCalender",
        data() {
            return {
                loginNannyUser: {},
                loginGzhUser: {},
                obj: {},
                obj1: {},
                list: [],
                dateTemp: null
            }
        },
        methods: {
            isLate(str, str1) {//判断两个时分秒大小
                return new Date((this.formatDates(new Date()) + " " + str).replace(/-/g, '/')) > new Date((this.formatDates(new Date()) + " " + str1).replace(/-/g, '/'));
            },
            clickDay(data) {//选中某天
                this.dateTemp = data
                this.axiosPost("/nannyCheckIn/findNowRecord.n", {
                    queryDate: data,
                    id: this.obj1.joinWishId
                }, function (resData) {
                    this.obj = resData.obj;
                });
            },
            changeDate(data) {//左右点击切换月份
                console.log(data);
            },
        },
        mounted() {

        },
        created() {
            let $this = this;
            this.getLoginAllUser("nanny", {}, function () {
                $this.axiosPost("/joinWishList/findNannyCon.n", {}, function (resData) {
                    $this.list = resData.list;
                    if (resData.list != null && resData.list.length != 0) {
                        $this.$set($this.obj1, "joinWishId", resData.list[0].id);
                    }
                    $this.axiosPost("/nannyCheckIn/findNowRecord.n", {id: $this.obj1.joinWishId}, function (resData) {
                        $this.obj = resData.obj;
                    });
                });
            });
        },
        watch: {
            "obj1.joinWishId": {
                handler(newObj, oldObj) {
                    if (newObj != oldObj && newObj && newObj != null) {
                        this.axiosPost("/nannyCheckIn/findNowRecord.n", {
                            queryDate: this.dateTemp,
                            id: newObj
                        }, function (resData) {
                            this.obj = resData.obj;
                        });
                    }
                },
                deep: true
            },
        }

    }
</script>

差不多就是以上代码,直接复制了用,改一下ajax请求就行,需要扩展的就自己修改了[抱拳]

你可能感兴趣的:(vue仿钉钉的考勤日历(基于vue-calendar-component组件))