template>
<!-- 打卡日历页面 -->
<view class="all">
<view class="bar">
<!-- 上一个月 -->
<view class="previous" @click="changeMonth(-1)">
<button class="barbtn">{{ langType == 'ch' ? '上一月' : 'Last' }}</button>
</view>
<!-- 显示年月 -->
<view class="date">{{ nowYear || '--' }} 年 {{ nowMonth || '--' }} 月</view>
<!-- 下一个月 -->
<view class="next" @click="changeMonth(1)">
<button class="barbtn">{{ langType == 'ch' ? '下一月' : 'Nex/' }}</button>
</view>
</view>
<!-- 显示星期 -->
<view class="week-area">
<view class="week-txt" v-for="(item, index) in weeksTxt[langType]" :key="index">{{ item }}</view>
</view>
<view class="myDateTable">
<view v-for="(item, j) in calendarDays" :key="j" class="dateCell">
<view v-if="item.date == undefined || item.date == null" class="cell"></view>
<template v-else>
<!-- 已签到日期 -->
<view v-if="item.isSign == true" class="cell greenColor bgWhite">
{{ item.date }}
</view>
<!-- 漏签 -->
<view @click="clickSign(item.date, 0)" class="cell outSignStyle" v-else-if="item.isBeforeToday && item.isThisMonth">
<!-- redColor bgGray -->
{{ item.date }}
<view class="redDot"></view>
</view>
<!-- 今日未签到-->
<view
@click="clickSign(item.date, 1)"
class="cell whiteColor bgBlue"
v-else-if="item.date == today && nowMonth == toMonth && nowYear == toYear"
>
签到
</view>
<!-- 当前日期之后 -->
<view class="whiteColor cell" v-else>
{{ item.date }}
</view>
</template>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { useSignIn } from '../useSignIn'
import { unref } from 'vue'
interface CalendarDay {
date: number | null
fullDate: string | null
isBeforeToday: boolean
isSign: boolean
isThisMonth: boolean
}
const { getListData, getMember, getList } = useSignIn()
const props = defineProps({
isReplenishSign: {
type: Boolean,
default: false,
},
isFullCalendar: {
type: Boolean,
default: false,
},
yearMonth: {
type: String,
default: new Date().getFullYear() + '-' + new Date().getMonth() + 1,
},
dataSource: {
type: Array,
default: () => [],
},
langType: {
type: String,
default: 'ch',
},
})
const a = ref<any>()
const allM = ref<any>()
const emits = defineEmits(['clickChange'])
onMounted(() => {
const ymArr = props.yearMonth.split('-')
buildCalendar(parseInt(ymArr[0]), parseInt(ymArr[1]))
if (ymArr[1].length == 1) {
a.value = '0' + ymArr[1]
} else {
a.value = ymArr[1]
}
const p = getListData(ymArr[0] + '-' + a.value)
Promise.all([p]).then(() => {
onSignDataChange(getList.value as string[])
})
})
const calendarDays = ref<CalendarDay[]>([])
const SignData = ref<string[]>([])
const nowYear = ref(0)
const nowMonth = ref(0)
const today = ref(parseInt(new Date().getDate().toString()))
const toMonth = ref(parseInt(new Date().getMonth() + 1 + ''))
const toYear = ref(parseInt(new Date().getFullYear() + ''))
const weeksTxt: any = {
ch: ['日', '一', '二', '三', '四', '五', '六'],
en: ['Sun', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'],
}
function clickSign(date: number, type: number) {
//type=0补签,type=1当日签到
var strTip = '签到'
if (type == 0) {
if (!props.isReplenishSign) {
// 未开启补签,阻止继续执行
console.log('————补签功能未开启————')
return
}
strTip = '补签'
}
const getmember = getMember()
Promise.all([getmember]).then(() => {
if (nowMonth.value < 10) {
getListData(nowYear.value + '-' + '0' + nowMonth.value).then(() => {
onSignDataChange(getList.value as string[])
})
} else {
getListData(nowYear.value + '-' + nowMonth.value).then(() => {
onSignDataChange(getList.value as string[])
})
}
})
uni.showToast({
title: date + '号' + strTip + '成功',
icon: 'success',
position: 'bottom',
})
// 父页面要在clickChange里去修改输入的数据源dataSource,否则视图不更新的!
}
function calculateEmptyGrids(year: number, month: number): void {
calendarDays.value = []
const firstDayOfWeek = getFirstDayOfWeek(year, month)
if (firstDayOfWeek > 0) {
for (let i = 0; i < firstDayOfWeek; i++) {
calendarDays.value.push({
date: null,
fullDate: null,
isBeforeToday: true,
isSign: false,
isThisMonth: false,
})
}
}
}
function calculateDays(year: number, month: number): void {
const thisMonthDays = getMonthDayLength(year, month)
const toDate = new Date(`${toYear.value}/${toMonth.value}/${today.value}`)
for (let i = 1; i <= thisMonthDays; i++) {
const fullDate = `${year}-${month}-${i}`
const isBeforeToday = new Date(toIOSDate(fullDate)) < toDate
calendarDays.value.push({
date: i,
fullDate,
isBeforeToday,
isSign: false,
isThisMonth: true,
})
}
}
function changeMonth(type: number): void {
const { year, month } = getOperateMonthDate(nowYear.value, nowMonth.value, type)
buildCalendar(year, month)
if (month < 10) {
allM.value = '0' + month
} else {
allM.value = month
}
getListData(year + '-' + allM.value)
matchSign()
}
function buildCalendar(y: number, m: number): void {
nowYear.value = y
nowMonth.value = m
calculateEmptyGrids(y, m)
calculateDays(y, m)
if (props.isFullCalendar) {
fullCell()
}
}
function fullCell(): void {
const endDay = getMonthDayLength(nowYear.value, nowMonth.value)
const beforeEmptyLength = getFirstDayOfWeek(nowYear.value, nowMonth.value)
const afterEmptyLength = 6 - getFirstDayOfWeek(nowYear.value, nowMonth.value, endDay)
const last = getOperateMonthDate(nowYear.value, nowMonth.value, -1)
const lastMonthEndDay = getMonthDayLength(last.year, last.month)
for (let i = 0; i < beforeEmptyLength; i++) {
const date = lastMonthEndDay - beforeEmptyLength + i + 1
calendarDays.value[i].date = date
calendarDays.value[i].fullDate = `${last.year}-${last.month}-${date}`
}
const next = getOperateMonthDate(nowYear.value, nowMonth.value, 1)
for (let i = 1; i <= afterEmptyLength; i++) {
calendarDays.value.push({
date: i,
fullDate: `${next.year}-${next.month}-${i}`,
isBeforeToday: false,
isSign: false,
isThisMonth: false,
})
}
}
function onSignDataChange(newData: string[], oldData: string[] = []): void {
SignData.value = newData
matchSign()
}
function matchSign(): void {
const signs = SignData.value
const daysArr = calendarDays.value
for (let i = 0; i < signs.length; i++) {
const current = new Date(toIOSDate(signs[i])).getTime()
for (let j = 0; j < daysArr.length; j++) {
if (current == new Date(toIOSDate(daysArr[j].fullDate || '')).getTime()) {
daysArr[j].isSign = true
}
}
}
calendarDays.value = daysArr
}
function getMonthDayLength(year: number, month: number): number {
return new Date(year, month, 0).getDate()
}
function getFirstDayOfWeek(year: number, month: number, day: number = 1): number {
return new Date(Date.UTC(year, month - 1, day)).getDay()
}
function toIOSDate(strDate: string): string {
return strDate ? strDate.replace(/-/g, '/') : strDate
}
function getOperateMonthDate(yy: number, mm: number, num: number): { year: number; month: number } {
let month = mm + num
let year = yy
if (month > 12) {
month = 1
year++
} else if (month < 1) {
month = 12
year--
}
return {
month,
year,
}
}
</script>
<style lang="scss" scoped>
.all {
margin-top: 20rpx;
}
.all .bar {
display: flex;
flex-direction: row;
justify-content: space-between;
margin: 30rpx 20rpx;
padding: 10rpx;
}
.bar .barbtn {
height: 30px;
line-height: 30px;
font-size: 12px;
}
.all .week-area {
display: flex;
justify-content: space-between;
padding: 10px 0;
box-sizing: border-box;
width: 91vw;
margin: 10px auto;
border-radius: 10px;
}
.all .week-txt {
text-align: center;
width: 13vw;
}
.myDateTable {
margin: 0 auto;
width: 91vw;
padding: 2vw 0;
border-radius: 10px;
background: linear-gradient(#74aada, #94db98);
}
.myDateTable .dateCell {
width: 13vw;
padding: 1vw;
display: inline-block;
text-align: center;
font-size: 16px;
box-sizing: border-box;
overflow: hidden;
}
.dateCell .cell {
display: flex;
border-radius: 50%;
height: 11vw;
justify-content: center;
align-items: center;
box-sizing: border-box;
flex-direction: column;
}
.whiteColor {
color: #fff;
}
.greenColor {
color: #01b90b;
font-weight: bold;
}
.bgWhite {
background-color: #fff;
}
.bgGray {
background-color: rgba(255, 255, 255, 0.42);
}
.bgBlue {
font-size: 14px;
background-color: #4b95e6;
}
.redColor {
color: #ff0000;
}
.outSignStyle {
border: 1px solid #ffffff;
color: #ffffff;
}
.redDot {
width: 3px;
height: 3px;
border-radius: 50%;
background-color: red;
}
</style>
签到组件封装 uniapp+hook+ts+vue3
最新推荐文章于 2024-07-12 09:36:48 发布