记录一个 vue3 uniapp 日历组件

352 阅读1分钟

问题产生

最近需要一个组件,但是uview-plus 没有可以使用的日历组件,从而借鉴了网上的思路,写了一个以下代码,但是渲染的时候出现问了,命名设置了不同日期,但是在数组中并没有生效

这个是存在问题的图片 image.png

image.png

我已经计算出了上下月的日期 但是在push操作时并没有push成功,这是为什么? 有没有大神解释一下

代码

<template>
    <view class="container" :style="{ background }">
        <!-- 日历头部 -->
        <view class="tmt-header">
            <view class="select-wrap" :style="{ color: actionColor }">

                <!-- 左箭头减少一月 -->
                <view class="p20" @click="changMonth(-1)"> <view class="arrow-left" /> </view>

                <!-- 单数字加前缀 0 -->
                <view class="time"> {{ `${currentYearMonth.year}年${currentYearMonth.month >= 10 ? currentYearMonth.month : '0' + currentYearMonth.month}月`}} </view>

                <!-- 右箭头增加一月 -->
                <view class="p20" @click="changMonth(1)"> <view class="arrow-right" /> </view>

            </view>

            <!-- 返回本月 -->
            <view class="back" @click="init(true)"> 返回本月 </view>
        </view>

        <!-- 日历内容 -->
        <view class="tmt-content">
            <!-- 星期标题展示 -->
            <view class="tmt-week-wrap">
                <view class="cell week-item" :style="{ color: weekColor }" v-for="(item, index) in week" :key="index">
                    {{ item.label }} </view>
            </view>

            <!-- 日期展示 -->
            <view class="tmt-day-wrap" :style="({ height: unfold ? contentHeight : '88rpx' })">

                <view class="cell" v-for="(item, index) in dateContent" @click="changeDay(item)">
                    <!-- 此处想订制不同月之间的数字 -->
                    <view class="dayText" :style="(current.year == item.year && current.month == item.month && current.day == item.day) ? 'background:' + selectBg : '',(item.type !== 0) ? 'color' + unSignColor : '' ">
                        <!-- 已经签到的日子 -->
                        <view class="point " :style="{ background: pointColor }" v-if="item.isSign" />
                        <view class="bgIcon" v-if="item.isSign"> <image src="@/static/image/Signin.svg" mode="widthFix" class="imgIcon" /> </view>
                        <text> {{ item.today ? '今' : item.day }}  </text>
                    </view>
                </view>
            </view>
        </view>
        <!-- 折叠按钮 -->
        <view v-if="showBtn" class="tmt-footer" @click="unfold = !unfold">
            <view :class="['arrow-down', { on: unfold }]"></view>
        </view>
    </view>
</template>

<script setup lang="ts">
import { computed, CSSProperties, onMounted, reactive, ref, toRaw } from 'vue';

interface dateType {
    year: number,
    month: number,
    day: number
}

interface signDateType extends dateType {
    type?: number,//  -1 上个月  0. 本月月 1. 下个月
    today?: boolean,// 是不是今天
    isSign?: boolean
}

interface weekType {
    label: string,
    value: number
}

const week: Array<weekType> = [
    {
        label: '一',
        value: 1
    },
    {
        label: '二',
        value: 2
    },
    {
        label: '三',
        value: 3
    },
    {
        label: '四',
        value: 4
    },
    {
        label: '五',
        value: 5
    },
    {
        label: '六',
        value: 6
    },
    {
        label: '日',
        value: 7
    }
]

const unfold = ref(true);

onMounted(() => {
    unfold.value = props.show
    init(false)
})



// 渲染用到的 30 天
const dateContent = reactive<Array<signDateType>>([]);

// 今天
const today = reactive<dateType>({} as dateType)
// 此刻年月 用于快速返回本月
const current = reactive<dateType>({} as dateType)

// 游标 记录展示的年月
const currentYearMonth = reactive({
    year: -1,
    month: -1,
})


const contentHeight = computed(() => Math.ceil(( dateContent.length) / 7) * 88 + 'rpx')

