当前位置: 首页 > article >正文

vue/H5的日历组件可简单定制

在components创建riliZujian.vue

<template>
  <div class="max_box">
    <!-- 日历 文字 -->
    <div class="month">
      <div @click="lastMonth" class="monthText13">上月</div>
      <div class="monthText2">{{ year }}年{{ month }}月</div>
      <div class="monthText13" @click="nextMonth">下月</div>
    </div>

    <!-- 日历 周 -->
    <div class="week">
      <div v-for="weeks in weekArr" :key="weeks">
        {{ weeks }}
      </div>
    </div>

    <!-- 日历 日 -->
    <div class="day">
      <div
        :class="[{ checkday: !days.date }, { choose: days.flag }]"
        v-for="(days, index) in dayArr"
        :key="index"
      >
        {{ days.day }}
        <img src="./yidaka.png" class="dayImg" v-if="days.flag" />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    already: {
      type: Array
    }
  },
  watch: {
    already: {
      handler (v) {
        // 监听getAlready方法 获得已打卡的样式
        this.getAlready()
      },
      immediate: true, // 第一次赋值时触发
      deep: true // 深度监听
    }
  },
  name: 'rili-zujian',
  data () {
    return {
      dayArr: [], // 当前月每日
      day: new Date().getDate(), // 当前日
      year: new Date().getFullYear(), // 当前年
      month: new Date().getMonth() + 1, // 当前月
      weekArr: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], // 每周
      aheadDay: 0 // 前方空白天数数量
    }
  },
  mounted () {
    const that = this
    // 初始日期
    that.initDate()
  },
  methods: {
    // 渲染已经签到的日期
    getAlready () {
      for (const i in this.dayArr) {
        if (
          this.already.indexOf(new Date(this.dayArr[i].date).getTime()) === -1
        ) {
          this.$set(this.dayArr[i], 'flag', false)
        } else {
          this.$set(this.dayArr[i], 'flag', true)
        }
      }
    },

    // 初始化日期
    initDate () {
      const that = this
      that.dayArr = []
      // 当前月总天数
      const totalDay = new Date(that.year, that.month, 0).getDate()
      // 遍历总天数将日期逐个添加至数组
      for (let i = 1; i <= totalDay; i++) {
        // 得到需补充天数
        const value = new Date(that.year, that.month - 1, i).getDay()
        // 补充前面空白日期
        if (i === 1 && value !== 0) {
          that.addBefore(value)
          that.aheadDay = value
        }
        // 添加本月日期
        const obj = {}
        obj.date =
          that.year +
          '-' +
          that.formatNum(that.month) +
          '-' +
          that.formatNum(i)
        obj.day = i
        that.dayArr.push(obj)
        // 补充后面空白日期
        if (i === totalDay && value !== 6) that.addAfter(value)
      }
    },
    // 补充前面空白日期
    addBefore (value) {
      const that = this
      const totalDay = new Date(that.year, that.month - 1, 0).getDate()
      for (let i = 0; i < value; i++) {
        const obj = {}
        obj.date = ''
        obj.day = totalDay - (value - i) + 1
        that.dayArr.push(obj)
      }
    },
    // 补充后空白日期
    addAfter (value) {
      const that = this
      for (let i = 0; i < 6 - value; i++) {
        const obj = {}
        obj.date = ''
        obj.day = i + 1
        that.dayArr.push(obj)
      }
    },
    // 格式化日期位数
    formatNum (num) {
      return num < 10 ? '0' + num : num
    },
    // 上一个月
    lastMonth () {
      const that = this
      if (that.month === 1) {
        that.year -= 1
        that.month = 12
      } else {
        that.month -= 1
      }

      that.initDate()
      that.getAlready()
    },
    // 下一个月
    nextMonth () {
      const that = this
      if (that.month === 12) {
        that.year += 1
        that.month = 1
      } else {
        that.month += 1
      }
      that.initDate()
      that.getAlready()
    }
  }
}
</script>

