签到组件封装 uniapp+hook+ts+vue3

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>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值