// 后端给的签到日子
const listData = [{
    day: 1,
    month: 12,
    year: 2022,
},
{
    day: 2,
    month: 12,
    year: 2022,
},
{
    day: 2,
    month: 12,
    year: 2022,
},
]

interface PropType {
    pointList?: Array<Object>,
    defaultDate?: string,
    showBtn?: boolean,
    show?: boolean,
    background?: string,
    weekColor?: string,
    dayColor?: string,
    selectBg?: string,
    pointColor?: string,
    backColor?: string,
    backBg?: string,
    actionColor?: string,
    unfoldBtnColor?: string,
    unSignColor?: string
}

const props = withDefaults(defineProps<PropType>(), {
    pointList: () => [],
    defaultDate: '2022-11-11',
    showBtn: true,
    show: true,
    background: 'linear-gradient(45deg, #5A24A6 0%, #7E3CB5 100%)',
    weekColor: "#fff",
    dayColor: "#fff",
    selectBg: 'linear-gradient(180deg, #FF855E 0%, #ED6337 100%)',
    pointColor: "#fff",
    backColor: "#fff",
    backBg: 'rgba(255, 255, 255, 0.19)',
    actionColor: '#fff',
    unfoldBtnColor: '#fff',
    unSignColor: "red"
})

// 点击选中这个日期
const changeDay = (item: dateType) => {
    current.year = item.year
    current.month = item.month
    current.day = item.day
    console.log(item);
}

// 左右箭头 改变月份
const changMonth = (num: number) => {
    const {
        year,
        month
    } = getMonthYear(num)
    makeCalendar(year, month)
}
// 点击左右箭头触发计算的月份与年份
const getMonthYear = (num: number) => {

    let year = currentYearMonth.year
    let month = currentYearMonth.month + num

    //  负数月份
    while (month <= 0) {
        month += 12;
        year -= 1;
    }

    // 大于12 月
    while (month > 12) {
        year += 1;
        month -= 12;
    }

    return {
        year,
        month
    }
}

// 初始化一个日期
const init = (isUserTime = true) => {
    let setTime = isUserTime ? new Date() : new Date(props.defaultDate)
    let year = setTime.getFullYear()
    let month = setTime.getMonth() + 1
    let day = setTime.getDate()
    if (!(props.defaultDate != '' && isUserTime)) {
        current.year = year;
        current.month = month;
        current.day = day;
    }
    today.year = new Date().getFullYear();
    today.month = new Date().getMonth() + 1;
    today.day = new Date().getDay();

    makeCalendar(year, month)
}

const makeCalendar = (year: number, month: number) => {

    // 修改展示月份标题
    currentYearMonth.year = year;
    currentYearMonth.month = month;

    // 本次要展示的日子
    let daysArr: Array<signDateType> = []

    // 展示月的天数
    let days = getDays(year, month)

    // 上个月
    let { year: lastYear, month: lastMonth } = getMonthYear(-1);
    console.log(lastYear, lastMonth, "上个月");

    // 上个月的天数
    let lastDay = getDays(lastYear, lastMonth);

    //获得展示月第一天的星期
    let firstDayWeek = getWeekByDay(`${year}-${month}-1`);
    // 找到起点
    let weekIndex = week.findIndex(item => item.value == firstDayWeek);

    // 上个月剩下未展示的天数
    const lastShowDay = weekIndex == -1 ? 0 : weekIndex;



    // 下个月
    let { year: nextYear, month: nextMonth } = getMonthYear(1);
    console.log(nextYear, nextMonth, "下个月");
    // 下个月开头的天数
    let nextDays = (7 - (days + lastShowDay) % 7) % 7;

    // 1. 先添加上个月剩下的天数
    for (let i = 1; i <= lastShowDay; i += 1) {

        daysArr.push({
            year: lastYear,
            month: lastMonth,
            day: lastDay - lastShowDay + i,
            today: lastYear == today.year && lastMonth == today.month && lastDay - lastShowDay + i == today.day
        });
    }

    // 2.添加本月的天数
    for (let i = 1; i <= days; i++) {
        daysArr.push({
            year,
            month,
            day: i,
            today: year == today.year && month == today.month && i == today.day
        });
    }
    // 3.添加下个月的天数
    for (let i = 1; i <= nextDays; i += 1) {
        daysArr.push({
            year: nextYear,
            month: nextMonth,
            day: i,
            today: nextYear == today.year && nextMonth == today.month && i == today.day
        });
    }


    // 添加签到日期
    for (let i = 0; i < daysArr.length; i++) {
        for (let j = 0; j < listData.length; j++) {
            if (daysArr[i].day == listData[j].day && daysArr[i].month == listData[j].month) {
                daysArr[i].isSign = true
            }
        }
    }

    // 修改指定日期的type
    for (let i = 0; i < daysArr.length; i++) {
        if (daysArr[i].month = currentYearMonth.month) {
            daysArr[i].type = 0;
        }
    }

    dateContent.length = 0;
    dateContent.push(...daysArr);
    console.log("本次渲染的日期", dateContent);
}