<style lang="less" scoped>
// 最大的盒子
.max_box {
  width: 375px;
  box-sizing: border-box;
  background-color: none;
}
// 月周日的样式 flex布局
.month,
.week,
.day {
  width: 375px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
// 月的文字的样式
.month {
  height: 50px;
}
.monthText13 {
  width: 50px;
  height: 50px;
  line-height: 50px;
  text-align: center;
}
.monthText2 {
  width: 100px;
  height: 50px;
  line-height: 50px;
  text-align: center;
}
// 日 flex超出 的换行
.day {
  flex-wrap: wrap;
}
// 周和日 的div样式
.week > div,
.day > div {
  width: 32px;
  height: 32px;
  line-height: 32px;
  margin: 9px;
  text-align: center;
  position: relative;
}
// 本月之外的数字 改成白色 隐藏起来
.checkday {
  color: #f9f9f9;
}
// 已打卡 的日期 的样式
.choose {
  color: #ffffff;
  background: #b5443a;
  border-radius: 10px;
  position: relative;
}
.dayImg {
  width: 36px;
  height: 13px;
  position: absolute;
  top: -3px;
  left: 0;
}
</style>

在业务代码中引入 riliZujian 组件

<template>
  <!-- 打卡领取道具礼包 -->
  <div class="maxBox">
    <!-- 导航栏 只有返回键 -->
    <div
      class="title"
      :style="{ top: this.$route.query.statusBarHeight + 'px' }"
    >
      <img src="./climbersImg/title01.png" class="title01" @click="goBack" />
      <div class="title03"></div>
    </div>
    <!-- 打卡 背景图 -->
    <img src="./climbersImg/dakaMaxBg.png" class="dakabeijingtu" />

    <!-- 内容 -->
    <div class="content">
      <!-- 用户信息 -->
      <div class="content_top">
        <!-- 打卡 打卡规则 子绝父相 -->
        <img
          src="./climbersImg/dakaguize.png"
          class="dakaguize"
          @click="onDakaguize"
        />

        <img :src="userInfo.userHeadUrl" class="content_top_img" />
        <div class="content_top_text">
          <div class="content_top_text_1">
            <img
              src="./climbersImg/dakaText1.png"
              class="content_top_text_1_img"
            />
            <div class="content_top_text_1_number">
              {{ userInfo.signCount }}
            </div>
            <img
              src="./climbersImg/dakaText2.png"
              class="content_top_text_1_img"
            />
          </div>

          <div class="content_top_text_2">
            <div class="content_top_text_2_text">
              <span style="color: #b5443a">{{ userInfo.signCount }}</span
              >/{{ userInfo.amount }}元
            </div>
            <div class="content_top_text_2_text">
              满{{ userInfo.amount }}元自动发放到钱包
            </div>
          </div>
        </div>
      </div>
      <!-- 日历 -->
      
      <riliZujian :already="dakaData" @changeMonth="historysign" />
    </div>

    <!-- 弹出框 打卡规则 -->
    <van-popup v-model="showDakaguize" position="bottom" style="background: none;">
      <img src="./climbersImg/dakaguizeBig.png" class="guizetanchuang" />
    </van-popup>
  </div>
</template>

<script>
// getDakaSub,
import { getDakaUserInfo, getDakaRili } from '@/api/zhidou'
import riliZujian from '@/components/rilizujian.vue'

export default {
  name: 'climbers-dakalinglibao',
  data () {
    return {
      showDakaguize: false, // 弹出框 展示打卡规则的字段
      dakaData: [], // 打卡的日历的接收字段
      userInfo: {} // 用户的打卡和基本信息
    }
  },
  components: {
    riliZujian
  },
  mounted () {
    this.getUserInfo()
    this.historysign()
  },
  methods: {
    // 打开 打卡规则 的方法
    onDakaguize () {
      console.log(123)
      this.showDakaguize = true
    },
    // 获取当前用户的打卡基础信息
    getUserInfo () {
      getDakaUserInfo({ userId: this.$route.query.userId }).then((res) => {
        // console.log(res, 'res---')
        if (res.status === 200) {
          this.userInfo = res.data
        } else {
          this.$toast.fail(res.msg)
        }
      })
    },
    // 获取当前用户的打卡日历信息
    historysign () {
      getDakaRili({
        userId: 107,
        time: '2025-03-17 12:26:43'
      }).then((res) => {
        if (res.status === 200) {
          for (const i in res.data) {
            // 接口返回的每一项 如果为 true 则进来
            if (res.data[i]) {
              this.dakaData.push(new Date(i).getTime())
            }
          }
          // console.log(this.dakaData)
        } else {
          this.$toast.fail(res.msg)
        }
      })
    },
    // 返回到上级页面
    goBack () {
      this.$router.push({
        path: '/上级页面地址',
        query: {
          userId: this.$route.query.userId,
          statusBarHeight: this.$route.query.statusBarHeight
        }
      })
    }
  }
}
</script>

<style lang="less" scoped>
.maxBox {
  width: 375px;
  position: relative;
}
.title {
  width: 375px;
  height: 44px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  position: absolute;
  left: 0;

  .title01 {
    width: 28px;
    height: 28px;
    margin-left: 16px;
  }
  .title03 {
    width: 44px;
    height: 20px;
  }
}
.dakabeijingtu {
  width: 375px;
}
.content {
  width: 375px;
  // height: 500px;
  background-image: url(./climbersImg/dakaMinBg.png);
  background-size: 100% 100%;
  position: absolute;
  top: 250px;
  bottom: 0;

  .content_top {
    width: 343px;
    height: 94px;
    margin: 0 16px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
    display: flex;
    position: relative;

    .dakaguize {
      width: 34px;
      height: 96px;
      position: absolute;
      top: -23px;
      right: -18px;
    }
    .content_top_img {
      width: 56px;
      height: 56px;
      border-radius: 56px;
      margin: 21px 15px 0 18px;
    }
    .content_top_text {
      width: 254px;
      margin-top: 20px;

      .content_top_text_1 {
        width: 254px;
        height: 32px;
        display: flex;
        align-items: center;

        .content_top_text_1_img {
          height: 16px;
        }
        .content_top_text_1_number {
          height: 32px;
          line-height: 32px;
          font-size: 27px;
          font-weight: 700;
          color: #b5443a;
          margin: 0 5px;
        }
      }
      .content_top_text_2 {
        width: 254px;
        height: 20px;
        display: flex;

        .content_top_text_2_text {
          font-size: 14px;
          font-weight: 400;
          color: rgba(0, 0, 0, 0.5);
          margin-right: 8px;
        }
      }
    }
  }
}
.guizetanchuang {
  width: 375px;
}

::v-deep .van-calendar__header {
  background: rgba(255, 255, 255, 0.1);
}
::v-deep .van-calendar__body {
  background: rgba(255, 255, 255, 0.1);
}
::v-deep .van-calendar__header-title {
  display: none;
}
</style>

参考资料:签到组件、可签到和补签,统计签到天数和积分 - DCloud 插件市场


http://www.kler.cn/a/590532.html

相关文章:

  • [网络][tcp协议]:tcp报头
  • UI设计公司:数据大屏设计提升用户体验的方法
  • 【软考-架构】5.2、传输介质-通信方式-IP地址-子网划分
  • OpenGL ES 入门指南:从基础到实战
  • golang-struct结构体
  • C# 使用Markdown2Pdf把md文件转换为pdf文件
  • centos 安装pip时报错 Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64
  • SQLMesh系列教程:利用date_spine宏构建日期序列实践指南
  • hbuiderx的sass编译器报dart-sass等错误的解决方法
  • 美团AI面试总结
  • 密码学研究热点
  • obeaver 连接oracle 库 模式乱码
  • 基于 GEE 利用 Sentinel-1 双极化数据计算 SDWI 指数实现逐月提取水域面积
  • 漏洞预警 | Apache OFBiz 服务端模板注入漏洞(CVE-2025-26865)
  • ChatGPT and Claude国内使用站点
  • 汽车PKE无钥匙进入系统一键启动系统定义与原理
  • 动作捕捉手套如何让虚拟现实人机交互 “触手可及”?
  • 【GPT入门】第24课 langfuse介绍
  • Django 集成 Redis 数据库指南
  • PySide(PyQt),QGraphicsItem的坐标映射转换函数