// 获取某年某月的天数
const getDays = (year: number, month: number) => new Date(year, month, 0).getDate()

//获取某一天为星期几
const getWeekByDay = (time: string) => {
    let day = new Date(Date.parse(time.replace(/-/g, '/'))); //将日期值格式化
    return day.getDay() == 0 ? 7 : day.getDay();
}

</script>

<style lang="less" scoped>
.p20 {
    padding: 20upx;
}

.container {
    padding: 0 20upx;
}

.tmt-header {
    display: flex;
    justify-content: space-between;
    padding: 20upx 0;

    .select-wrap {
        display: flex;
        align-items: center;

        .arrow-left {
            width: 18upx;
            height: 18upx;
            transform: rotate(45deg);
            border-left-width: 1upx;
            border-bottom-width: 1upx;
            border-left-style: solid;
            border-bottom-style: solid;
            border-left-color: v-bind(actionColor);
            border-bottom-color: v-bind(actionColor);
        }

        .time {
            font-size: 36upx;
            margin: 0 20upx;
        }

        .arrow-right {
            width: 18upx;
            height: 18upx;
            transform: rotate(45deg);
            border-top-width: 1upx;
            border-right-width: 1upx;
            border-top-style: solid;
            border-right-style: solid;
            border-left-color: v-bind(actionColor);
            border-right-color: v-bind(actionColor);
        }
    }

    .back {
        padding: 0 20upx;
        font-size: 24upx;
        height: 52upx;
        color: #fff;
        line-height: 52upx;
        background: rgba(255, 255, 255, .19);
        text-align: center;
        border-radius: 30upx;
        color: v-bind(backColor);
        background: v-bind(backBg);

    }
}

.cell {
    width: 14.28%;
    height: 88upx;
    text-align: center;
    line-height: 88upx;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;

    .bgIcon {
        position: absolute;
        width: 100%;
        height: 100%;

        .imgIcon {
            width: 100%;
            display: block;
        }
    }

    .point {
        width: 6upx;
        height: 6upx;
        position: absolute;
        bottom: 3upx;
        border-radius: 50%;
        left: 50%;
        transform: translateX(-50%);
    }

    .dayText {
        width: 56upx;
        height: 56upx;
        text-align: center;
        line-height: 56upx;
        border-radius: 50%;

        &.on {
            background: linear-gradient(180deg, #FF855E 0%, #ED6337 100%);
        }
    }

}

.tmt-content {
    padding-bottom: 20upx;

    .tmt-week-wrap {
        display: flex;

        .week-item {
            font-size: 36upx;
        }
    }

    .tmt-day-wrap {
        display: flex;
        flex-wrap: wrap;
        transition: height .3s;
        overflow: hidden;
        color: v-bind(dayColor);
    }
}

.tmt-footer {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 80upx;
    margin-top: -36upx;

    .arrow-down {
        width: 18upx;
        height: 18upx;
        transform: rotate(-45deg);
        border-left-width: 1upx;
        border-bottom-width: 1upx;
        border-left-style: solid;
        border-bottom-style: solid;
        transition: all .3s;
        border-left-color: v-bind(unfoldBtnColor);
        border-bottom-color: v-bind(unfoldBtnColor);

        &.on {
            transform: rotate(135deg);
        }
    }
}
</style>

问题解决